Budowanie na oryginale
Twoje pierwotne zapytanie było na właściwej ścieżce, aby wykluczyć obraźliwe wiersze. Właśnie miałeś >
zamiast =
. Brakowało trudnego kroku do policzenia.
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;
Krótszy
Prawdopodobnie też szybciej.
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;
Podobne do zapytanie @Clodoaldo
lub ta wcześniejsza odpowiedź z dodatkowymi wyjaśnieniami
.every(rating_id = 1)
jest prostsze niż not bool_or(rating_id > 1)
, ale wyklucza także rating < 1
- co prawdopodobnie jest w porządku (lub nawet lepiej) w twoim przypadku.
MySQL obecnie nie implementuje (standardowy SQL!) every()
. Ponieważ chcesz wyeliminować tylko rating_id > 1
, to proste wyrażenie lepiej pasuje do Twoich wymagań i działa w obu RDBMS:
HAVING max(rating_id) = 1
Najkrótszy
Z count(*)
jako funkcja agregująca okna i bez podzapytania.
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;
Funkcje okna są stosowane po zagregowany krok. Bazując na tym, otrzymujemy dwa agregacja kroków wykonanych na poziomie pojedynczego zapytania:
- Zwiń odpowiednik
(atr1_id, atr2_id)
, z wyłączeniem wierszy, w których rozbieżnyrating_id
istnieje. - Policz pozostałe wiersze za pomocą funkcji okna w całym zestawie.
LIMIT 1
aby uzyskać pojedynczy wiersz (wszystkie wiersze byłyby identyczne).
MySQL nie posiada funkcji okna. Postgres tylko.
Najkrótszy, niekoniecznie najszybszy.
Skrzypce SQL. (Na stronie 9.2, ponieważ strona 9.3 jest obecnie offline.)