Prueba si alguna columna es NULL

16

Estoy tratando de encontrar una consulta fácil que pueda hacer para probar si una tabla grande tiene una lista de entradas que tiene al menos UN valor en blanco (NULL / empty) en CUALQUIER columna.

Necesito algo como

SELECT * FROM table AS t WHERE ANY(t.* IS NULL)

No quiero tener que hacer

SELECT * FROM table AS t WHERE t.c1 = NULL OR t.c2 = NULL OR t.c3 = NULL

Esta sería una consulta ENORME.

Diestro
fuente

Respuestas:

16

Una extensión de la respuesta de @ db2 con menos (leer: cero) disputas manuales:

DECLARE @tb nvarchar(512) = N'dbo.[table]';

DECLARE @sql nvarchar(max) = N'SELECT * FROM ' + @tb
    + ' WHERE 1 = 0';

SELECT @sql += N' OR ' + QUOTENAME(name) + ' IS NULL'
    FROM sys.columns 
    WHERE [object_id] = OBJECT_ID(@tb);

EXEC sys.sp_executesql @sql;
Aaron Bertrand
fuente
8

Debe enumerar todas las columnas según el comentario de JNK.

WHERE c1 IS NULL OR c2 IS NULL OR c3 IS NULL

Sin embargo, a continuación se muestra un enfoque algo menos eficiente que evita esto.

;WITH xmlnamespaces('http://www.w3.org/2001/XMLSchema-instance' AS ns) 
SELECT * 
FROM   YourTable AS T1 
WHERE (
    SELECT T1.* 
    FOR XML PATH('row'), ELEMENTS XSINIL, TYPE
  ).exist('//*/@ns:nil') = 1 

(Basado en esta respuesta SO)

Martin Smith
fuente
5

No hay una buena sintaxis incorporada, pero Management Studio tiene un par de características convenientes para generar la consulta rápidamente.

En el Explorador de objetos, profundice en la tabla que desee, amplíelo y luego arrastre toda la carpeta "Columnas" a un editor de consultas en blanco. Esto agregará una lista de columnas separadas por comas a la consulta.

A continuación, abra Buscar y reemplazar. Establezca "Buscar qué" ,y establezca "Reemplazar por" en IS NULL OR(con un espacio inicial) y luego presione Reemplazar todo. Tendrás que limpiar el último de la secuencia a mano.

Todavía es feo, pero es menos laborioso y feo.

db2
fuente
4

Múltiples soluciones para: algunos nulos, todos nulos, columnas simples y múltiples, además de hacerlo RÁPIDO usando Top 1

Si necesita probar varias columnas, puede usar lo siguiente:

Column_1 Column_2 Column_3
-------- -------- --------
1        2        NULL
1        NULL     NULL
5        6        NULL

Primero , pruebe los NULL y cuéntelos:

select 
    sum(case when Column_1 is null then 1 else 0 end) as Column_1, 
    sum(case when Column_2 is null then 1 else 0 end) as Column_2, 
    sum(case when Column_3 is null then 1 else 0 end) as Column_3,
from TestTable 

Produce un recuento de NULL:

Column_1  Column_2  Column_3
0         1         3

Donde el resultado es 0, no hay NULL.

En segundo lugar , cuentemos los no NULL:

select 
    sum(case when Column_1 is null then 0 else 1 end) as Column_1, 
    sum(case when Column_2 is null then 0 else 1 end) as Column_2, 
    sum(case when Column_3 is null then 0 else 1 end) as Column_3,
from TestTable

... Pero debido a que estamos contando no NULL aquí, esto se puede simplificar para:

select 
    count(Column_1) as Column_1, 
    count(Column_2) as Column_2, 
    count(Column_3) as Column_3,
from TestTable

Cualquiera de los dos produce:

Column_1  Column_2  Column_3
3         2         0

Donde el resultado es 0, la columna está compuesta completamente por NULL.

Por último , si solo necesita verificar una columna específica, TOP 1 es más rápido porque debería detenerse en el primer golpe. Luego, opcionalmente, puede utilizar count (*) para obtener un resultado de estilo booleano:

select top 1 'There is at least one NULL' from TestTable where Column_3 is NULL

select count(*) from (select top 1 'There is at least one NULL' AS note from TestTable where Column_3 is NULL) a

0 = No hay NULL, 1 = Hay al menos un NULL

o

select top 1 'There is at least one non-NULL' AS note from TestTable where Column_3 is not NULL

select count(*) from (select top 1 'There is at least one non-NULL' AS note from TestTable where Column_3 is not NULL) a

0 = Todos son NULL, 1 = Hay al menos un no NULL

Espero que esto ayude.

jwolf
fuente
Si bien esto parece bastante útil, siento la obligación de tener en cuenta que no es lo que pedía el OP: querían el contenido de cada fila que incluyera un valor NULL, no solo una comprobación para ver si existe.
RDFozz
Lo suficientemente justo. Creo que lo estaba leyendo de manera diferente. Estaba concentrado en la parte "... probar si una tabla grande tiene ...", así que ... booleano (en mi caso booleano). Pero si, por "lista de entradas", se refería a filas, entonces tienes toda la razón.
jwolf
Solo volví a visitar esto. Definitivamente malinterpreté la pregunta: debería haber inferido que estaba buscando las filas como resultado. Creo que también leí mal lo que quería decir con ENORME. Originalmente pensé que se refería a computacionalmente caro, pero ahora solo pienso que se refería a las columnas para que Arron y DB2 lo entendieran bien, tanto en la lectura como en las soluciones (dependiendo de cuál esté más cansado: tu cerebro o tus dedos)
jwolf
2

UNPIVOT traduce columnas en filas. En el proceso elimina valores NULL ( referencia ).

Dada la entrada

create table #t
(
    ID  int primary key,
    c1  int null,
    c2  int null
);

insert #t(id, c1, c2)
values
    (1, 12, 13),
    (2, null, 14),
    (3, 15, null),
    (4, null, null);

la consulta UNPIVOT

select
    ID, ColName, ColValue
from
(
    select *
    from #t
) as p
unpivot
(
    ColValue for ColName in
    (c1, c2)                  -- explicit source column names required
) as unpvt;

producirá la salida

| ID | ColName | ColValue |
|----|---------|----------|
| 1  | c1      | 12       |
| 1  | c2      | 13       |
| 2  | c2      | 14       |
| 3  | c1      | 15       |

Lamentablemente, la fila 4 se ha eliminado por completo, ya que solo tiene NULL. Se puede reiniciar convenientemente inyectando un valor ficticio en la consulta de origen:

select
    ID, ColName, ColValue
from
(
    select
        -5 as dummy,               -- injected here, -5 is arbitrary
        *
    from #t
) as p
unpivot
(
    ColValue for ColName in
    (dummy, c1, c2)                -- referenced here
) as unpvt;

Al agregar las filas en la ID, podemos contar los valores no nulos. Una comparación con el número total de columnas en la tabla de origen identificará las filas que contienen uno o más NULL.

select
    ID
from
(
    select -5 as dummy, *
    from #t
) as p
unpivot
(
    ColValue for ColName in
    (dummy, c1, c2)
) as unpvt
group by ID
having COUNT(*) <> 3;


Calculo 3 como número de columnas en la tabla fuente #t
+ 1 para la columna ficticia inyectada
- 1 para ID, que no está SIN AVANZAR

Este valor se puede obtener en tiempo de ejecución examinando las tablas del catálogo.

Las filas originales se pueden recuperar uniéndose a los resultados.

Si se van a investigar valores distintos de NULL, se pueden incluir en una cláusula where:

...
) as unpvt
where ColValue <> ''      -- will eliminate empty strings

Discusión

Esto requiere un identificador que se lleva a través de UNPIVOT. Una clave sería lo mejor. Si no existe ninguno , la función de ventana ROW_NUMBER () puede inyectarlo , aunque puede ser costoso ejecutarlo.

Todas las columnas deben enumerarse explícitamente dentro de la cláusula UNPIVOT. Se pueden arrastrar usando SSMS, como sugirió @ db2. No será dinámico cuando la definición de la tabla cambie, como lo sería la sugerencia de Aaron Bertrand. Sin embargo, este es el caso de casi todos los SQL.

Para mi conjunto de datos bastante limitado, el plan de ejecución es un análisis de índice agrupado y un agregado de flujo. Esto será más costoso de memoria que un escaneo directo de la tabla y muchas cláusulas OR.

Michael Green
fuente