99

I am trying to order by date. I want the most recent dates coming in first. That's easy enough, but there are many records that are null and those come before any records that have a date.

I have tried a few things with no success:

ORDER BY ISNULL(Next_Contact_Date, 0)

ORDER BY ISNULL(Next_Contact_Date, 999999999)

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

How can I order by date and have the nulls come in last? The data type is smalldatetime.

Mechlar
  • 4,946
  • 12
  • 58
  • 83
  • Does the sort order need to be Ascending, but with nulls at the end? And will you have future dates in your table? – AllenG May 04 '11 at 16:46
  • @AllenG, yeah, from past to future with past first and so on. So yeah, ascending. Yes, future dates are what most of them will be. – Mechlar May 04 '11 at 16:49

9 Answers9

127

smalldatetime has range up to June 6, 2079 so you can use

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

If no legitimate records will have that date.

If this is not an assumption you fancy relying on a more robust option is sorting on two columns.

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

Both of the above suggestions are not able to use an index to avoid a sort however and give similar looking plans.

enter image description here

One other possibility if such an index exists is

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
  • 438,706
  • 87
  • 741
  • 845
  • This trick can be applied to `VARCHAR` fields as well (e.g. `ORDER BY ISNULL(my_varchar, 'ZZZZZZ')` )and is extremely useful, especially for getting orders a certain way when using `GROUP BY . . . GROUPING SETS`. Thanks for posting this. – sparc_spread Jan 16 '19 at 19:53
  • Why can't we use order by desc to put nulls at the bottom ? also, why do we map null to 1 ? – MasterJoe Sep 01 '20 at 20:03
45

According to Itzik Ben-Gan, author of T-SQL Fundamentals for MS SQL Server 2012, "By default, SQL Server sorts NULL marks before non-NULL values. To get NULL marks to sort last, you can use a CASE expression that returns 1 when the" Next_Contact_Date column is NULL, "and 0 when it is not NULL. Non-NULL marks get 0 back from the expression; therefore, they sort before NULL marks (which get 1). This CASE expression is used as the first sort column." The Next_Contact_Date column "should be specified as the second sort column. This way, non-NULL marks sort correctly among themselves." Here is the solution query for your example for MS SQL Server 2012 (and SQL Server 2014):

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

Equivalent code using IIF syntax:

ORDER BY 
   IIF(Next_Contact_Date IS NULL, 1, 0),
   Next_Contact_Date;
Pedro
  • 1,274
  • 13
  • 12
Andy
  • 789
  • 8
  • 19
  • Also, to add to the answer, if you switch the IIF's 1 and 0 around the nulls will go to the top. This also works if you want a single out tuples by putting them on the top of the table. – Franco Pettigrosso Jan 08 '19 at 15:34
4
order by -cast([Next_Contact_Date] as bigint) desc
paparazzo
  • 44,497
  • 23
  • 105
  • 176
  • throw an error if `Next_Contact_Date` is null `Explicit conversion from data type date to bigint is not allowed.` – Nerdroid May 26 '17 at 04:15
3

If your SQL doesn't support NULLS FIRST or NULLS LAST, the simplest way to do this is to use the value IS NULL expression:

ORDER BY Next_Contact_Date IS NULL, Next_Contact_Date

to put the nulls at the end (NULLS LAST) or

ORDER BY Next_Contact_Date IS NOT NULL, Next_Contact_Date

to put the nulls at the front. This doesn't require knowing the type of the column and is easier to read than the CASE expression.

EDIT: Alas, while this works in other SQL implementations like PostgreSQL and MySQL, it doesn't work in MS SQL Server. I didn't have a SQL Server to test against and relied on Microsoft's documentation and testing with other SQL implementations. According to Microsoft, value IS NULL is an expression that should be usable just like any other expression. And ORDER BY is supposed to take expressions just like any other statement that takes an expression. But it doesn't actually work.

The best solution for SQL Server therefore appears to be the CASE expression.

Vroo
  • 1,064
  • 1
  • 11
  • 19
  • 9
    This is not valid SQL Server syntax – Martin Smith Aug 22 '13 at 08:19
  • 3
    Sorry about that. It *should* be valid per Microsoft documentation and works in other SQLs, but MS doesn't actually allow it. – Vroo Aug 22 '13 at 17:18
  • performance wise this sounds terrible your are doing 2 sort criterias – ColacX Feb 19 '16 at 14:14
  • In Microsoft documentation that you linked, I read that **value IS NULL** is not an *expression*, but a *predicate*. It's not the same. – BertuPG Aug 07 '18 at 08:00
  • 2
    According to Microsoft, a predicate is an expression. That's literally the first three words at https://learn.microsoft.com/en-us/sql/t-sql/queries/predicates – Vroo Sep 03 '18 at 06:48
2

A bit late, but maybe someone finds it useful.

For me, ISNULL was out of question due to the table scan. UNION ALL would need me to repeat a complex query, and due to me selecting only the TOP X it would not have been very efficient.

If you are able to change the table design, you can:

  1. Add another field, just for sorting, such as Next_Contact_Date_Sort.

  2. Create a trigger that fills that field with a large (or small) value, depending on what you need:

    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
  • 614
  • 5
  • 14
2

Use desc and multiply by -1 if necessary. Example for ascending int ordering with nulls last:

select * 
from
(select null v union all select 1 v union all select 2 v) t
order by -t.v desc
Progo
  • 3,452
  • 5
  • 27
  • 44
2

I know this is old but this is what worked for me

Order by Isnull(Date,'12/31/9999')
anonymous
  • 113
  • 13
0

I think I found a way to show nulls in the end and still be able to use indexes for sorting.

The idea is super simple - create a calculatable column which will be based on existing column, and put an index on it.

ALTER TABLE dbo.Users 
ADD [FirstNameNullLast]  
AS (case when [FirstName] IS NOT NULL AND (ltrim(rtrim([FirstName]))<>N'' OR [FirstName] IS NULL) then [FirstName] else N'ZZZZZZZZZZ' end) PERSISTED

So, we are creating a persisted calculatable column in the SQL, in that column all blank and null values will be replaced by 'ZZZZZZZZ', this will mean, that if we will try to sort based on that column, we will see all the null or blank values in the end. Now we can use it in our new index.

Like this:

CREATE NONCLUSTERED INDEX [IX_Users_FirstNameNullLast] ON [dbo].[Users]
(
    [FirstNameNullLast] ASC
)

So, this is an ordinary nonclustered index. We can change it however we want, i.e. include extra columns, increase number of indexes columns, change sorting order etc.

Eramir
  • 482
  • 1
  • 5
  • 18
0

I know this is a old thread, but in SQL Server nulls are always lower than non-null values. So it's only necessary to order by Desc

In your case Order by Next_Contact_Date Desc should be enough.

Source: order by with nulls- LearnSql

XAronX
  • 54
  • 1
  • 10