Bygger på din original
Din oprindelige forespørgsel var på rette vej for at ekskludere stødende rækker. Du har lige haft > i stedet for
=
. Det vanskelige trin at tælle manglede.
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;
Kortere
Sandsynligvis også hurtigere.
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;
Svarer til @Clodoaldos forespørgsel
eller dette tidligere svar med mere forklaring
.every(rating_id =1)
er enklere end ikke bool_or(rating_id> 1)
, men udelukker også rating <1
- hvilket sikkert er fint (eller endnu bedre) for dit tilfælde.
MySQL implementerer ikke i øjeblikket (standard SQL!) every()
. Da du kun ønsker at fjerne rating_id> 1
, dette enkle udtryk passer bedre til dine krav og fungerer i begge RDBMS:
HAVING max(rating_id) = 1
Korteste
Med count(*)
som vinduesaggregatfunktion og uden underforespørgsel.
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;
Vinduesfunktioner anvendes efter det samlede trin. Med udgangspunkt i dette får vi to aggregerede trin udført på et enkelt forespørgselsniveau:
- Fold tilsvarende
(atr1_id, atr2_id)
, undtagen rækker, hvorrating_id
er divergerende eksisterer. - Tæl resterende rækker med en vinduesfunktion over hele sættet.
LIMIT 1
for at få en enkelt række (alle rækker ville være identiske).
MySQL har ikke vinduesfunktioner. Postgres kun.
Korteste, ikke nødvendigvis hurtigst.
SQL Fiddle. (På pg9.2, da pg9.3 i øjeblikket er offline.)