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?

meni
źródło

Odpowiedzi:

369
CASE WHEN last_name IS NULL THEN '' ELSE ' '+last_name END
Marcelo Cantos
źródło
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ć”)

Nordic Mainframe
źródło
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.

kamahl
źródło
11

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

SELECT first_name + ' ' + ISNULL(last_name, '') AS Name FROM dbo.person
Ian Jacobs
źródło
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
b0fh
źródło
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

KM.
źródło
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).

yoel halb
źródło
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
Linda
źródło
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
Szef kuchni Slagle
źródło
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
Szef kuchni Slagle
źródło
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+

Molem
źródło
0

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

SELECT CONCAT([FirstName], ' , ' , [LastName]) FROM YOURTABLE
TSQL
źródło
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 
użytkownik2463829
źródło
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.

Joshua Walker
źródło