SQL Server ORDER BY日期,最后为空


80

我想按日期订购。我希望最近的日期排在第一位。这很容易,但是有许多记录为空,并且在任何有日期的记录之前。

我尝试了一些没有成功的事情:

ORDER BY ISNULL(Next_Contact_Date, 0)

ORDER BY ISNULL(Next_Contact_Date, 999999999)

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

如何按日期排序并让空值排​​在最后?数据类型为smalldatetime


排序顺序是否需要升序,但结尾必须为null?表格中还会有将来的日期吗?
AllenG

@AllenG,是的,从过去到未来,从过去到现在等等。是的,上升。是的,将来的日期将是大多数日期。
UpHelix 2011年

Answers:


110

smalldatetime 范围截至2079年6月6日,因此您可以使用

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

为什么不能使用desc的order来将null放在底部?同样,为什么我们将null映射到1?
MasterJoe

35

据伊茨克奔甘,作者T-SQL基础对MS SQL Server 2012中,“默认情况下,SQL Server的排序NULL之前非标记NULL值。要获得NULL痕最后排序,你可以使用一个CASE表达式返回1当“ Next_Contact_Date列是NULL,”和0时,它不是NULL非。NULL马克获得从表达式0后面;因此,它们排序之前NULL标记(这得到1)本。CASE表达被用作所述第一排序列。” 该Next_Contact_Date列“应指定为第二个排序列。这样,非NULL标记将在它们之间正确排序。” 这是您的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;

另外,要增加答案,如果将IIF的1和0切换为零,则会转到顶部。如果您希望将元组放在表的顶部,这也可以使用。
弗朗哥·佩蒂格罗索

3
order by -cast([Next_Contact_Date] as bigint) desc

如果Next_Contact_Date为nullExplicit conversion from data type date to bigint is not allowed.
则会

2

如果您的SQL不支持NULLS FIRSTNULLS 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表达式更容易阅读。

编辑:las,虽然这可以在其他SQL实现(例如PostgreSQL和MySQL)中使用,但在MS SQL Server中不起作用。我没有SQL Server可以测试,也没有依赖Microsoft的文档和其他SQL实现的测试。根据Microsoft的说法,value IS NULL 该表达式应该与其他任何表达式一样可用。并且ORDER BY 应该像接受其他任何使用表达式的语句一样使用表达式。但这实际上不起作用。

因此,SQL Server的最佳解决方案似乎是CASE表达式。


7
这不是有效的SQL Server语法
Martin Smith

3
对于那个很抱歉。它应该根据Microsoft文档有效,并且可以在其他SQL中使用,但是MS实际上不允许这样做。
Vroo

性能明智,这听起来很糟糕,您正在执行2个排序标准
ColacX '16

在您链接的Microsoft文档中,我读到IS NULL值不是表达式,而是谓词。这是不一样的。
BertuPG

1
根据Microsoft的说法,谓词是一个表达式。从字面上看,这就是docs.microsoft.com/zh-cn/sql/t-sql/queries/predicates上
Vroo

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。最后以null升序进行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')
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.