SQL「サブクエリにない場所を選択」は結果を返さない


130

免責事項:私は問題を理解しました(私はそう思います)が、(簡単に)どこにもそれを見つけることができなかったので、この問題をスタックオーバーフローに追加したいと思いました。また、誰かが私よりも良い答えを持っているかもしれません。

1つのテーブル「Common」が他のいくつかのテーブルによって参照されているデータベースがあります。Commonテーブルのどのレコードが孤立している(つまり、他のどのテーブルからの参照もない)かを確認したかったのです。

私はこのクエリを実行しました:

select *
from Common
where common_id not in (select common_id from Table1)
and common_id not in (select common_id from Table2)

孤立したレコードがあることは知っていますが、返されたレコードはありません。何故なの?

(重要な場合、これはSQL Serverです。)


このstackoverflow.com/a/129152/1667619は、WHYの質問にかなりよく答えます。
Ruchan 2017

回答:


234

更新:

私のブログのこれらの記事では、方法の違いについて詳しく説明しています。


このようなクエリを実行するには、3つの方法があります。

  • LEFT JOIN / IS NULL

    SELECT  *
    FROM    common
    LEFT JOIN
            table1 t1
    ON      t1.common_id = common.common_id
    WHERE   t1.common_id IS NULL
    
  • NOT EXISTS

    SELECT  *
    FROM    common
    WHERE   NOT EXISTS
            (
            SELECT  NULL
            FROM    table1 t1
            WHERE   t1.common_id = common.common_id
            )
    
  • NOT IN

    SELECT  *
    FROM    common
    WHERE   common_id NOT IN
            (
            SELECT  common_id
            FROM    table1 t1
            )
    

table1.common_idがnull可能ではない場合、これらのクエリはすべて意味的に同じです。

null可能である場合NOT INは異なります。なぜなら、値がを含むリスト内のいずれにも一致しない場合にIN(したがって、NOT IN)が返さNULLれるためですNULL

これは紛らわしいかもしれませんが、これの代替構文を思い出せばもっと明白になるかもしれません:

common_id = ANY
(
SELECT  common_id
FROM    table1 t1
)

この条件の結果は、リスト内のすべての比較のブール積です。もちろん、単一のNULL値はNULL結果全体をレンダリングする結果NULLも生成します。

common_id少なくとも1つの値がであるため、これがこのリストのどれとも等しくないことを明確に述べることはできませんNULL

次のデータがあるとします。

common

--
1
3

table1

--
NULL
1
2

LEFT JOIN / IS NULLそして、NOT EXISTSを返します3何もNOT IN返しません(常にFALSEまたはに評価されるためNULL)。

ではMySQL、nullを許容しない列の場合、LEFT JOIN / IS NULLおよびNOT INよりも少し(数パーセント)効率的ですNOT EXISTS。列がNULL可能でNOT EXISTSある場合、最も効率的です(これもそれほど多くありません)。

ではOracle、3つのクエリすべてが同じプラン(an ANTI JOIN)を生成します。

ではSQL ServerNOT IN/ NOT EXISTSLEFT JOIN / IS NULLオプティマイザANTI JOINによってに最適化できないため、より効率的です。

そしてより効率的であり、それらが最適化された正弦ながら、用途(あるいは普通はサブクエリがハッシュに大きすぎます)PostgreSQLLEFT JOIN / IS NULLNOT EXISTSNOT INAnti JoinNOT INhashed subplansubplan


8
正解です。ありがとう!
StevenMcD 2009

これは
素晴らしくて

1
+1、4年半後、この答えは私を困惑させた問題を解決するのに役立ちました!
Carson63000 2014年

@ Carson63000 Snap!私はこの答えを見る前に怒っていると思っていました
ボビー

1
@IstiaqueAhmed:NOT EXISTS内部のクエリが行を返す場合、TRUEと評価されます。SELECT NULL同様かもしれないSELECT *か、SELECT 1または何か他のもの、NOT EXISTS述語はそれらだけをカウントし、行の値を見ていません。
Quassnoi 2017年

36

世界を2値のブール値の場所にしたい場合は、ヌル(3番目の値)のケースを自分で防ぐ必要があります。

リスト側でnullを許可するIN句を記述しないでください。それらを除外してください!

common_id not in
(
  select common_id from Table1
  where common_id is not null
)

6
in-clause-list内のnullは、クエリ結果が欠落する一般的な理由です。
Amy B

'nullと比較すると、答えは不明です'-@Jeremy Steinの答えから。からcommon_id not in、私たちはまだあるというcommon_id値を持つことができますNULL。結果が得られないという問題はまだ続きませんか?
Istiaque Ahmed 2017

5

Table1またはTable2には、common_idにいくつかのnull値があります。代わりにこのクエリを使用してください:

select *
from Common
where common_id not in (select common_id from Table1 where common_id is not null)
and common_id not in (select common_id from Table2 where common_id is not null)

1
一方のテーブルにデータがあり、もう一方のテーブルにない場合はどうなりますか?「and」または「or」が必要ですか?
フィリップケリー

1
どのテーブルでも参照されていないレコードを探しているので、ANDが必要です。質問を明確にします。
ジェレミースタイン

4
select *
from Common c
where not exists (select t1.commonid from table1 t1 where t1.commonid = c.commonid)
and not exists (select t2.commonid from table2 t2 where t2.commonid = c.commonid)

4

頭のてっぺんから...

select c.commonID, t1.commonID, t2.commonID
from Common c
     left outer join Table1 t1 on t1.commonID = c.commonID
     left outer join Table2 t2 on t2.commonID = c.commonID
where t1.commonID is null 
     and t2.commonID is null

私はいくつかのテストを実行し、@ patmortechの回答と@rexemのコメントに関する私の結果がここにありました。

Table1またはTable2のいずれかがcommonID​​でインデックス付けされていない場合、テーブルスキャンが行われますが、@ patmortechのクエリは依然として2倍高速です(100K行マスターテーブルの場合)。

commonID​​でどちらにもインデックスが付けられていない場合、2つのテーブルスキャンが行われ、その差はごくわずかです。

commonID​​で両方にインデックスが付けられている場合、「存在しない」クエリは1/3の時間で実行されます。


1
それはwhere句のANDである必要があります。それ以外の場合は機能します。
ジェレミースタイン

1
コメントごとに変更されました。「または」は、いずれかのテーブルで孤立したユーザーを選択します。
オースティンサローネン

1
それは良いです。ところで、サブクエリではなく外部結合を使用する必要がある理由はありますか?
ジェレミースタイン

3
読みやすさが第一です。より優れた実行プランが生成されると思いますが、クエリプランがないと確認できません。
オースティンサローネン

2
このアプローチは、NOT EXISTSを使用するよりも悪いです。結合の結果、必要以上の行がフェッチされ、列の結果がnullである場合と比較されます。また、NOT EXISTSの方が起動が読みやすくなっています。
OMGポニー

3
SELECT T.common_id
  FROM Common T
       LEFT JOIN Table1 T1 ON T.common_id = T1.common_id
       LEFT JOIN Table2 T2 ON T.common_id = T2.common_id
 WHERE T1.common_id IS NULL
   AND T2.common_id IS NULL

1
このアプローチは、NOT EXISTSを使用するよりも悪いです。結合の結果、必要以上の行がフェッチされ、列の結果がnullである場合と比較されます。これは機能しますが、パフォーマンスはそれほど良くありません-相関サブクエリでINを使用するよりもおそらく悪いでしょう。
OMGポニー

3

common_idの次の値を想定します。

Common - 1
Table1 - 2
Table2 - 3, null

Commonの行は他のどのテーブルにも存在しないため、この行が返されるようにします。ただし、ヌルはモンキーレンチを投げます。

これらの値を使用すると、クエリは次と同等になります。

select *
from Common
where 1 not in (2)
and 1 not in (3, null)

これは次と同等です。

select *
from Common
where not (1=2)
and not (1=3 or 1=null)

これが問題の始まりです。nullと比較すると、答えは不明です。したがって、クエリは次のようになります。

select *
from Common
where not (false)
and not (false or unkown)

falseまたはunknownは不明です:

select *
from Common
where true
and not (unknown)

真であり、不明ではないことも不明です。

select *
from Common
where unknown

where条件は、結果が不明なレコードを返さないため、返されるレコードはありません。

これに対処する1つの方法は、inではなくexists演算子を使用することです。existsは、列ではなく行を操作するため、不明を返しません。(行が存在するか、存在しません。行レベルでのこのnullのあいまいさはありません!)

select *
from Common
where not exists (select common_id from Table1 where common_id = Common.common_id)
and not exists (select common_id from Table2 where common_id = Common.common_id)

2

これは私のために働いた:)

コモンから選択*

どこ

common_id not(select ISNULL(common_id、 'dummy-data') from Table1)

およびcommon_idが(Table2からISNULL(common_id、 'dummy-data')を選択していない)


@marlar、サブクエリは常に値のリストではなく1または0を返します。では、NOT INそこでのパフォーマンスはどうでしょうか?
Istiaque Ahmed 2017


0

私が調べている例があり、1つのテーブルが値をdoubleとして保持し、もう1つは文字列として保持しているため、それらは一致しません(またはキャストなしでは一致しません)。しかし、INにはありませんSELECT ... ... IN働いていました。奇妙ですが、誰かがこの簡単な修正に遭遇した場合に備えて、私は共有すると思いました。


0

上記のトピックを理解するには、以下の例に従ってください。

また、次のリンクにアクセスして、Anti Joinについて知ることもできます。

select department_name,department_id from hr.departments dep
where not exists 
    (select 1 from hr.employees emp
    where emp.department_id=dep.department_id
    )
order by dep.department_name;
DEPARTMENT_NAME DEPARTMENT_ID
Benefits    160
Construction    180
Contracting 190
.......

ただしNOT IN、その場合、データを取得できません。

select Department_name,department_id from hr.departments dep 
where department_id not in (select department_id from hr.employees );

何もデータが見つかりませんでした

これは、(select department_id from hr.employees)がnull値を返し、クエリ全体がfalseと評価されるために発生します。以下のようにSQLを少し変更して、NVL関数でnull値を処理すると、それを確認できます。

select Department_name,department_id from hr.departments dep 
where department_id not in (select NVL(department_id,0) from hr.employees )

データを取得しています:

DEPARTMENT_NAME DEPARTMENT_ID
Treasury    120
Corporate Tax   130
Control And Credit  140
Shareholder Services    150
Benefits    160
....

ここでも、NVL関数でnull値を処理したため、データを取得しています。


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