Reputation: 14835
I have the following MySQL query:
SELECT dgc.name, dgc.id, dgc.has_special
FROM keyword k
JOIN d_gift_card dgc ON k.gc_id = dgc.id
WHERE keyword IN ('red')
GROUP BY k.gc_id
HAVING COUNT(DISTINCT k.keyword) = 1
It returns to me a list of unique id
's that have the keyword
'red' attached with it.
What I am trying to do is get a list of unique id
's that do not have the keyword
'red' attached with it. Basically the opposite of what the query is doing.
I've tried this query using NOT IN but it's bringing back all results, included the ones that have 'red' associated with them. What is the proper way to this?
SELECT dgc.name, dgc.id, dgc.has_special
FROM keyword k
JOIN d_gift_card dgc ON k.gc_id = dgc.id
WHERE keyword NOT IN ('red')
GROUP BY k.gc_id
Upvotes: 0
Views: 212
Reputation: 1269703
I would simply do this by moving the condition to the having
clause:
SELECT dgc.name, dgc.id, dgc.has_special
FROM keyword k JOIN
d_gift_card dgc
ON k.gc_id = dgc.id
GROUP BY dgc.name, dgc.id, dgc.has_special
HAVING SUM( keyword IN ('red') ) = 0;
Note that the GROUP BY
clause matches the unaggregated SELECT
columns. This is a best practice -- and required by the SQL standard in most cases.
The expression in the HAVING
clause counts the number of rows that are 'red'
. The = 0
says there are none.
The most efficient way to write the query is probably:
SELECT dgc.name, dgc.id, dgc.has_special
FROM d_gift_card dgc
WHERE NOT EXISTS (SELECT 1
FROM keyword k
WHERE k.gc_id = dgc.id AND k.keyword IN ('red')
);
This can take advantage of an index on keyword(gc_id, keyword)
, and doesn't require any aggregation at all.
Upvotes: 1
Reputation: 75
You can use MINUS
. Something like this:
SELECT dgc.name, dgc.id, dgc.has_special
FROM keyword k
JOIN d_gift_card dgc ON k.gc_id = dgc.id
MINUS
SELECT dgc.name, dgc.id, dgc.has_special
FROM keyword k
JOIN d_gift_card dgc ON k.gc_id = dgc.id
WHERE keyword NOT IN ('red')
GROUP BY k.gc_id
Upvotes: -1
Reputation: 521168
One option would be to use conditional aggregation and assert that red
never occurs:
SELECT dgc.name, dgc.id, dgc.has_special
FROM d_gift_card dgc
INNER JOIN
(
SELECT gc_id
FROM keyword
GROUP BY gc_id
HAVING SUM(CASE WHEN keyword = 'red' THEN 1 ELSE 0 END) = 0
) k
ON k.gc_id = dgc.id;
The logical problem with putting the check on the keyword in the WHERE
clause is that applies to individual records, not entire id groups. So it would leave non matching records even if an overall group had positive matches as well.
Upvotes: 1