SQL - bir tablodan diğerinde olmayan kayıtları bul


310

Aşağıdaki iki SQL tablo var (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 |
+----+------+--------------+

Nasıl çağrılar olan insanlar tarafından yapıldığı öğrenebilirim phone_numberdeğil Phone_book? İstenen çıktı:

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

Herhangi bir yardım çok takdir edilecektir.

Yanıtlar:


439

Sorgu optimize edicinizin ne kadar iyi olduğuna ve iki tablonuzun göreceli boyutuna bağlı olarak, farklı verimlilikle bunu yapmanın birkaç farklı yolu vardır:

Bu en kısa ifadedir ve telefon rehberiniz çok kısa ise en hızlı olabilir:

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

alternatif olarak ( Alterlife sayesinde )

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

veya (WOPR sayesinde)

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

(diğerlerinin söylediği gibi, normalde sadece istediğiniz sütunları seçmek en iyisidir, ' *' değil )


1
IN önlemek, EXISTS kullanın - ipucu soru başlığında
annakata

28
Sol dış birleşim muhtemelen alt sorgunun tekrar tekrar yürütülmesini engellediği için genel durumda en hızlıdır.
WOPR

Seçici olmamak, ancak önerimdeki alt sorgu <code> select 'x' </code> değil <code> select * </code> değil
Alterlife

evet - MySQL kılavuzu bunun bir 'EXISTS' sorgusu için normal olduğunu öne sürüyor
Alnitak

2
@Alnitak: İkinci sorguda SELECT *alt sorguya ihtiyacınız yok . Bunun yerine, örneğin, SELECT 1yeterince iyi olmalıdır.
Alexander Abakumov

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

Sorgu iyileştiricisinin sihrini çalıştırmasına izin vererek alt sorguyu kaldırmalısınız.

Ayrıca, "SELECT *" öğesinden kaçının, çünkü birisi alttaki tabloları veya görünümleri değiştirirse kodunuzu kırabilir (ve verimsizdir).


10
İkinci masada birden fazla geçiş yapmadığı için bu genellikle en etkili yöntemdir ... umarım bazı insanlar komutanları okur.
Nerdfest

3
Ben insanların profili umuyoruz: en iyi bir SQL performans gurusu değilseniz, önceden en hızlı ne olacağını söylemek oldukça zordur (ve kullandığınız DBMS motoruna bağlıdır).
bortzmeyer

2
Big O notasyonu, bu durumda en hızlı olmasını bekleyebileceğiniz şeyi kolayca söyleyecektir. Farklı büyüklükteki emirler.
Jonesopolis

İki tablonuz arasında bir ilişki varsa, Afterlife'ın cevabına ve benim yorumuma bakın 1:N. VEYA DISTINCT
Vlado'nun

25

Aşağıdaki kod, daha büyük veri kümeleriyle ilgili olarak yukarıda verilen cevaplardan biraz daha verimli olacaktır.

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

1
Her zaman olduğu gibi, en iyi performansa sahip olanı seçmek için sorguların hedef veri kümenizdeki performansının profilini çıkarmaya değer. SQL optimisers bugünlerde performans sonuçlarının şaşırtıcı olduğu kadar iyi.
Greg Hewgill

1
Bu yaklaşımın bir avantajı (WOPR tarafından LEFT OUTER JOIN ile karşılaştırıldığında), birden Callfazla eşleşen satır varsa, satır başına birden fazla satır döndürülmesini önlemesidir Phone_book. Yani, 1:Niki tablonuz arasında bir ilişki varsa .
ToolmakerSteve

Bununla başlayacağım - doğrudan niyetini temsil ediyor. Performans yeterince iyi değilse, uygun dizinlerin bulunduğundan emin olun. Ancak o zaman, daha az belirgin LEFT OUTER JOINolanı deneyin, performansının daha iyi olup olmadığına bakın.
ToolmakerSteve

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

Bu, Phone_book tablonuzda eksik olan ek kimlikleri döndürür.


4

bence

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

idSütun calltablo olarak aynı değeri değil idsütun Phone_bookEğer bu değerlere katılamaz, böylece masaya. Benzer bir yaklaşım için WOPR'un cevabına bakınız.
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

Aynı sütun için başka bir tabloda veri yoksa, bu size bir tablodan veri döndürür
Harvinder Sidhu

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

Bu soruya bir cevap sağlamaz. Bir yazardan eleştiri veya açıklama istemek için gönderilerinin altına bir yorum bırakın. - Şu kaynaktan
Dennis Kriechel

@DennisKriechel soruyu daha spesifik olması için sorguyu güncelledi.
JoshYates1980

1

Alternatif olarak,

select id from call
minus
select id from phone_number

1
Bunun (MINUS olmasına rağmen) operatörü yeni bir ek olarak soruyu cevapladığından emin değilim. Bu, düşük kaliteli kuyrukta sona erdi - bu cevabı geliştirmek isteyebilirsiniz.
ste-fu
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.