Teste se alguma coluna é NULL

16

Estou tentando descobrir uma consulta fácil que posso fazer para testar se uma tabela grande possui uma lista de entradas que possui pelo menos UM valor em branco (NULL / vazio) em QUALQUER coluna.

Eu preciso de algo como

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

Eu não quero ter que fazer

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

Esta seria uma enorme consulta.

Dexter
fonte

Respostas:

16

Uma extensão da resposta do @ db2 com menos disputas manuais (leia-se: zero):

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

Você deve listar todas as colunas conforme o comentário de JNK.

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

Uma abordagem um pouco menos eficiente que evita isso está abaixo.

;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 

(Com base nesta resposta SO)

Martin Smith
fonte
5

Não há uma sintaxe interna agradável, mas o Management Studio possui alguns recursos convenientes para gerar a consulta rapidamente.

No Pesquisador de Objetos, vá até a tabela desejada, expanda-a e arraste a pasta "Colunas" inteira para um editor de consultas em branco. Isso adicionará uma lista de colunas separadas por vírgula à consulta.

Em seguida, abra Localizar e substituir. Defina "Find What" como ,e "Replace With" para IS NULL OR(com um espaço à esquerda) e pressione Replace All. Você terá que limpar o último na sequência manualmente.

Ainda é feio, mas é feio menos trabalhoso.

db2
fonte
4

Várias soluções para: alguns nulos, todos nulos, colunas únicas e múltiplas, além de torná-lo RÁPIDO usando o Top 1

Se você precisar testar várias colunas, poderá usar o seguinte:

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

Primeiro , teste NULLs e conte-os:

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 

Rende uma contagem de NULLs:

Column_1  Column_2  Column_3
0         1         3

Onde o resultado é 0, não há NULLs.

Segundo , vamos contar os não-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

... Mas, como contamos não NULLs aqui, isso pode ser simplificado para:

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

Qualquer um produz:

Column_1  Column_2  Column_3
3         2         0

Onde o resultado é 0, a coluna é totalmente composta de NULLs.

Por fim , se você só precisa verificar uma coluna específica, o TOP 1 é mais rápido, pois deve parar na primeira ocorrência. Você pode, opcionalmente, usar count (*) para obter um resultado no 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 = Não há NULLs, 1 = Há pelo menos um 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 = Eles são todos NULL, 1 = Há pelo menos um não NULL

Eu espero que isso ajude.

jwolf
fonte
Embora isso pareça bastante útil, sinto uma obrigação de observar que não é o que o OP estava pedindo - eles queriam o conteúdo de cada linha que incluísse um valor NULL, não apenas uma verificação para ver se havia algum.
RDFozz
Justo. Eu acho que estava lendo de forma diferente. Eu estava focado na parte "... testar se uma tabela grande tem ...", então ... Boolean (no meu caso Boolean-ish). Mas se, por "lista de entradas", ele quis dizer linhas, então você está absolutamente certo.
jwolf
Apenas revisitei isso. Eu definitivamente interpretei mal a pergunta - deveria ter inferido que ele estava procurando as linhas como resultado. Eu acho que também interpretou mal o que ele quis dizer com ENORME. Originalmente, pensei que ele quisesse dizer computacionalmente caro, mas agora acho que ele quis dizer amplo com colunas, para que Arron e DB2 acertassem, tanto na leitura quanto nas soluções (dependendo do que está mais cansado: seu cérebro ou seus dedos)
jwolf
2

UNPIVOT converte colunas em linhas. No processo, elimina valores NULL ( referência ).

Dada a 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);

a 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;

produzirá a saída

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

Infelizmente a linha 4 foi totalmente eliminada, pois possui apenas NULLs! Ele pode ser convenientemente reintroduzido injetando um valor fictício na consulta de origem:

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;

Ao agregar as linhas no ID, podemos contar os valores não nulos. Uma comparação com o número total de colunas na tabela de origem identificará as linhas que contêm um ou mais 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 colunas na tabela de origem #t
+ 1 para a coluna fictícia injetada
- 1 para ID, que não é PERMANENTE

Este valor pode ser obtido em tempo de execução examinando as tabelas de catálogos.

As linhas originais podem ser recuperadas juntando-se aos resultados.

Se valores diferentes de NULL devem ser investigados, eles podem ser incluídos na cláusula where:

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

Discussão

Isso requer um identificador que é realizado através do UNPIVOT. Uma chave seria melhor. Se não existir, pode ser injetado pelo ROW_NUMBER () função da janela , embora isso possa ser caro de executar.

Todas as colunas devem ser listadas explicitamente dentro da cláusula UNPIVOT. Eles podem ser arrastados usando o SSMS, como @ db2 sugerido. Não será dinâmico quando a definição da tabela mudar, como seria a sugestão de Aaron Bertrand. Este é o caso de quase todo o SQL, no entanto.

Para meu conjunto de dados bastante limitado, o plano de execução é uma verificação de índice em cluster e um agregado de fluxo. Isso custará mais memória do que uma verificação direta da tabela e muitas cláusulas OR.

Michael Green
fonte