SQL Server的奇怪排序

时间:2018-08-07 23:16:52

标签: sql sql-server

我正在尝试将一些SQL移植到代码中,并且我注意到SQL Server的ORDER BY(和分区ORDER BY)在某些情况下似乎表现得不确定,但是也许我只是没有看到这种模式。我正在使用Azure SQL数据库,该数据库或多或少应该是SQL Server 2017,但有一些限制。

以下是一些示例:

select * from (
    values
        ('2014-01-01', NULL),
        ('2014-01-02', NULL),
        (NULL, '2014-01-01 00:01:00'),
        (NULL, '2014-01-01 23:59:59')
) AS temp (t, u)
order by isnull(t, u);

产生以下意外结果集,其中日期时间在同一天的日期之前,而较晚的日期时间在较早的日期之前:

t           u
NULL        2014-01-01 23:59:59
NULL        2014-01-01 00:01:00
2014-01-01  NULL
2014-01-02  NULL

但是,如果我添加一些额外的行:

select * from (
    values
        ('2014-01-01', NULL),
        ('2014-01-02', NULL),
        (NULL, '2014-01-01 00:01:00'),
        (NULL, '2014-01-01 23:59:59'),
        (NULL, '2014-01-01 00:00:00'),
        (NULL, '2014-01-02 00:00:00')
) AS temp (t, u)
order by isnull(t, u);

日期仍然在日期时间之后,但是现在日期时间是按顺序排列的:

t           u
NULL        2014-01-01 00:00:00
NULL        2014-01-01 00:01:00
NULL        2014-01-01 23:59:59
2014-01-01  NULL
NULL        2014-01-02 00:00:00
2014-01-02  NULL

使用分区会产生类似的结果:

select 
    *,row_number() over (
    partition BY g
    ORDER BY isnull(t, u)
    ) rn
from (
    values
        (123, '2014-01-01', NULL),
        (123, '2014-01-02', NULL),
        (123, NULL, '2014-01-01 00:01:00'),
        (123, NULL, '2014-01-01 23:59:59')
) AS temp (g, t, u);


select 
    *,row_number() over (
    partition BY g
    ORDER BY isnull(t, u)
    ) rn
from (
    values
        (123, '2014-01-01', NULL),
        (123, '2014-01-02', NULL),
        (123, NULL, '2014-01-01 00:01:00'),
        (123, NULL, '2014-01-01 23:59:59'),
        (123, NULL, '2014-01-01 00:00:00'),
        (123, NULL, '2014-01-02 00:00:00')
) AS temp (g, t, u);

任何有关正在发生的事情或如何重现此行为的指导都将受到赞赏。

1 个答案:

答案 0 :(得分:6)

如果您运行此查询:

select temp.*, isnull(t, u)    from (values
        ('2014-01-01', NULL),
        ('2014-01-02', NULL),
        (NULL, '2014-01-01 00:01:00'),
        (NULL, '2014-01-01 23:59:59'),
        (NULL, '2014-01-01 00:00:00'),
        (NULL, '2014-01-02 00:00:00')
     ) temp (t, u)
order by isnull(t, u)

您将看到会发生什么。仅出现前10个字符。您可以在this rex tester中看到它。

isnull()必须确定表达式的类型。它选择一个字符串,其长度是第一个参数。因此,结果很有意义。 documentation中对此进行了解释:

  

返回与check_expression相同的类型。

请注意,ANSI标准coalesce()在类型检查方面做得更多,因此它返回您期望的结果。