別のテーブルで一致するエントリがない行を選択するにはどうすればよいですか?


323

データベースアプリケーションのメンテナンス作業を行っていますが、1つのテーブルの値が外部キーのスタイルで使用されているにもかかわらず、テーブルに外部キーの制約がないことがわかりました。

これらの列にFK制約を追加しようとしていますが、単純に修正されている以前のエラーからのテーブル内の不良データがすでに大量にあるため、それが見つからない行を見つける必要があります。他のテーブルと一致させてから削除します。

Webでこの種のクエリの例をいくつか見つけましたが、それらはすべて、説明ではなく例を提供しているようであり、なぜ機能するのかわかりません。

誰かが私はむしろ、この内のすべてのテーブルのSOに動作してくるよりも、これらのクエリを自分で作ることができるように、それはやっている別のテーブルにいない試合で、すべての行を返すクエリ、および何を構築する方法を私に説明することができます混乱がありますFK制約はありませんか?

回答:


614

簡単なクエリは次のとおりです。

SELECT t1.ID
FROM Table1 t1
    LEFT JOIN Table2 t2 ON t1.ID = t2.ID
WHERE t2.ID IS NULL

重要な点は次のとおりです。

  1. LEFT JOIN使用されている; これTable1は、に一致する行があるかどうかに関係なく、からすべての行を返しTable2ます。

  2. WHERE t2.ID IS NULL句。これは、IDから返された行のみに返される結果を制限しますTable2です、ヌルを-他の言葉でありNOの内のレコードTable2からその特定のIDのためにTable1Table2.IDTable1IDが一致しないすべてのレコードに対してNULLとして返されますTable2


4
IDがNULLの場合は失敗します
Michael

169
@Michael- NULLスキーマでIDが有効である場合、より大きな問題が発生する可能性があります。同意しませんか?:)
rinogo 2013年

1
table1にtable2より多くのレコードがある場合でも、これは機能しますか?table1に100のレコードがあり、table2に200のレコードがある(一致/結合する100と一致しない/結合する100)場合、200レコードすべてが返されますか?
ファンベレス2016

1
WHERE句とLEFT JOINの間に相互作用がないことを確認するために、左結合をサブクエリ/インラインビューとしてラップすることがよくあります。
Andrew Wolfe

1
@Jas回答の重要なポイント1、最初のテーブルのすべての行。左結合のt1.ID = t2.ID条件に一致しないものも含みます。最初の行をに変更してSELECT t1.ID, t2.IDWHERE行を削除すると、これがどのように機能するかをよりよく理解できます。
PeterLaboš19年

97

EXISTS式の方が強力なので、式を使用します。つまりLEFT JOIN、結合されたテーブルにあるものすべてを取得する必要がある場合に、結合する行をより正確に選択できます。その効率は、おそらくLEFT JOINnullテストの場合と同じです。

SELECT t1.ID
FROM Table1 t1
WHERE NOT EXISTS (SELECT t2.ID FROM Table2 t2 WHERE t1.ID = t2.ID)

この単純なものは、クエリオプティマイザーによって簡単に処理され、最適に実行されます。
Andrew Wolfe

2
はい、主な利点EXISTSはその変動性です。
Ondrej Bozek 2017

1
シンプルでエレガント、それが私の問題を解決しました!良いですね!
MikeMighty 2018年

2
実際に1つのクエリの速度を7秒から200ミリ秒に減らしました...(と比較してWHERE t2.id IS NULL)ありがとうございます。
Moti Korets

4
@MotiKoretsつまり、速度が向上しました:)
Ondrej

14
SELECT id FROM table1 WHERE foreign_key_id_column NOT IN (SELECT id FROM table2)

表1には、外部キー制約を追加したい列がありますが、foreign_key_id_columnすべての値idが表2のと一致していません。

  1. 最初の選択では、idテーブル1のがリストされます。これらは、削除する行です。
  2. NOT INwhereステートメントの句は、クエリをforeign_key_id_column、テーブル2 idのリストにない値の行のみに制限します。
  3. SELECT括弧内のステートメントidは、表2にあるすべてののリストを取得します。

@ zb226:へのリンクはIN、リテラル値のリストを含む句の制限に関係しています。INサブクエリの結果を含む句の使用には適用されません。その質問に対するその受け入れられた回答は、実際にはサブクエリを使用して問題を解決します。(リテラル値の大きなリストは巨大なSQL式を作成するため問題があります。結果のリストが大きくてもSQL式自体が小さいため、
サブクエリは正常に機能し

@KannanGoundanあなたは絶対的に正しいです。欠陥のあるコメントを取り下げる。
zb226

8

T2制約を追加するテーブルはどこですか?

SELECT *
FROM T2
WHERE constrained_field NOT
IN (
    SELECT DISTINCT t.constrained_field
    FROM T2 
    INNER JOIN T1 t
    USING ( constrained_field )
)

結果を削除します。


4

次の2つのテーブル(給与と従業員)があるとします。 ここに画像の説明を入力してください

ここで、給与に含まれていない従業員テーブルのレコードが必要です。 これは3つの方法で実行できます。

  1. 内部結合の使用
select * from employee
where id not in(select e.id from employee e inner join salary s on e.id=s.id)

ここに画像の説明を入力してください

  1. 左外部結合の使用
select * from employee e 
left outer join salary s on e.id=s.id  where s.id is null

ここに画像の説明を入力してください

  1. 完全結合の使用
select * from employee e
full outer join salary s on e.id=s.id where e.id not in(select id from salary)

ここに画像の説明を入力してください


2

ここで同様の質問からMySQLの内部結合クエリは、他のテーブル存在しないレコードを取得するためにこれを機能させました

SELECT * FROM bigtable 
LEFT JOIN smalltable ON bigtable.id = smalltable.id 
WHERE smalltable.id IS NULL

smalltable不足しているレコードbigtableがある場所、すべてのレコードがある場所です。クエリリストに存在しないすべてのレコードsmalltableが、上に存在しますbigtableid他の一致基準で置き換えることができます。


0

以下に示すように、ビューを選択できます。

CREATE VIEW AuthorizedUserProjectView AS select t1.username as username, t1.email as useremail, p.id as projectid, 
(select m.role from userproject m where m.projectid = p.id and m.userid = t1.id) as role 
FROM authorizeduser as t1, project as p

次に、ビューの選択または更新を行います。

select * from AuthorizedUserProjectView where projectid = 49

これにより、下の図に示すような結果が得られます。つまり、一致しない列にはnullが入力されています。

[Result of select on the view][1]

0

(@AdaTheDevと比較して)どちらが最適化されているかはわかりませんが、これは私が使用する場合に最も速くなるようです(少なくとも私にとって)

SELECT id  FROM  table_1 EXCEPT SELECT DISTINCT (table1_id) table1_id FROM table_2

あなたが使用できる他の特定の属性を取得したい場合:

SELECT COUNT(*) FROM table_1 where id in (SELECT id  FROM  table_1 EXCEPT SELECT DISTINCT (table1_id) table1_id FROM table_2);


-2

あなたはこのようなことをすることができます

   SELECT IFNULL(`price`.`fPrice`,100) as fPrice,product.ProductId,ProductName 
          FROM `products` left join `price` ON 
          price.ProductId=product.ProductId AND (GeoFancingId=1 OR GeoFancingId 
          IS NULL) WHERE Status="Active" AND Delete="No"

-6

両方のテーブルで一致するエントリがない行を選択するにはどうすればよいですか?

    [dbo]から*を選択します。[EmppDetails] e
     e.Gid = d.Gidの[Employee]。[Gender] dに右参加
    ここでe.GidはNullです

    連合 
    [dbo]から*を選択します。[EmppDetails] e
     左結合[Employee]。[Gender] d on e.Gid = d.Gid
    ここで、d.GidはNullです

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