Data i ostatnie null SQL Server ORDER BY

82

Próbuję zamówić według daty. Chcę, aby najnowsze daty pojawiały się jako pierwsze. To dość proste, ale jest wiele rekordów, które są zerowe i poprzedzają jakiekolwiek rekordy z datą.

Próbowałem kilku rzeczy bez powodzenia:

ORDER BY ISNULL(Next_Contact_Date, 0)

ORDER BY ISNULL(Next_Contact_Date, 999999999)

ORDER BY coalesce(Next_Contact_Date, 99/99/9999)

Jak mogę porządkować według daty i mieć zerowe dane jako ostatnie? Typ danych to smalldatetime.

UpHelix
źródło
Czy porządek sortowania musi być rosnący, ale z zerami na końcu? Czy będziesz mieć przyszłe daty w swoim stole?
AllenG
@AllenG, tak, od przeszłości do przyszłości, najpierw przeszłość i tak dalej. Więc tak, rosnąco. Tak, przyszłe daty są tym, czym większość z nich będzie.
UpHelix

Odpowiedzi:

112

smalldatetime ma zakres do 6 czerwca 2079 r., więc możesz użyć

ORDER BY ISNULL(Next_Contact_Date, '2079-06-05T23:59:00')

Jeśli żadne legalne rekordy nie będą miały tej daty.

Jeśli nie jest to założenie, możesz polegać na bardziej niezawodnej opcji, czyli sortowaniu w dwóch kolumnach.

ORDER BY CASE WHEN Next_Contact_Date IS NULL THEN 1 ELSE 0 END, Next_Contact_Date

Obie powyższe sugestie nie pozwalają jednak na użycie indeksu, aby uniknąć sortowania i przedstawić podobnie wyglądające plany.

wprowadź opis obrazu tutaj

Jest jeszcze jedna możliwość, jeśli taki indeks istnieje

SELECT 1 AS Grp, Next_Contact_Date 
FROM T 
WHERE Next_Contact_Date IS NOT NULL
UNION ALL
SELECT 2 AS Grp, Next_Contact_Date 
FROM T 
WHERE Next_Contact_Date IS NULL
ORDER BY Grp, Next_Contact_Date

Plan

Martin Smith
źródło
Ta sztuczka może być również zastosowana do VARCHARpól (np. ORDER BY ISNULL(my_varchar, 'ZZZZZZ')) I jest niezwykle przydatna, szczególnie do uzyskiwania zamówień w określony sposób podczas używania GROUP BY . . . GROUPING SETS. Dzięki za opublikowanie tego.
sparc_spread
Dlaczego nie możemy użyć polecenia order by desc, aby umieścić wartości zerowe na dole? także, dlaczego mapujemy null na 1?
MasterJoe
35

Według Itzika Ben-Gana, autora T-SQL Fundamentals for MS SQL Server 2012 , „Domyślnie SQL Server sortuje znaki NULL przed wartościami innymi niż NULL . Aby uzyskać znaki NULL do sortowania jako ostatnie, możesz użyć wyrażenia CASE, które zwraca 1, gdy kolumna „ Next_Contact_Date ma wartość NULL ” i 0, gdy nie jest równa NULL . Znaki inne niż NULL uzyskują 0 z powrotem z wyrażenia; dlatego są sortowane przed znakami NULL (które otrzymują 1). To wyrażenie CASE jest używane jako pierwsze sortowanie kolumny ”. Next_Contact_Datekolumna "powinna być określona jako druga kolumna sortowania. W ten sposób niż NULL będą znaczniki są sortowane poprawnie między sobą. ”Oto zapytanie rozwiązania dla Twojego przykładu dla MS SQL Server 2012 (i SQL Server 2014):

ORDER BY 
   CASE 
        WHEN Next_Contact_Date IS NULL THEN 1
        ELSE 0
   END, Next_Contact_Date;

Równoważny kod wykorzystujący składnię IIF:

ORDER BY 
   IIF(Next_Contact_Date IS NULL, 1, 0),
   Next_Contact_Date;
Andy
źródło
Ponadto, aby dodać do odpowiedzi, jeśli zmienisz IIF 1 i 0 wokół wartości zerowych, przejdą na górę. Działa to również, jeśli chcesz wyodrębnić krotki, umieszczając je na górze tabeli.
Franco Pettigrosso
3

Jeśli Twój SQL nie obsługuje NULLS FIRSTlub NULLS LAST, najprostszym sposobem na to jest użycie value IS NULLwyrażenia:

ORDER BY Next_Contact_Date IS NULL, Next_Contact_Date

aby umieścić null na końcu ( NULLS LAST) lub

ORDER BY Next_Contact_Date IS NOT NULL, Next_Contact_Date

aby umieścić wartości zerowe z przodu. Nie wymaga to znajomości typu kolumny i jest łatwiejsze do odczytania niż CASEwyrażenie.

EDYCJA: Niestety, chociaż działa to w innych implementacjach SQL, takich jak PostgreSQL i MySQL, nie działa w MS SQL Server. Nie miałem serwera SQL do przetestowania i polegałem na dokumentacji firmy Microsoft oraz testach z innymi implementacjami SQL. Według Microsoftu value IS NULL jest to wyrażenie, które powinno być użyteczne tak jak każde inne wyrażenie. I ORDER BY ma przyjmować wyrażenia, tak jak każda inna instrukcja, która przyjmuje wyrażenie. Ale to tak naprawdę nie działa.

Dlatego najlepszym rozwiązaniem dla SQL Server wydaje się być CASEwyrażenie.

Vroo
źródło
7
To nie jest poprawna składnia SQL Server
Martin Smith
3
Przepraszam za to. To powinno być ważne na dokumentację i pracuje w innym zapytania SQL firmy Microsoft, ale MS faktycznie nie pozwala na to.
Vroo
jeśli chodzi o wydajność, brzmi to okropnie, robisz dwa
rodzaje
W dokumentacji firmy Microsoft, którą utworzyłeś, czytałem, że wartość JEST NULL nie jest wyrażeniem , ale predykatem . To nie to samo.
BertuPG,
1
Według Microsoftu predykat jest wyrażeniem. To dosłownie pierwsze trzy słowa na docs.microsoft.com/en-us/sql/t-sql/queries/predicates
Vroo
3
order by -cast([Next_Contact_Date] as bigint) desc
paparazzo
źródło
wyrzuć błąd, jeśli Next_Contact_Datejest zerowyExplicit conversion from data type date to bigint is not allowed.
Nerdroid
2

Trochę późno, ale może komuś się przyda.

Dla mnie ISNULL nie wchodziło w grę ze względu na skanowanie tabeli. UNION ALL wymagałoby powtórzenia złożonego zapytania, a ponieważ wybrałem tylko TOP X, nie byłoby to zbyt wydajne.

Jeśli jesteś w stanie zmienić projekt stołu, możesz:

  1. Dodaj kolejne pole, tylko do sortowania, takie jak Next_Contact_Date_Sort.

  2. Utwórz regułę, która wypełni to pole dużą (lub małą) wartością, w zależności od potrzeb:

    CREATE TRIGGER FILL_SORTABLE_DATE ON YOUR_TABLE AFTER INSERT,UPDATE AS 
    BEGIN
        SET NOCOUNT ON;
        IF (update(Next_Contact_Date)) BEGIN
        UPDATE YOUR_TABLE SET Next_Contact_Date_Sort=IIF(YOUR_TABLE.Next_Contact_Date IS NULL, 99/99/9999, YOUR_TABLE.Next_Contact_Date_Sort) FROM inserted i WHERE YOUR_TABLE.key1=i.key1 AND YOUR_TABLE.key2=i.key2
        END
    END
    
Aaa
źródło
2

Użyj desc i pomnóż przez -1, jeśli to konieczne. Przykład rosnącej kolejności int z zerami last:

select * 
from
(select null v union all select 1 v union all select 2 v) t
order by -t.v desc
Ulf Herrmann
źródło
Nie myśl, że to zadziałaby dla zmiennych takich jak daty ..
Charlotte Deng
1

Wiem, że to jest stare, ale to działa dla mnie

Order by Isnull(Date,'12/31/9999')
anonimowy
źródło