SQL Server ORDER BY по дате и последним значениям NULL

82

Пытаюсь заказать по дате. Я хочу, чтобы самые свежие свидания были первыми. Это достаточно просто, но есть много записей с нулевым значением, которые предшествуют любым записям с датой.

Я безуспешно пробовал несколько вещей:

ORDER BY ISNULL(Next_Contact_Date, 0)

ORDER BY ISNULL(Next_Contact_Date, 999999999)

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

Как сделать заказ по дате, чтобы нули приходили последними? Тип данных - smalldatetime.

UpHelix
источник
Должен ли быть порядок сортировки по возрастанию, но с нулями в конце? И будут ли у вас в таблице будущие даты?
AllenG
@AllenG, да, из прошлого в будущее, сначала прошлое, и так далее. Так что да, по возрастанию. Да, большинство из них будут свиданиями в будущем.
UpHelix

Ответы:

112

smalldatetime имеет диапазон до 6 июня 2079 г., поэтому вы можете использовать

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

Если в законных записях не будет этой даты.

Если это не предположение, вы можете полагаться на более надежный вариант - сортировку по двум столбцам.

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

Оба приведенных выше предложения не могут использовать индекс, чтобы избежать сортировки и дать похожие планы.

введите описание изображения здесь

Еще одна возможность, если такой индекс существует:

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

Строить планы

Мартин Смит
источник
Этот прием можно применить и к VARCHARполям (например ORDER BY ISNULL(my_varchar, 'ZZZZZZ')), и он чрезвычайно полезен, особенно для получения заказов определенным образом при использовании GROUP BY . . . GROUPING SETS. Спасибо, что разместили это.
sparc_spread
Почему мы не можем использовать порядок по убыванию, чтобы помещать нули внизу? кроме того, почему мы сопоставляем ноль с 1?
MasterJoe 01
35

По словам Ицика Бен-Гана, автора книги « Основы T-SQL для MS SQL Server 2012» : «По умолчанию SQL Server сортирует метки NULL перед значениями, отличными от NULL . Чтобы получить метки NULL для сортировки в последнюю очередь, вы можете использовать выражение CASE, которое возвращает 1 , когда « Next_Contact_Date столбец NULL ,» и 0 , если он не NULL Непро-. NULL метки получить 0 обратно из выражения, поэтому они сортируют перед тем NULL знаков (которые получают 1) Это. СЛУЧАЙ выражение используется в качестве первой столбец сортировки. " Next_Contact_Datecolumn "следует указать как второй столбец сортировки. Таким образом, ненулевые отметки правильно сортируют между собой." Вот запрос решения для вашего примера для MS SQL Server 2012 (и SQL Server 2014):

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

Эквивалентный код с использованием синтаксиса IIF:

ORDER BY 
   IIF(Next_Contact_Date IS NULL, 1, 0),
   Next_Contact_Date;
Энди
источник
Кроме того, чтобы добавить к ответу, если вы переключите 1 и 0 в IIF вокруг нулей, они переместятся наверх. Это также работает, если вы хотите выделить кортежи, поместив их в верхнюю часть таблицы.
Franco Pettigrosso
3

Если ваш SQL не поддерживает NULLS FIRSTили NULLS LAST, самый простой способ сделать это - использовать value IS NULLвыражение:

ORDER BY Next_Contact_Date IS NULL, Next_Contact_Date

поставить нули в конец ( NULLS LAST) или

ORDER BY Next_Contact_Date IS NOT NULL, Next_Contact_Date

поставить нули впереди. Это не требует знания типа столбца, и его легче читать, чем CASEвыражение.

РЕДАКТИРОВАТЬ: Увы, хотя это работает в других реализациях SQL, таких как PostgreSQL и MySQL, это не работает в MS SQL Server. У меня не было SQL Server для тестирования, и я полагался на документацию Microsoft и тестирование с другими реализациями SQL. Согласно Microsoft, value IS NULL это выражение, которое должно использоваться, как и любое другое выражение. И ORDER BY должен принимать выражения как и любой другой оператор, который принимает выражение. Но на самом деле это не работает.

Поэтому лучшим решением для SQL Server является CASEвыражение.

Вроо
источник
7
Это недопустимый синтаксис SQL Server
Мартин Смит,
3
Прости за это. Он должен быть действительным в документации Microsoft и работать с другими SQL, но MS на самом деле этого не позволяет.
Vroo
с точки зрения производительности, это звучит ужасно, вы выполняете 2 критерия сортировки
ColacX
В документации Microsoft, которую вы связали, я прочитал, что значение IS NULL - это не выражение , а предикат . Это не одно и то же.
BertuPG
1
Согласно Microsoft, предикат - это выражение. Это буквально первые три слова на docs.microsoft.com/en-us/sql/t-sql/queries/predicates
Vroo
3
order by -cast([Next_Contact_Date] as bigint) desc
папарацци
источник
выдаст ошибку , если Next_Contact_Dateравна нулюExplicit conversion from data type date to bigint is not allowed.
Nerdroid
2

Немного поздно, но, возможно, кому-то это пригодится.

Для меня ISNULL был исключен из-за сканирования таблицы. UNION ALL потребует, чтобы я повторил сложный запрос, и из-за того, что я выбрал только TOP X, это было бы не очень эффективно.

Если у вас есть возможность изменить дизайн стола, вы можете:

  1. Добавьте еще одно поле для сортировки, например Next_Contact_Date_Sort.

  2. Создайте триггер, который заполняет это поле большим (или маленьким) значением, в зависимости от того, что вам нужно:

    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
    
Ааа
источник
2

Используйте desc и при необходимости умножьте на -1. Пример сортировки int по возрастанию с последними нулями:

select * 
from
(select null v union all select 1 v union all select 2 v) t
order by -t.v desc
Ульф Херрманн
источник
Не думаю, что это сработает для таких переменных, как даты ..
Шарлотта Дэн,
1

Я знаю, что это старый, но это то, что у меня сработало

Order by Isnull(Date,'12/31/9999')
анонимный
источник