Bagaimana cara memilih baris tanpa entri yang cocok di tabel lain?


323

Saya melakukan beberapa pekerjaan pemeliharaan pada aplikasi database dan saya telah menemukan bahwa, kegembiraan sukacita, meskipun nilai-nilai dari satu tabel digunakan dalam gaya kunci asing, tidak ada batasan kunci asing di atas tabel.

Saya mencoba menambahkan batasan FK pada kolom ini, tapi saya menemukan itu, karena sudah ada banyak data buruk di tabel dari kesalahan sebelumnya yang telah diperbaiki secara naif, saya perlu menemukan baris yang tidak cocokkan dengan tabel lainnya dan kemudian hapus.

Saya telah menemukan beberapa contoh permintaan semacam ini di web, tetapi mereka semua tampaknya memberikan contoh daripada penjelasan, dan saya tidak mengerti mengapa mereka bekerja.

Dapatkah seseorang menjelaskan kepada saya bagaimana membangun kueri yang mengembalikan semua baris tanpa kecocokan di tabel lain, dan apa yang dilakukannya, sehingga saya bisa membuat kueri ini sendiri, daripada berlari ke SO untuk setiap tabel dalam kekacauan ini yang memiliki tidak ada kendala FK?

Jawaban:


614

Inilah pertanyaan sederhana:

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

Poin kuncinya adalah:

  1. LEFT JOINdigunakan; ini akan mengembalikan SEMUA baris dari Table1, terlepas dari apakah ada baris yang cocok atau tidak Table2.

  2. The WHERE t2.ID IS NULLklausul; ini akan membatasi hasil yang dikembalikan hanya pada baris di mana ID yang dikembalikan dari Table2adalah nol - dengan kata lain TIDAK ada catatan Table2untuk ID tertentu dari Table1. Table2.IDakan dikembalikan sebagai NULL untuk semua catatan dari Table1tempat ID tidak cocok Table2.


4
Gagal jika ID NULL
Michael

169
@Michael - Jika memiliki NULLID valid di skema Anda, Anda mungkin memiliki masalah yang lebih besar, tidakkah Anda setuju? :)
rinogo

1
akankah ini bekerja bahkan jika table1 memiliki lebih banyak record daripada table2? jika table1 memiliki 100 record dan table2 memiliki 200 record (100 cocok / gabung dan 100 tidak cocok / gabung) akankah kita mendapatkan semua 200 catatan dikembalikan?
Juan Velez

1
Saya sering suka membungkus join kiri sebagai tampilan subquery / inline untuk memastikan tidak ada interaksi antara klausa WHERE dan LEFT JOIN.
Andrew Wolfe

1
@Jas Poin kunci 1 dari jawaban, SEMUA baris dari tabel pertama, bahkan yang tidak cocok dengan t1.ID = kondisi t2.ID dari join kiri. Jika Anda mengubah baris pertama ke SELECT t1.ID, t2.IDdan menghapus baris WHERE Anda akan mendapatkan ide yang lebih baik bagaimana ini bekerja.
Peter Laboš

97

Saya akan menggunakan EXISTSekspresi karena lebih kuat, Anda dapat memilih baris yang ingin Anda ikuti, jika LEFT JOINAnda harus mengambil semua yang ada di tabel bergabung. Efisiensinya mungkin sama LEFT JOINdengan uji nol.

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

Sesuatu yang sederhana ini mudah ditangani oleh pengoptimal permintaan untuk eksekusi terbaik.
Andrew Wolfe

2
Ya, keunggulan utama EXISTSadalah variabilitasnya.
Ondrej Bozek

1
Sederhana, elegan, dan itu menyelesaikan masalah saya! Yang bagus!
MikeMighty

2
Sebenarnya mengurangi kecepatan satu permintaan yang saya miliki dari 7 detik menjadi 200 ms ... (dibandingkan dengan WHERE t2.id IS NULL) Terima kasih.
Moti Korets

4
@MotiKorets maksud Anda meningkatkan kecepatan :)
Ondrej Bozek

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

Tabel 1 memiliki kolom yang ingin Anda tambahkan batasan kunci asing, tetapi nilai dalam foreign_key_id_columntidak semua cocok dengan idtabel 2.

  1. Pilih awal daftar ids dari table1. Ini akan menjadi baris yang ingin kita hapus.
  2. The NOT INklausul dalam mana pernyataan membatasi query untuk hanya baris di mana nilai dalam foreign_key_id_columntidak dalam daftar tabel 2 ids.
  3. The SELECTpernyataan dalam kurung akan mendapatkan daftar semua ids yang berada di tabel 2.

@ zb226: Tautan Anda ke berkaitan dengan batasan pada INklausa dengan daftar nilai literal. Itu tidak berlaku untuk menggunakan INklausa dengan hasil sub-permintaan. Jawaban yang diterima untuk pertanyaan itu sebenarnya memecahkan masalah dengan menggunakan sub-kueri. (Daftar besar nilai literal bermasalah karena ia menciptakan ekspresi SQL yang besar. Sub-kueri berfungsi dengan baik karena, bahkan jika daftar yang dihasilkan besar, ekspresi SQL itu sendiri kecil.)
Kannan Goundan

@KannanGoundan Anda benar sekali. Menarik komentar yang salah.
zb226

8

Di mana T2tabel tempat Anda menambahkan batasan:

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

Dan hapus hasilnya.


4

Biarkan kami memiliki 2 tabel berikut (gaji dan karyawan) masukkan deskripsi gambar di sini

Sekarang saya ingin catatan-catatan dari tabel karyawan yang tidak dalam gaji. Kita dapat melakukan ini dalam 3 cara:

  1. Menggunakan Gabung dalam
select * from employee
where id not in(select e.id from employee e inner join salary s on e.id=s.id)

masukkan deskripsi gambar di sini

  1. Menggunakan Left outer join
select * from employee e 
left outer join salary s on e.id=s.id  where s.id is null

masukkan deskripsi gambar di sini

  1. Menggunakan Full Join
select * from employee e
full outer join salary s on e.id=s.id where e.id not in(select id from salary)

masukkan deskripsi gambar di sini


2

Dari pertanyaan serupa di sini MySQL Inner Gabung Query Untuk Mendapatkan Catatan Tidak Hadir di Tabel Lain Saya berhasil ini

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

smalltableadalah di mana Anda memiliki catatan yang hilang, bigtableadalah di mana Anda memiliki semua catatan. Permintaan daftar semua catatan yang tidak ada di smalltabletetapi ada di bigtable. Anda dapat mengganti iddengan kriteria pencocokan lainnya.


0

Anda dapat memilih Tampilan seperti yang ditunjukkan di bawah ini:

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

dan kemudian bekerja pada tampilan untuk memilih atau memperbarui:

select * from AuthorizedUserProjectView where projectid = 49

yang menghasilkan hasil seperti yang ditunjukkan pada gambar di bawah ini yaitu untuk kolom yang tidak cocok nol telah diisi.

[Result of select on the view][1]

0

Saya Tidak Tahu Yang mana yang Dioptimalkan (dibandingkan dengan @AdaTheDev) tetapi yang ini tampaknya lebih cepat ketika saya gunakan (minimal untuk saya)

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

Jika Anda ingin mendapatkan atribut spesifik lainnya, Anda dapat menggunakan:

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


-2

Anda dapat melakukan hal seperti ini

   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

Bagaimana cara memilih baris tanpa entri yang cocok di kedua tabel?

    pilih * dari [dbo]. [EmppDetails] e
     bergabung dengan [Karyawan]. [Jender] d di e.Gid = d.Gid
    di mana e.Gid adalah Null

    Persatuan 
    pilih * dari [dbo]. [EmppDetails] e
     kiri gabung [Karyawan]. [Jender] d di e.Gid = d.Gid
    di mana d.Gid adalah Null

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.