Klauzula T-SQL CASE: Jak określić WHEN NULL


227

Napisałem instrukcję T-SQL podobną do tej (ta oryginalna wygląda inaczej, ale chcę tutaj podać prosty przykład):

SELECT first_name + 
    CASE last_name WHEN null THEN 'Max' ELSE 'Peter' END AS Name
FROM dbo.person

Ta instrukcja nie zawiera żadnych błędów składniowych, ale klauzula przypadku zawsze wybiera część ELSE - także jeśli ostatnia nazwa jest pusta. Ale dlaczego?

Chcę zjednoczyć imię i nazwisko, ale jeśli nazwisko jest puste, całe imię staje się puste:

SELECT first_name +
   CASE last_name WHEN null THEN '' ELSE ' ' + last_name END AS Name 
FROM dbo.person

Czy wiesz, gdzie jest problem?

Odpowiedzi:


369
CASE WHEN last_name IS NULL THEN '' ELSE ' '+last_name END

4
@ Sugestia WSPÓŁPRACY Lutra jest lepsza niż moja odpowiedź. Jest nieznacznie mniej wydajny, ale o wiele bardziej elegancki.
Marcelo Cantos,

Przydałoby się to z użyciem czegoś takiego lub podobnego: COALESCE (last_name, '') W przypadku dużych zapytań instrukcja case, choć zwięzła, jest trudniejsza do utrzymania niż COALESCE. W końcu masz ten sam wynik. Jeśli musisz zoptymalizować, sprawdź plany wykonania, ale nie zauważyłem dużej różnicy.
Anthony Mason,

1
COALESCEw zasadzie wewnętrznie przekłada się na CASE. Problem z CASE polega na tym, że last_namejest oceniany dwukrotnie i może prowadzić do innych skutków ubocznych - zobacz ten doskonały artykuł . Aby rozwiązać to, o co pytano, wolę iść z ISNULL(' '+ last_name, '')wymienionym w komentarzu poniżej.
miroxlav

41

CZĘŚĆ KIEDY jest porównywana z ==, ale tak naprawdę nie można porównywać z NULL. Próbować

CASE WHEN last_name is NULL  THEN ... ELSE .. END

zamiast lub COALESCE:

COALESCE(' '+last_name,'')

(„+ last_name to NULL, gdy last_name jest NULL, więc w takim przypadku powinno zwrócić”)


Ok, dziękuję za informację z == w części KIEDY. Nie wiedziałem tego Z COALESCE działa również dobrze. Nie sądziłem, że jest tak wiele możliwości, aby to zrobić.
meni

15

Istnieje wiele rozwiązań, ale żadne nie wyjaśnia, dlaczego oryginalne oświadczenie nie działa.

CASE last_name WHEN null THEN '' ELSE ' '+last_name

Po tym czasie następuje sprawdzenie równości, która powinna być prawdziwa lub fałszywa.

Jeśli jedna lub obie części porównania mają wartość NULL, wynikiem porównania będzie NIEZNANY, co jest traktowane jak fałsz w strukturze przypadku. Widzieć: https://www.xaprb.com/blog/2006/05/18/why-null-never-compares-false-to-anything-in-sql/

Aby tego uniknąć, Coalesce jest najlepszym sposobem.


11

Biorąc pod uwagę zapytanie, możesz to również zrobić:

SELECT first_name + ' ' + ISNULL(last_name, '') AS Name FROM dbo.person

2
Dodaje to nadmiarową spację, gdy last_name ma wartość null, a tego OP chciał uniknąć.
Marcelo Cantos,

6
Lepsze użycie, ISNULL(' '+ last_name, '')aby zapobiec zbędnej przestrzeni.
Prutswonder

Tak, zrozumiałem to po tym, jak opublikowałem. Pomyślałem, że zostawię to, ponieważ rozwiązało to problem, z którym miał problemy.
Ian Jacobs

7

Problem polega na tym, że wartość null nie jest uważana za równą sobie, dlatego klauzula nigdy się nie zgadza.

Należy jawnie sprawdzić wartość null:

SELECT CASE WHEN last_name is NULL THEN first_name ELSE first_name + ' ' + last_name

5

próbować:

SELECT first_name + ISNULL(' '+last_name, '') AS Name FROM dbo.person

To dodaje spację do nazwiska, jeśli jest puste, cała spacja + nazwisko przechodzi do NULL i dostajesz tylko imię, w przeciwnym razie dostaniesz firts + spację + nazwisko.

będzie to działać tak długo, jak ustawione jest domyślne ustawienie konkatenacji z łańcuchami zerowymi:

SET CONCAT_NULL_YIELDS_NULL ON 

nie powinno to stanowić problemu, ponieważ OFFtryb ten zniknie w przyszłych wersjach SQl Server


4

Problem polega na tym, że NULL nie jest uważany za równy niczego, nawet samemu sobie, ale dziwne jest to, że również nie jest równy sobie.

Rozważ następujące instrukcje (które są BTW nielegalne w SQL Server T-SQL, ale są prawidłowe w My-SQL, jednak to właśnie ANSI definiuje jako null, i można je zweryfikować nawet w SQL Server za pomocą instrukcji case itp.)

SELECT NULL = NULL -- Results in NULL

SELECT NULL <> NULL -- Results in NULL

Więc nie ma prawdziwej / fałszywej odpowiedzi na pytanie, zamiast tego odpowiedź jest również pusta.

Ma to wiele implikacji, na przykład w

  1. Sprawozdanie sprawy, w której każda wartość zerowa zawsze korzystają z klauzuli ELSE chyba użyć jawnie WHEN warunek IS NULL ( NIE warunek )WHEN NULL
  2. Łączenie ciągów, jak
    SELECT a + NULL -- Results in NULL
  3. W klauzulach WHERE IN lub WHERE NOT IN, jeśli chcesz poprawnych wyników, upewnij się w skorelowanym zapytaniu podrzędnym, aby odfiltrować wartości zerowe.

Można nadpisać to zachowanie w SQL Server, określając SET ANSI_NULLS OFF, ale NIE jest to zalecane i nie należy tego robić, ponieważ może powodować wiele problemów, po prostu z powodu odchylenia standardu.

(Na marginesie, w My-SQL istnieje opcja użycia specjalnego operatora <=>do porównania zerowego.)

Dla porównania, w ogólnych językach programowania null jest traktowany jako wartość regularna i jest sobie równy, jednak jest to wartość NAN, która również nie jest sobie równa, ale przynajmniej zwraca wartość „fałsz” podczas porównywania go z samym sobą (i podczas sprawdzania, czy nie jest równy, różne języki programowania mają różne implementacje).

Zauważ jednak, że w językach podstawowych (tj. VB itp.) Nie ma słowa kluczowego „null”, a zamiast tego używa się słowa kluczowego „Nic”, którego nie można użyć w bezpośrednim porównaniu, a zamiast tego należy użyć słowa „IS” jak w języku SQL, jednak w rzeczywistości jest on równy sobie (przy użyciu porównań pośrednich).


1
„Dziwne jest to, że również nie jest równe sobie”. => nie jest to dziwne, biorąc pod uwagę, że null w SQL ma znaczenie „nieznane”. Coś nieznanego, najprawdopodobniej nie jest tym samym, co coś nieznanego.
JDC

1
CASE  
    WHEN last_name IS null THEN '' 
    ELSE ' ' + last_name 
END

1

Kiedy czujesz się sfrustrowany, spróbuj:

CASE WHEN last_name IS NULL THEN '' ELSE ' '+last_name END

Spróbuj zamiast tego:

CASE LEN(ISNULL(last_Name,''))
WHEN 0 THEN '' 
ELSE ' ' + last_name
END AS newlastName

LEN(ISNULL(last_Name,'')) mierzy liczbę znaków w tej kolumnie, która będzie zero, niezależnie od tego, czy jest pusta, czy też NULL WHEN 0 THEN oceni wartość true i zwróci „” zgodnie z oczekiwaniami.

Mam nadzieję, że jest to pomocna alternatywa.

Uwzględniłem ten przypadek testowy dla serwera SQL 2008 i nowszych wersji:

DECLARE @last_Name varchar(50) = NULL

SELECT 
CASE LEN(ISNULL(@last_Name,''))
WHEN 0 THEN '' 
ELSE 'A ' + @last_name
END AS newlastName

SET @last_Name = 'LastName'

SELECT 
CASE LEN(ISNULL(@last_Name,''))
WHEN 0 THEN '' 
ELSE 'A ' + @last_name
END AS newlastName

2
Jesteś pewien, że to działa? Większość funkcji zwraca NULL, gdy podano dane wejściowe NULL, a moje testy potwierdzają, że jest to również prawdą w przypadku LEN (), tj. LEN (NULL) zwraca NULL, a nie 0.
Jason Musgrove

Pokechu22 jest poprawny, a to jest poprawiony skrypt: CASE LEN (ISNULL (last_Name, '')) WHEN 0 THEN 'ELSE' '+ last_name END AS newlastName
Chef Slagle

0

Jason złapał błąd, więc to działa ...

Czy ktoś może potwierdzić inne wersje platformy?
SQL Server:

SELECT
CASE LEN(ISNULL(last_name,'')) 
WHEN 0 THEN '' 
ELSE ' ' + last_name
END AS newlastName

MySQL:

SELECT
CASE LENGTH(IFNULL(last_name,'')) 
WHEN 0 THEN '' 
ELSE ' ' + last_name
END AS newlastName

Wyrocznia:

SELECT
CASE LENGTH(NVL(last_name,'')) 
WHEN 0 THEN '' 
ELSE ' ' + last_name
END AS newlastName

0

Możesz użyć funkcji IsNull

select 
    isnull(rtrim(ltrim([FirstName]))+' ','') +
    isnull(rtrim(ltrim([SecondName]))+' ','') +
    isnull(rtrim(ltrim([Surname]))+' ','') +
    isnull(rtrim(ltrim([SecondSurname])),'')
from TableDat

jeśli jedna kolumna jest pusta, otrzymasz pusty znak

Kompatybilny z Microsoft SQL Server 2008+


0

Użyj funkcji CONCAT dostępnej w SQL Server 2012 i nowszych.

SELECT CONCAT([FirstName], ' , ' , [LastName]) FROM YOURTABLE

0

Próbowałem rzutować na łańcuch i testować łańcuch o zerowej długości i zadziałało.

CASE 
   WHEN LEN(CAST(field_value AS VARCHAR(MAX))) = 0 THEN 
       DO THIS
END AS field 

0

NULL nic nie równa. Instrukcja case w zasadzie mówi, kiedy wartość = NULL .. nigdy nie zostanie trafiona.
Istnieje również kilka procedur przechowywanych w systemie, które zostały niepoprawnie zapisane w Twojej składni. Zobacz sp_addpullsubscription_agent i sp_who2.
Chciałbym wiedzieć, jak powiadomić Microsoft o tych błędach, ponieważ nie jestem w stanie zmienić proc. Przechowywanych w systemie.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.