web-dev-qa-db-ja.com

列がNULLかどうかをテストします

大規模なテーブルのANY列に少なくとも1つの空白(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

これは巨大なクエリになります。

16
Dexter

@ db2の回答への拡張で、より少ない(read: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;
16
Aaron Bertrand

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答え)

8
Martin Smith

Nice組み込み構文はありませんが、Management Studioには、クエリをすばやく生成するための便利な機能がいくつかあります。

オブジェクトエクスプローラーで、目的のテーブルにドリルダウンして展開し、「Columns」フォルダー全体を空のクエリエディターにドラッグします。これにより、クエリに列のコンマ区切りのリストが追加されます。

次に、検索と置換を開きます。 [検索対象]を,に設定し、[置換]をIS NULL OR(先頭にスペースを含む)に設定して、[すべて置換]をクリックします。シーケンスの最後のものは手動でクリーンアップする必要があります。

それはまだ醜いですが、醜い労働集約的ではありません。

5
db2

複数のソリューション:一部の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はありません。

2番目、非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 =少なくとも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 =少なくとも1つの非NULLがあります

これがお役に立てば幸いです。

4
jwolf

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以外の値をカウントできます。ソーステーブルの列の総数と比較すると、1つ以上の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注入されたダミー列
-IDに1、アンピボットされていません

この値は、カタログテーブルを調べることにより、実行時に取得できます。

結果に結合することにより、元の行を取得できます。

NULL以外の値を調べる場合は、where句に含めることができます。

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

討論

これには、UNPIVOTを通じて伝達される識別子が必要です。キーが一番いいでしょう。存在しない場合は ROW_NUMBER() ウィンドウ関数で注入できますが、これは実行にコストがかかる可能性があります。

すべての列は、UNPIVOT句内に明示的にリストされている必要があります。 @ db2で提案されているように、SSMSを使用してドラッグできます。 Aaron Bertrandの提案がそうであるように、テーブル定義が変化するとき、それは動的ではありません。ただし、これはほとんどすべてのSQLに当てはまります。

私のやや限定されたデータセットの場合、実行計画はクラスター化インデックススキャンとストリーム集約です。これは、テーブルを直接スキャンするよりもメモリの負荷が高くなり、多くのOR句が含まれます。

2
Michael Green