Проверьте, являются ли какие-либо столбцы NULL

16

Я пытаюсь выяснить простой запрос, который я могу сделать, чтобы проверить, есть ли в большой таблице список записей, который имеет ЛЮБОЕ пустое (NULL / empty) значение в ЛЮБОМ столбце.

Мне нужно что-то вроде

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

Я не хочу делать

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

Это был бы ОГРОМНЫЙ запрос.

правый
источник

Ответы:

16

Расширение ответа @ db2 с меньшим (читай: ноль) ручным спором:

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;
Аарон Бертран
источник
8

Вы должны перечислить все столбцы согласно комментарию JNK.

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

Несколько менее эффективный подход, который избегает этого, хотя ниже.

;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 

(На основании этого SO ответа)

Мартин Смит
источник
5

Нет хорошего встроенного синтаксиса, но Management Studio имеет несколько удобных функций для быстрой генерации запроса.

В обозревателе объектов перейдите к нужной таблице, разверните ее и перетащите всю папку «Столбцы» в пустой редактор запросов. Это добавит разделенный запятыми список столбцов к запросу.

Затем откройте «Найти и заменить». Установите «Найти что» ,и установите «Заменить на» на IS NULL OR(с начальным пробелом), затем нажмите «Заменить все». Вы должны будете убрать последний в последовательности вручную.

Это все еще некрасиво, но менее утомительно.

db2
источник
4

Несколько решений для: некоторые нули, все нули, один и несколько столбцов, а также сделать его быстро, используя топ 1

Если вам нужно протестировать несколько столбцов, вы можете использовать следующее:

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

Сначала проверьте NULL и посчитайте их:

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 

Возвращает количество пустых значений:

Column_1  Column_2  Column_3
0         1         3

Если результат равен 0, значения NULL отсутствуют.

Во-вторых , давайте посчитаем ненулевые значения:

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

... Но поскольку здесь мы считаем ненулевые значения, это можно упростить до:

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

Либо один дает:

Column_1  Column_2  Column_3
3         2         0

Если результат равен 0, столбец полностью состоит из NULL.

Наконец , если вам нужно только проверить определенный столбец, то TOP 1 быстрее, потому что он должен остановиться при первом попадании. Затем вы можете при желании использовать count (*) для получения результата в логическом стиле:

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 = нет NULL, 1 = есть хотя бы один NULL

или

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 = все они равны NULL, 1 = есть хотя бы один ненулевой

Надеюсь, это поможет.

jwolf
источник
Хотя это кажется весьма полезным, я чувствую обязанность отметить, что это не то, о чем просил OP - они хотели, чтобы содержимое каждой строки содержало значение NULL, а не просто проверяло, существует ли оно.
RDFozz
Справедливо. Я думаю, что я просто читал это по-другому. Я сосредоточился на «... проверять, есть ли в большой таблице ...» часть, так что ... Boolean (в моем случае Boolean-ish). Но если под «списком записей» он подразумевал строки, то вы абсолютно правы.
jwolf
Просто пересмотрел это. Я определенно неверно истолковал вопрос - следовало предположить, что в результате он искал строки. Я думаю, что я также неправильно понял, что он имел в виду под ОГРОМНЫМ. Первоначально я думал, что он имел в виду вычислительные затраты, но теперь я просто думаю, что он имел в виду широкие столбцы, поэтому Arron и DB2 поняли это правильно, как в чтении, так и в решениях (в зависимости от того, что более утомительно: ваш мозг или ваши пальцы)
jwolf
2

UNPIVOT переводит столбцы в строки. В процессе исключаются значения NULL ( ссылка ).

Учитывая вход

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

запрос 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;

будет производить вывод

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

К сожалению, строка 4 полностью исключена, поскольку в ней только NULL! Это может быть удобно повторно введено путем введения фиктивного значения в исходный запрос:

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;

Агрегируя строки по ID, мы можем посчитать ненулевые значения. Сравнение с общим числом столбцов в исходной таблице идентифицирует строки, содержащие один или несколько 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;

Я вычисляю 3 как
количество столбцов в исходной таблице #t
+ 1 для вставленного фиктивного столбца
- 1 для идентификатора, который не является НЕУЧАСТНЫМ

Это значение может быть получено во время выполнения путем изучения таблиц каталога.

Исходные строки можно получить, присоединившись к результатам.

Если значения, отличные от NULL, должны быть исследованы, они могут быть включены в предложение where:

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

обсуждение

Для этого требуется идентификатор, который передается через UNPIVOT. Ключ будет лучшим. Если ничего не существует, его можно ввести с помощью ROW_NUMBER () оконной функции , хотя это может быть дорогостоящим для выполнения.

Все столбцы должны быть явно перечислены в предложении UNPIVOT. Их можно перетащить с помощью SSMS, как предложено @ db2. Это не будет динамичным, когда определение таблицы искажается, как было бы предложено Аароном Бертраном. Однако это касается почти всех SQL.

Для моего довольно ограниченного набора данных план выполнения - это сканирование кластерного индекса и агрегат потока. Это будет дороже памяти, чем прямое сканирование таблицы и множество предложений OR.

Майкл Грин
источник