SQL-あるテーブルから別のテーブルに存在しないレコードを検索する


310

次の2つのSQLテーブル(MySQL)があります。

Phone_book
+----+------+--------------+
| id | name | phone_number |
+----+------+--------------+
| 1  | John | 111111111111 |
+----+------+--------------+
| 2  | Jane | 222222222222 |
+----+------+--------------+

Call
+----+------+--------------+
| id | date | phone_number |
+----+------+--------------+
| 1  | 0945 | 111111111111 |
+----+------+--------------+
| 2  | 0950 | 222222222222 |
+----+------+--------------+
| 3  | 1045 | 333333333333 |
+----+------+--------------+

phone_numberいない人からの通話を確認するにはどうすればよいPhone_bookですか?望ましい出力は次のとおりです。

Call
+----+------+--------------+
| id | date | phone_number |
+----+------+--------------+
| 3  | 1045 | 333333333333 |
+----+------+--------------+

どんな助けでも大歓迎です。

回答:


439

クエリオプティマイザーがどの程度優れているか、および2つのテーブルの相対的なサイズに応じて、さまざまな効率でこれを行ういくつかの異なる方法があります。

これは最も短いステートメントであり、電話帳が非常に短い場合は最も速くなる可能性があります。

SELECT  *
FROM    Call
WHERE   phone_number NOT IN (SELECT phone_number FROM Phone_book)

あるいは(Alterlifeに感謝)

SELECT *
FROM   Call
WHERE  NOT EXISTS
  (SELECT *
   FROM   Phone_book
   WHERE  Phone_book.phone_number = Call.phone_number)

または(WOPRに感謝)

SELECT * 
FROM   Call
LEFT OUTER JOIN Phone_Book
  ON (Call.phone_number = Phone_book.phone_number)
  WHERE Phone_book.phone_number IS NULL

(他の人が言ったように、それを無視して、通常は ' *' ではなく、必要な列だけを選択するのが最善です)


1
INを避け、EXISTSを使用してください-ヒントは質問のタイトルにあります
annakata 2008

28
左外部結合は、サブクエリの繰り返し実行を防ぐため、一般的なケースではおそらく最も高速です。
WOPR

うるさくはありませんが、私の提案のサブクエリは<code> select * </ code>ではなく<code> select 'x' </ code>を返します
Alterlife

はい-MySQLマニュアルでは、これが「EXISTS」クエリの正常な状態であることを示しています
Alnitak

2
@Alnitak:2番目のクエリSELECT *では、サブクエリでは必要ありません。代わりに、たとえば、SELECT 1で十分です。
Alexander

90
SELECT Call.ID, Call.date, Call.phone_number 
FROM Call 
LEFT OUTER JOIN Phone_Book 
  ON (Call.phone_number=Phone_book.phone_number) 
  WHERE Phone_book.phone_number IS NULL

サブクエリを削除して、クエリオプティマイザーがその魔法を働かせるようにする必要があります。

また、 "SELECT *"は避けてください。基になるテーブルまたはビューを誰かが変更するとコードが壊れる可能性があるためです(非効率的です)。


10
2番目のテーブルで複数のパスを実行しないため、これは一般に最も効率的な方法です。
Nerdfest 2008

3
私はむしろ人々がプロファイルを作成することを望みます:あなたが最高のSQLパフォーマンスの達人でない限り、何が最も速いかを事前に伝えることは非常に困難です(そして使用するDBMSエンジンに依存します)。
bortzmeyer 2008

2
Big O表記は、この場合に最速であると予想できるものを簡単に教えてくれます。それは桁違いです。
ジョネソポリス

2つのテーブルの間に関係がある場合は、Afterlifeの回答と私のコメントを参照してください1:N。またはVladoの回答にDISTINCT示されているように追加
ToolmakerSteve

25

以下のコードは、より大きなデータセットを処理するときに、上記の回答よりも少し効率的です。

SELECT * FROM Call WHERE 
NOT EXISTS (SELECT 'x' FROM Phone_book where 
Phone_book.phone_number = Call.phone_number)

1
いつものように、ターゲットデータセットに対するクエリのパフォーマンスをプロファイリングして、最高のパフォーマンスを持つデータセットを選択することは価値があります。SQLオプティマイザは最近、パフォーマンスの結果が驚くべきものになることがよくあるほど十分に優れています。
グレッグヒューギル

1
このアプローチの利点(WOPRによるLEFT OUTER JOINとの比較)は、にCall一致する行が複数ある場合に、の行ごとに複数の行が返されないようにすることPhone_bookです。つまり1:N、2つのテーブルの間に関係がある場合です。
ToolmakerSteve

私はこれから始めます-それは意図を直接表します。パフォーマンスが十分でない場合は、適切なインデックスが存在することを確認してください。それから、あまり目立たないを試してLEFT OUTER JOIN、パフォーマンスが向上するかどうかを確認してください。
ToolmakerSteve

6
SELECT DISTINCT Call.id 
FROM Call 
LEFT OUTER JOIN Phone_book USING (id) 
WHERE Phone_book.id IS NULL

これにより、Phone_bookテーブルにない余分なIDが返されます。


4

おもう

SELECT CALL.* FROM CALL LEFT JOIN Phone_book ON 
CALL.id = Phone_book.id WHERE Phone_book.name IS NULL

idcallのテーブルには、同じ値ではないidの列Phone_bookのテーブル、あなたはこれらの値に参加することはできません。同様のアプローチについては、WOPRの回答を参照してください。
Michael Fredrickson、

3
SELECT t1.ColumnID,
CASE 
    WHEN NOT EXISTS( SELECT t2.FieldText  
                     FROM Table t2 
                     WHERE t2.ColumnID = t1.ColumnID) 
    THEN t1.FieldText
    ELSE t2.FieldText
END FieldText       
FROM Table1 t1, Table2 t2

同じ列の別のテーブルにデータが存在しない場合、1つのテーブルからデータが返されます
Harvinder Sidhu 2013

1
SELECT name, phone_number FROM Call a
WHERE a.phone_number NOT IN (SELECT b.phone_number FROM Phone_book b)

これは質問に対する答えを提供しません。批評したり、著者に説明を求めるには、投稿の下にコメントを残してください。- 口コミより
デニスKriechel

@DennisKriechelがクエリを更新して、質問に対してより具体的になるようにしました。
JoshYates1980

1

または、

select id from call
minus
select id from phone_number

1
(MINUSですが)そのままで質問に答えられるかどうかはわかりませんが、演算子は新しい追加です。これは最終的に低品質のキューに入れられました-この答えを強化したいかもしれません。
ste-fu 2017
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.