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.