测试是否有任何列为NULL


16

我试图找出一个简单的查询,我可以测试一个大表是否具有在ANY列中至少具有一个空白(NULL /空)值的条目列表。

我需要类似的东西

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

这将是一个巨大的查询。

Answers:


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 

(基于此答案)


5

没有好的内置语法,但是Management Studio具有几个方便的功能来快速生成查询。

在对象资源管理器中,深入到所需的表,将其展开,然后将整个“列”文件夹拖到空白的查询编辑器中。这会将以逗号分隔的列列表添加到查询中。

接下来,打开“查找并替换”。将“查找内容” ,设置为并将“替换为”设置为IS NULL OR(以空格开头),然后单击“全部替换”。您必须手动清理序列中的最后一个。

它仍然很丑陋,但劳动强度却不那么丑陋。


4

多种解决方案:一些null,所有null,单列和多列,以及使用前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 

产生NULL计数:

Column_1  Column_2  Column_3
0         1         3

如果结果为0,则没有NULL。

其次,让我们计算非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

...但是由于我们在这里计算非NULL,因此可以简化为:

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 =至少有一个非NULL

我希望这有帮助。


尽管这似乎很有用,但我确实有义务指出,这不是OP所要的-他们希望每行的内容都包含NULL值,而不仅仅是检查是否存在任何值。
RDFozz

很公平。我想我只是在以不同的方式阅读。我专注于“ ...测试大表是否具有...”部分,因此...布尔值(在我的情况下为布尔值-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值。与源表中的列总数进行比较,将确定包含一个或多个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用于ID,这不是UNPIVOTED

该值可以在运行时通过检查目录表获得。

可以通过加入结果来检索原始行。

如果要调查非NULL值,则可以将它们包含在where子句中:

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

讨论区

这需要通过UNPIVOT携带的标识符。一把钥匙是最好的。如果不存在,则可以通过ROW_NUMBER()窗口函数注入一个,尽管执行起来可能会很昂贵。

所有列必须在UNPIVOT子句中明确列出。可以使用SSMS将它们拖入,如@ db2所示。当表定义改变时,它不会是动态的,就像Aaron Bertrand的建议那样。但是,几乎所有SQL都是这种情况。

对于我的有限数据集,执行计划是聚簇索引扫描和流聚合。与直接扫描表和许多OR子句相比,这将花费更多的内存。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.