いずれかの列がNULLかどうかをテストします


16

私は、大きなテーブルの任意の列に少なくとも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

@ 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にはクエリを迅速に生成するための便利な機能がいくつかあります。

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

次に、検索と置換を開きます。「Find What」を,「Replace With」にIS NULL OR(先頭にスペースを付けて)設定し、Replace Allを押します。シーケンスの最後の1つを手動でクリーンアップする必要があります。

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


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 

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 =少なくとも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があります

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


これは非常に便利に思えますが、OPが求めていたものではないことに注意する義務があります。存在するかどうかを確認するだけでなく、NULL値を含む各行の内容が必要でした。
RDFozz

けっこうだ。私はただそれを異なって読んでいたと思います。私は「...大きなテーブルに...があるかどうかをテストする」という部分に焦点を当てていたので、...ブール値(私の場合はブール値に似ています)。しかし、「エントリのリスト」で、彼が行を意味する場合、あなたは絶対に正しいです。
-jwolf

これを再訪してください。私は間違いなく質問を誤って解釈しました-彼は結果として行を探していたと推測すべきでした。私は彼が巨大な意味を誤解したと思います。私はもともと、彼は計算上高価意味と思ったが、今私はアーロンとDB2は読書で、右の両方にそれを得たので、彼は列に広い意味を考えると解決策は、(これに応じて、より疲れている:あなたの脳や指)
jwolf

2

UNPIVOTは、列を行に変換します。プロセスでは、NULL値(reference)が削除されます。

与えられた入力

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で、UNPIVOTEDではありません

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

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

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

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

討論

これには、UNPIVOTを通じて運ばれる識別子が必要です。キーが最適です。存在しない場合は、ROW_NUMBER()ウィンドウ関数によって挿入できますが、これは実行に費用がかかる場合があります。

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

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.