I have this simple structure in my database
CREATE TABLE species (
_id INTEGER PRIMARY KEY,
name TEXT NOT NULL
);
CREATE TABLE compatibility (
_id INTEGER PRIMARY KEY,
speciesA INTEGER,
speciesB INTEGER,
compatibility TINYINT NOT NULL
);
I want to point out that speciesA and speciesB are compound unique to prevent duplicated information. For example, if there would be only 5 species in my database, the compatibility table would look like this:
INSERT INTO species VALUES (1, 'EspecieA');
INSERT INTO species VALUES (2, 'EspecieB');
INSERT INTO species VALUES (3, 'EspecieC');
INSERT INTO species VALUES (4, 'EspecieD');
INSERT INTO species VALUES (5, 'EspecieD');
INSERT INTO compatibility VALUES (null, 1, 2, 1);
INSERT INTO compatibility VALUES (null, 1, 3, 1);
INSERT INTO compatibility VALUES (null, 1, 4, 1);
INSERT INTO compatibility VALUES (null, 1, 5, 0);
INSERT INTO compatibility VALUES (null, 2, 3, 1);
INSERT INTO compatibility VALUES (null, 2, 4, 1);
INSERT INTO compatibility VALUES (null, 2, 5, 0);
INSERT INTO compatibility VALUES (null, 3, 4, 1);
INSERT INTO compatibility VALUES (null, 3, 5, 1);
INSERT INTO compatibility VALUES (null, 4, 5, 1);
I need to write a query that from a given list of species returns a list of species that are fully compatible with each other, meaning all species in the result list must be compatible with all the species in the list provided. Provided species must not be in the result list.
I have tried the following query, but it only returns species that are compatible with at least one of the provided species:
SELECT id, name
FROM species s
WHERE s.id NOT IN (
SELECT IF(speciesA NOT IN (1,2,3), speciesA, speciesB) AS specie
FROM compatibility
WHERE (speciesA IN (1,2,3)
AND compatible IN (0)) OR (speciesB IN (1,2,3)
AND compatible IN (0))
)
AND s.id NOT IN (1,2,3);
How can I modify this query to obtain the list of species that are fully compatible with each other?
For the query above the expected result should be a list of species that only contains species 4. Species 1, 2, 3 are excluded as are in the list provided, and 5 should be excluded cause is not compatible with species 1 and 2.
Any help or suggestions would be greatly appreciated. Thank you!
2
Answers
This will only include species that are explicitly linked with compatibility = 1 (ie assumes by default that species are not compatible)
This will include all species except those that are explicitly linked with compatibility = 0 (ie assumes by default that species are compatible)
Whichever of these better fits your logic, I would also recommend that to optimise query performance you add indices on the
compatibility.speciesA
andcompatibility.speciesB
columns.We can solve the question with filtering and aggregation.
The subquery puts the reference species (
ref
) and the compatible species (spec
) in two different columns. We can then filter on the three reference species that you are interested in, group by the compatible species, and finally retain groups that match entirely with ahaving
clause.If you wanted the name of the species as well, we can
join
:Demo on DB Fiddle