Prüfen Sie, ob Spalten NULL sind

16

Ich versuche, eine einfache Abfrage zu finden, mit der ich testen kann, ob eine große Tabelle eine Liste von Einträgen enthält, die mindestens EINEN leeren Wert (NULL / leer) in JEDER Spalte enthält.

Ich brauche sowas

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

Ich möchte nicht tun müssen

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

Dies wäre eine große Abfrage.

Dexter
quelle

Antworten:

16

Eine Erweiterung der Antwort von @ db2 mit weniger (read: zero) Hand-Wrangling:

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
quelle
8

Sie sollten alle Spalten gemäß dem Kommentar von JNK auflisten.

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

Ein etwas weniger effizienter Ansatz, der dies vermeidet, ist im Folgenden aufgeführt.

;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 

(Basierend auf dieser SO-Antwort)

Martin Smith
quelle
5

Es gibt keine nette eingebaute Syntax, aber Management Studio verfügt über ein paar praktische Funktionen, um die Abfrage schnell zu generieren.

Gehen Sie im Objekt-Explorer zu der gewünschten Tabelle, erweitern Sie sie und ziehen Sie den gesamten Ordner "Columns" in einen leeren Abfrage-Editor. Dadurch wird der Abfrage eine durch Kommas getrennte Liste von Spalten hinzugefügt.

Öffnen Sie als Nächstes Suchen und Ersetzen. Stellen Sie "Suchen nach" auf ,und "Ersetzen durch" auf IS NULL OR(mit einem führenden Leerzeichen) und klicken Sie dann auf "Alle ersetzen". Sie müssen den letzten in der Sequenz von Hand bereinigen.

Es ist immer noch hässlich, aber weniger arbeitsintensiv.

db2
quelle
4

Mehrere Lösungen für: einige NULL-Werte, alle NULL-Werte, einzelne und mehrere Spalten sowie die SCHNELLE Verwendung von Top 1

Wenn Sie mehrere Spalten testen müssen, können Sie Folgendes verwenden:

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

Erstens , Test für NULL - Werte und sie zählen:

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 

Ergibt eine Anzahl von NULLs:

Column_1  Column_2  Column_3
0         1         3

Wenn das Ergebnis 0 ist, gibt es keine NULL-Werte.

Zweitens wollen wir die Nicht-NULL-Werte zählen:

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

... Da wir hier jedoch Nicht-NULL-Werte zählen, kann dies vereinfacht werden:

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

Beides ergibt:

Column_1  Column_2  Column_3
3         2         0

Wenn das Ergebnis 0 ist, besteht die Spalte vollständig aus NULL.

Schließlich , wenn Sie nur eine bestimmte Spalte müssen prüfen, dann 1 TOP ist schneller , weil es bei dem ersten Treffer aufhören sollte. Sie können dann optional count (*) verwenden, um ein Ergebnis im Booleschen Stil zu erhalten:

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 = Es gibt keine NULL, 1 = Es gibt mindestens eine NULL

oder

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 = Sie sind alle NULL, 1 = Es gibt mindestens einen Nicht-NULL

Ich hoffe das hilft.

Wolf
quelle
Auch wenn dies recht nützlich erscheint, muss ich feststellen, dass es nicht das ist, wonach das OP gefragt hat - sie wollten, dass der Inhalt jeder Zeile einen NULL-Wert enthält und nicht nur überprüft, ob einer vorhanden ist.
RDFozz
Meinetwegen. Ich glaube, ich habe es nur anders gelesen. Ich konzentrierte mich auf den Teil "... testen, ob eine große Tabelle ...", also ... Boolean (in meinem Fall Boolean-ish). Aber wenn er mit "Liste der Einträge" Zeilen meinte, dann haben Sie absolut Recht.
Jwolf
Ich habe es gerade noch einmal besucht. Ich habe die Frage definitiv falsch interpretiert - hätte daraus schließen sollen, dass er als Ergebnis nach den Zeilen gesucht hat. Ich glaube, ich habe auch falsch verstanden, was er mit RIESIG meinte. Ich dachte ursprünglich, er meinte es rechenintensiv, aber jetzt denke ich, er meinte es breit mit Spalten, so dass Arron und DB2 es richtig verstanden haben, sowohl beim Lesen als auch bei den Lösungen (je nachdem, was müder ist: Ihr Gehirn oder Ihre Finger)
jwolf
2

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.

Michael Green
quelle