Saya tidak akan mengomentari apakah ada skema yang lebih cocok untuk melakukan ini (sangat mungkin), tetapi untuk skema yang memiliki kolom name
dan item
, kueri berikut akan berfungsi. (sintaks mysql)
SELECT k.name
FROM (SELECT DISTINCT name FROM sets) AS k
INNER JOIN sets i1 ON (k.name = i1.name AND i1.item = 1)
INNER JOIN sets i2 ON (k.name = i2.name AND i2.item = 3)
INNER JOIN sets i3 ON (k.name = i3.name AND i3.item = 5)
LEFT JOIN sets ix ON (k.name = ix.name AND ix.item NOT IN (1, 3, 5))
WHERE ix.name IS NULL;
Idenya adalah bahwa kita memiliki semua set kunci di k
, yang kemudian kita gabungkan dengan set data item di sets
sekali untuk setiap set item dalam set yang kita cari, tiga dalam kasus ini. Masing-masing dari tiga gabungan bagian dalam dengan alias tabel i1
, i2
dan i3
menyaring semua nama set yang tidak berisi item yang dicari dengan gabungan itu. Akhirnya, kami memiliki gabungan kiri dengan sets
dengan tabel alias ix
, yang membawa semua item tambahan di set, yaitu, setiap item yang tidak kami cari. ix.name
adalah NULL
jika tidak ada item tambahan yang ditemukan, persis seperti yang kita inginkan, maka WHERE
ayat. Kueri mengembalikan baris yang berisi set kunci jika set ditemukan, sebaliknya tidak ada baris.
Sunting: Gagasan di balik jawaban collapsars tampaknya jauh lebih baik daripada milik saya, jadi inilah versi yang lebih pendek dengan penjelasannya.
SELECT sets.name
FROM sets
LEFT JOIN (
SELECT DISTINCT name
FROM sets
WHERE item NOT IN (1, 3, 5)
) s1
ON (sets.name = s1.name)
WHERE s1.name IS NULL
GROUP BY sets.name
HAVING COUNT(sets.item) = 3;
Idenya di sini adalah subquery s1
memilih kunci dari semua set yang berisi item lain yang kita cari. Jadi, ketika kami pergi, gabungkan sets
dengan s1
, s1.name
adalah NULL
ketika set hanya berisi item yang kita cari. Kami kemudian mengelompokkan berdasarkan set kunci dan menyaring setiap set yang memiliki jumlah item yang salah. Kami kemudian hanya memiliki set yang hanya berisi item yang kami cari dan dengan panjang yang benar. Karena set hanya dapat berisi satu item, hanya ada satu set yang memenuhi kriteria tersebut, dan itulah yang kami cari.
Sunting: Saya baru sadar bagaimana melakukan ini tanpa pengecualian.
SELECT totals.name
FROM (
SELECT name, COUNT(*) count
FROM sets
GROUP BY name
) totals
INNER JOIN (
SELECT name, COUNT(*) count
FROM sets
WHERE item IN (1, 3, 5)
GROUP BY name
) matches
ON (totals.name = matches.name)
WHERE totals.count = 3 AND matches.count = 3;
Subquery pertama menemukan jumlah total item di setiap set dan yang kedua menemukan jumlah item yang cocok di setiap set. Saat matches.count
adalah 3, set memiliki semua item yang kita cari, dan jika totals.count
juga 3, set tidak memiliki item tambahan.