For the given specifications, it won't get faster than this::
PREPARE q1(text[]) AS
SELECTWITH p.package_id
FROMm AS ( message m -- translate names to IDs
JOIN package_to_messageSELECT pid ONAS p.message_id
= m.id FROM message
WHERE m.name = ANY($1)
)
SELECT p.package_id
FROM m
JOIN package_to_message p USING (message_id)
GROUP BY 1
HAVING count(*) = cardinality($1) -- length of array
AND NOT EXISTS ( -- no other! message
SELECT FROM package_to_message p0
WHERE p0.package_id = p.package_id
AND p0.message_id <> ALL($1TABLE m) -- m holds IDs
);
My queries assume that you pass distinct message names (no duplicates). Plus, message.name
is defined UNIQUE
.
Note how ANY
and ALL
accept an array or a set. See:
About the short syntax TABLE m
:
Indexes & performance
Index 1
The UNIQUE
constraint on table message
covers the lookup nicely.
If performance is crucial (and you meet preconditions for index-only scans) a covering index would be slightly better:
ALTER TABLE message
DROP CONSTRAINT message_name_key -- actual constraint name here!
, ADD CONSTRAINT message_name_name_id_key UNIQUE (name) INCLUDE (name_id)
;
See:
Index 2
The PRIMARY KEY
on package_to_message(message_id, package_id)
provides the perfect index for the next step.
Index 3
Ideally, you add another index on package_to_message(package_id, message_id)
for the final step. See:
With these 3 indexes in place, and if your tables are vacuumed enough, both queries can make do with index-only scans exclusively. Then we are talking a few ms even for huge tables, or even below 1 ms execution time.