web-dev-qa-db-fra.com

Tester si des colonnes sont NULL

J'essaie de comprendre une requête simple que je peux faire pour tester si une grande table a une liste d'entrées qui a au moins UNE valeur vide (NULL/vide) dans N'IMPORTE QUELLE colonne.

J'ai besoin de quelque chose comme

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

Je ne veux pas avoir à faire

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

Ce serait une ÉNORME requête.

16
Dexter

Une extension de la réponse de @ db2 avec moins (lire: zéro) de disputes à la main:

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;
16
Aaron Bertrand

Vous devez lister toutes les colonnes selon le commentaire de JNK.

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

Une approche un peu moins efficace qui évite cela est cependant ci-dessous.

;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 

(Sur la base de cette SO)

8
Martin Smith

Il n'y a pas de syntaxe intégrée Nice, mais Management Studio dispose de quelques fonctionnalités pratiques pour générer rapidement la requête.

Dans l'Explorateur d'objets, accédez à la table souhaitée, développez-la, puis faites glisser le dossier "Colonnes" dans un éditeur de requête vide. Cela ajoutera une liste de colonnes séparées par des virgules à la requête.

Ensuite, ouvrez Rechercher et remplacer. Réglez "Find What" sur , et définissez "Remplacer par" sur IS NULL OR (avec un espace au début) puis appuyez sur Remplacer tout. Vous devrez nettoyer le dernier de la séquence à la main.

C'est toujours moche, mais c'est moins moche.

5
db2

Solutions multiples pour: certaines valeurs nulles, toutes les valeurs nulles, colonnes uniques et multiples, plus une utilisation rapide en utilisant le Top 1

Si vous devez tester plusieurs colonnes, vous pouvez utiliser les éléments suivants:

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

First, testez les valeurs NULL et comptez-les:

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 

Donne un nombre de NULLs:

Column_1  Column_2  Column_3
0         1         3

Lorsque le résultat est 0, il n'y a pas de NULL.

Second, comptons les non-NULLs:

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

... Mais parce que nous comptons ici les valeurs non NULL, cela peut être simplifié pour:

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

Soit on donne:

Column_1  Column_2  Column_3
3         2         0

Lorsque le résultat est 0, la colonne est entièrement composée de valeurs NULL.

Enfin, si vous avez seulement besoin de vérifier une colonne spécifique, alors TOP 1 est plus rapide car il devrait s'arrêter au premier hit. Vous pouvez ensuite éventuellement utiliser count (*) pour donner un résultat de style booléen:

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 = Il n'y a pas de NULL, 1 = Il y a au moins un NULL

ou

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 = Ils sont tous NULL, 1 = Il y a au moins un non NULL

J'espère que ça aide.

4
jwolf

UNPIVOT traduit les colonnes en lignes. Dans le processus, il élimine les valeurs NULL ( référence ).

Compte tenu de l'entrée

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 requête 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;

produira la sortie

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

Malheureusement, la ligne 4 a été entièrement éliminée car elle ne contient que des valeurs NULL! Il peut être facilement réintroduit en injectant une valeur fictive dans la requête source:

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;

En agrégeant les lignes sur l'ID, nous pouvons compter les valeurs non nulles. Une comparaison avec le nombre total de colonnes dans la table source identifiera les lignes contenant un ou plusieurs 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;

Je calcule 3 comme
nombre de colonnes dans la table source #t
+ 1 pour la colonne factice injectée
- 1 pour ID, qui n'est pas UNPIVOTED

Cette valeur peut être obtenue au moment de l'exécution en examinant les tables du catalogue.

Les lignes d'origine peuvent être récupérées en se joignant aux résultats.

Si des valeurs autres que NULL doivent être recherchées, elles peuvent être incluses dans une clause where:

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

Discussion

Cela nécessite un identifiant qui est acheminé via l'UNPIVOT. Une clé serait la meilleure. Si aucun n'existe, un peut être injecté par la fonction de fenêtre ROW_NUMBER () , bien que cela puisse être coûteux à exécuter.

Toutes les colonnes doivent être explicitement répertoriées dans la clause UNPIVOT. Ils peuvent être glissés dans SSMS, comme l'a suggéré @ db2. Il ne sera pas dynamique lorsque la définition de la table chagnera, comme le serait la suggestion d'Aaron Bertrand. C'est cependant le cas pour presque tous les SQL.

Pour mon ensemble de données plutôt limité, le plan d'exécution est une analyse d'index en cluster et un agrégat de flux. Cela coûtera plus de mémoire qu'un simple scan de la table et beaucoup de clauses OR.

2
Michael Green