It wouldn’t surprise me if there were already an answer to this type of problem, but I don’t know how to ask it.
The statement compares the task keys in the task table against the task/tag key combinations in another table.
I use PHP to execute this SQL, which compares values in one table against the values of another table. It works perfectly, but it looks ridiculously inefficient. It uses the same inner select statement three times to compare against a single value each time.
SELECT * FROM ttasks t
WHERE bComplete = 0
AND ( 1 IN (SELECT iTagKey FROM ttagties WHERE t.taskKey = iLinkKey AND iType = 0)
OR 3 IN (SELECT iTagKey FROM ttagties WHERE t.taskKey = iLinkKey AND iType = 0) )
AND 10 NOT IN (SELECT iTagKey FROM ttagties WHERE t.taskKey = iLinkKey AND iType = 0)
ORDER BY dCreated;
Is there something like:
WHERE bComplete = 0
AND ((1 OR 3) AND NOT 10) IN (SELECT iTagKey FROM ttagties WHERE t.taskKey = iLinkKey AND iType = 0)
or maybe
WHERE bComplete = 0
AND ASSIGN SET AS (SELECT iTagKey FROM ttagties WHERE t.taskKey = iLinkKey AND iType = 0)
AND (1 IN (SET) OR 3 IN (SET)) AND 10 NOT IN (SET)
Even if you can’t find a better way of writing the statement, could you suggest some better ways to describe the problem?
To clarify, the SELECT in the parentheses could be a set like (1,5,6,10). I haven’t found a JOIN that rejects the set because it has a 10, even though it also has a 1.
Here is the complete dataset for table ttagties
:
CREATE TABLE ttagties (
iLinkKey INT,
iTagKey INT,
iType INT);
INSERT INTO ttagties VALUES
(1,3,0),(2,1,0),(2,10,0),(3,10,0),(4,1,0),(4,10,0),(7,1,0),(7,10,0),(8,1,0),
(8,5,0),(8,10,0),(11,1,0),(11,10,0),(14,1,0),(14,10,0),(19,3,0),(19,10,0),
(21,3,0),(21,10,0),(23,3,0),(23,10,0),(25,3,0),(28,2,0),(29,2,0),(30,1,0),
(30,10,0),(31,2,0),(32,1,0),(32,6,0),(32,10,0),(33,1,0),(33,10,0),(34,2,0),
(34,6,0),(35,2,0),(36,3,0),(37,3,0),(38,1,0),(38,10,0),(39,2,0),(48,3,0),
(48,10,0),(51,1,0),(51,5,0),(51,7,0),(51,10,0),(54,1,0),(54,10,0),(55,2,0),
(57,10,0),(58,3,0),(58,10,0),(58,11,0),(60,1,0),(60,5,0),(60,10,0),(67,2,0),
(68,2,0),(69,10,0),(70,10,0),(71,2,0);
3
Answers
A combination of
JOIN
,GROUP_CONCAT()
,HAVING
andFIND_IN_SET()
functions could be an alternative:Or maybe like this:
Here’s a fiddle
You could use
EXISTS
andNOT EXISTS
to re-write your queryYou must join to the tag table twice; once to find a hit on 1 or 3 and again to assert there is no hit on 10:
Using
distinct
removes duplicates in case there are multiple hits on 1, 3.The check for
null
asserts that the join to 10 tagkeys failed to find any.This can be simplified somewhat, probably at the cost of performance, as follows:
but the style gain, if any, is marginal.