Надграждане на вашия оригинал
Първоначалната ви заявка беше на правилния път, за да изключи редове в нарушение. Току-що имахте >
вместо =
. Липсваше сложната стъпка за преброяване.
SELECT count(*) AS ct
FROM (
SELECT 1
FROM compatibility c
WHERE rating_id = 1
AND NOT EXISTS (
SELECT 1
FROM compatibility c2
WHERE c2.rating_id > 1
AND (c2.attr1_id = c.attr1_id AND c2.attr2_id = c.attr2_id OR
c2.attr1_id = c.attr2_id AND c2.attr2_id = c.attr1_id))
GROUP BY least(attr1_id, attr2_id), greatest(attr1_id, attr2_id)
) sub;
По-кратко
Вероятно и по-бързо.
SELECT count(*) AS ct
FROM (
SELECT 1 -- selecting more columns for count only would be a waste
FROM compatibility
GROUP BY least(attr1_id, attr2_id), greatest(attr1_id, attr2_id)
HAVING every(rating_id = 1)
) sub;
Подобно на заявката на @Clodoaldo
или този по-ранен отговор с повече обяснения
.every(rating_id = 1)
е по-просто от not bool_or(rating_id > 1)
, но също така изключва rating < 1
- което вероятно е добре (или дори по-добре) за вашия случай.
MySQL в момента не прилага (стандартен SQL!) every()
. Тъй като искате да елиминирате само rating_id > 1
, този прост израз отговаря по-точно на вашите изисквания и работи и в двете RDBMS:
HAVING max(rating_id) = 1
Най-кратък
С count(*)
като агрегатна функция за прозорец и без подзаявка.
SELECT count(*) OVER () AS ct
FROM compatibility
GROUP BY least(attr1_id, attr2_id), greatest(attr1_id, attr2_id)
HAVING max(rating_id) = 1
LIMIT 1;
Функциите на прозореца се прилагат след агрегатната стъпка. Надграждайки това, получаваме две обобщени стъпки, извършени в едно ниво на заявка:
- Сгъваем еквивалент
(atr1_id, atr2_id)
, с изключение на редове, къдетоrating_id
се различава съществуват. - Преброяване на оставащите редове с прозоречна функция върху целия набор.
LIMIT 1
за да получите един ред (всички редове ще бъдат идентични).
MySQL няма прозоречни функции. Postgres само.
Най-кратък, не непременно най-бърз.
SQL Fiddle. (На pg9.2, тъй като pg9.3 в момента е офлайн.)