Няма да коментирам дали има по-подходяща схема за това (възможно е), но за схема с колони name
и item
, следната заявка трябва да работи. (синтаксис на 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;
Идеята е, че имаме всички зададени ключове в k
, които след това присъединяваме с данните от набора в sets
веднъж за всеки комплект от комплекта, който търсим, в този случай три. Всяко от трите вътрешни свързвания с псевдоними на таблица i1
, i2
и i3
филтрирайте всички имена на набори, които не съдържат търсения елемент с това обединение. И накрая, имаме ляво присъединяване с sets
с псевдоним на таблица ix
, което въвежда всички допълнителни елементи в комплекта, тоест всеки артикул, който не сме търсили. ix.name
е NULL
в случай, че не бъдат намерени допълнителни елементи, което е точно това, което искаме, по този начин WHERE
клауза. Заявката връща ред, съдържащ ключа set, ако наборът е намерен, в противен случай няма редове.
Редактиране: Идеята зад отговора на колапсите изглежда е много по-добра от моята, така че ето малко по-кратка версия на това с обяснение.
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;
Идеята тук е подзаявката s1
избира ключовете на всички набори, които съдържат елементи, различни от тези, които търсим. По този начин, когато напуснахме join sets
с s1
, s1.name
е NULL
когато комплектът съдържа само елементи, които търсим. След това групираме по ключ за набор и филтрираме всички набори с грешен брой елементи. След това остават само набори, които съдържат само елементи, които търсим, и са с правилна дължина. Тъй като наборите могат да съдържат елемент само веднъж, може да има само един набор, отговарящ на тези критерии и това е този, който търсим.
Редактиране: Просто ми хрумна как да направя това без изключване.
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;
Първата подзаявка намира общия брой елементи във всеки набор, а втората открива броя на съответстващите елементи във всеки набор. Когато matches.count
е 3, комплектът има всички елементи, които търсим, и ако totals.count
също е 3, в комплекта няма допълнителни елементи.