SQL Server ORDER BY fecha y últimos nulos

82

Estoy intentando ordenar por fecha. Quiero que las fechas más recientes aparezcan primero. Eso es bastante fácil, pero hay muchos registros que son nulos y vienen antes de cualquier registro que tenga una fecha.

He intentado algunas cosas sin éxito:

ORDER BY ISNULL(Next_Contact_Date, 0)

ORDER BY ISNULL(Next_Contact_Date, 999999999)

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

¿Cómo puedo ordenar por fecha y que los nulos sean los últimos? El tipo de datos es smalldatetime.

UpHelix
fuente
¿El orden de clasificación debe ser Ascendente, pero con nulos al final? ¿Y tendrás fechas futuras en tu mesa?
AllenG
@AllenG, sí, del pasado al futuro con el pasado primero y así sucesivamente. Así que sí, ascendiendo. Sí, las fechas futuras son las que serán la mayoría de ellas.
UpHelix

Respuestas:

112

smalldatetime tiene un rango hasta el 6 de junio de 2079, por lo que puede usar

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

Si no hay registros legítimos tendrá esa fecha.

Si esta no es una suposición, le gustaría confiar en una opción más sólida: ordenar en dos columnas.

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

Sin embargo, ambas sugerencias anteriores no pueden utilizar un índice para evitar una clasificación y ofrecen planes de apariencia similar.

ingrese la descripción de la imagen aquí

Otra posibilidad si existe tal índice es

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
fuente
Este truco también se puede aplicar a los VARCHARcampos (p ORDER BY ISNULL(my_varchar, 'ZZZZZZ'). Ej. ) Y es extremadamente útil, especialmente para obtener pedidos de cierta manera cuando se usa GROUP BY . . . GROUPING SETS. Gracias por publicar esto.
sparc_spread
¿Por qué no podemos usar order by desc para poner nulos en la parte inferior? además, ¿por qué asignamos nulo a 1?
MasterJoe
35

Según Itzik Ben-Gan, autor de T-SQL Fundamentals para MS SQL Server 2012 , "De forma predeterminada, SQL Server ordena las marcas NULL antes que los valores no NULL . Para que las marcas NULL clasifiquen en último lugar, puede usar una expresión CASE que devuelva 1 cuando la columna " Next_Contact_Date es NULL " y 0 cuando no es NULL . Las marcas que no son NULL obtienen 0 de la expresión; por lo tanto, se clasifican antes que las marcas NULL (que obtienen 1). Esta expresión CASE se usa como la primera ordenar columna ". el Next_Contact_Datecolumn "debe especificarse como la segunda columna de clasificación. De esta forma, las marcas que no son NULL se clasifican correctamente entre sí". Aquí está la consulta de solución para su ejemplo para MS SQL Server 2012 (y SQL Server 2014):

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

Código equivalente con sintaxis IIF:

ORDER BY 
   IIF(Next_Contact_Date IS NULL, 1, 0),
   Next_Contact_Date;
Andy
fuente
Además, para agregar a la respuesta, si cambia el 1 y el 0 del IIF, los nulos irán a la parte superior. Esto también funciona si desea una tupla única colocándolas en la parte superior de la mesa.
Franco Pettigrosso
3

Si su SQL no admite NULLS FIRSTo NULLS LAST, la forma más sencilla de hacer esto es usar la value IS NULLexpresión:

ORDER BY Next_Contact_Date IS NULL, Next_Contact_Date

para poner los nulos al final ( NULLS LAST) o

ORDER BY Next_Contact_Date IS NOT NULL, Next_Contact_Date

para poner los nulos al frente. Esto no requiere conocer el tipo de columna y es más fácil de leer que la CASEexpresión.

EDITAR: Por desgracia, aunque esto funciona en otras implementaciones de SQL como PostgreSQL y MySQL, no funciona en MS SQL Server. No tenía un servidor SQL para probar y confié en la documentación y las pruebas de Microsoft con otras implementaciones de SQL. Según Microsoft, value IS NULL es una expresión que debería ser utilizable como cualquier otra expresión. Y ORDER BY se supone que toma expresiones como cualquier otra declaración que toma una expresión. Pero en realidad no funciona.

Por tanto, la mejor solución para SQL Server parece ser la CASEexpresión.

Vroo
fuente
7
Esta no es una sintaxis válida de SQL Server
Martin Smith
3
Lo siento por eso. Se debe ser válido por la documentación y obras en otros LSQ Microsoft, pero MS en realidad no lo permiten.
Vroo
En cuanto al rendimiento, esto suena terrible, estás haciendo 2 criterios de clasificación
ColacX
En la documentación de Microsoft que vinculó, leí que el valor IS NULL no es una expresión , sino un predicado . No es lo mismo.
BertuPG
1
Según Microsoft, un predicado es una expresión. Esas son literalmente las primeras tres palabras en docs.microsoft.com/en-us/sql/t-sql/queries/predicates
Vroo
3
order by -cast([Next_Contact_Date] as bigint) desc
paparazzo
fuente
arrojar un error si Next_Contact_Datees nuloExplicit conversion from data type date to bigint is not allowed.
Nerdroid
2

Un poco tarde, pero tal vez alguien lo encuentre útil.

Para mí, ISNULL estaba fuera de discusión debido al escaneo de la tabla. UNION ALL necesitaría que repitiera una consulta compleja, y debido a que seleccioné solo el TOP X, no habría sido muy eficiente.

Si puede cambiar el diseño de la mesa, puede:

  1. Agregue otro campo, solo para ordenar, como Next_Contact_Date_Sort.

  2. Cree un disparador que llene ese campo con un valor grande (o pequeño), según lo que necesite:

    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
fuente
2

Use desc y multiplique por -1 si es necesario. Ejemplo de orden int ascendente con nulos al final:

select * 
from
(select null v union all select 1 v union all select 2 v) t
order by -t.v desc
Ulf Herrmann
fuente
No creo que esto funcione para fechas variables como ...
Charlotte Deng
1

Sé que esto es viejo pero esto es lo que funcionó para mí

Order by Isnull(Date,'12/31/9999')
anónimo
fuente