Reputation: 98
I'm trying to port some SQL to code and I've noticed that SQL Server's ORDER BY (and partition ORDER BY) seem to behave somewhat non-deterministically in some cases, but perhaps I'm just not seeing the pattern. I'm using Azure SQL Database which should be more or less SQL Server 2017 with some limitations.
Here are some examples:
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);
Produces the following, unexpected, result set where the datetimes are before the date on the same day AND the later datetime is before the earlier one:
t u
NULL 2014-01-01 23:59:59
NULL 2014-01-01 00:01:00
2014-01-01 NULL
2014-01-02 NULL
However, if I add a few extra rows:
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);
The date is still after the datetimes, but now the datetimes are in order:
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
Using partitions produce similar results:
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);
Any guidance with regard to what is happening or how I can reproduce this behavior would be appreciated.
Upvotes: 3
Views: 56
Reputation: 1270573
If you run this query:
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)
You will see what happens. Only the first 10 characters appear. You can see this in this rex tester.
isnull()
has to determine a type for the expression. It chooses a string, whose length is the first argument. Hence, the results make perfect sense. This is explained in the documentation:
Returns the same type as check_expression.
Note that the ANSI-standard coalesce()
does more work on type-checking, so it returns what you expect.
Upvotes: 6