UNPIVOT übersetzt Spalten in Zeilen. Dabei werden NULL-Werte ( Referenz ) entfernt.
Angesichts der Eingabe
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);
die UNPIVOT-Abfrage
select
ID, ColName, ColValue
from
(
select *
from #t
) as p
unpivot
(
ColValue for ColName in
(c1, c2) -- explicit source column names required
) as unpvt;
erzeugt die Ausgabe
| ID | ColName | ColValue |
|----|---------|----------|
| 1 | c1 | 12 |
| 1 | c2 | 13 |
| 2 | c2 | 14 |
| 3 | c1 | 15 |
Leider wurde Zeile 4 komplett entfernt, da sie nur NULL enthält! Es kann bequem wieder eingeführt werden, indem ein Dummy-Wert in die Quellabfrage eingefügt wird:
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;
Durch Aggregieren der Zeilen nach ID können wir die Nicht-Null-Werte zählen. Ein Vergleich mit der Gesamtzahl der Spalten in der Quellentabelle ergibt Zeilen mit einem oder mehreren NULL-Werten.
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;
Ich berechne 3 als
Anzahl der Spalten in der Quellentabelle #t
+ 1 für die injizierte Dummy-Spalte
- 1 für ID, die nicht UNPIVOTED ist
Dieser Wert kann zur Laufzeit durch Untersuchen der Katalogtabellen ermittelt werden.
Die ursprünglichen Zeilen können durch Zusammenfügen mit den Ergebnissen abgerufen werden.
Wenn andere Werte als NULL untersucht werden sollen, können sie in eine where-Klausel aufgenommen werden:
...
) as unpvt
where ColValue <> '' -- will eliminate empty strings
Diskussion
Dies erfordert eine Kennung, die über den UNPIVOT übertragen wird. Ein Schlüssel wäre am besten. Wenn keine vorhanden ist, kann eine von der ROW_NUMBER () -Fensterfunktion injiziert werden , obwohl dies in der Ausführung teuer sein kann.
Alle Spalten müssen explizit in der UNPIVOT-Klausel aufgeführt sein. Sie können mit SSMS eingezogen werden, wie von @ db2 vorgeschlagen. Es wird nicht dynamisch sein, wenn sich die Tabellendefinition ändert, wie es der Vorschlag von Aaron Bertrand wäre. Dies ist jedoch bei fast allen SQL-Anweisungen der Fall.
Für meine eher begrenzte Datenmenge besteht der Ausführungsplan aus einem Clustered-Index-Scan und einem Stream-Aggregat. Dies ist speicherintensiver als ein direkter Scan der Tabelle und viele OR-Klauseln.