У меня есть две таблицы: Products и ProductTags (у меня также есть теги таблиц, которые не являются частью проблемы)
Мой запрос
SELECT
product.id
FROM
Products JOIN ProductTags ON Products.id = ProductTags.product_id
WHERE ProductTags.tag_id = 10 and ProductTags.tag_id <> 20
Таблица ProductTags — это связь один-ко-многим между product_id и tag_id, если она так называется. Допустим, вся таблица ProductTags:
в результате моего запроса я получаю 777 и 888. Но я хочу исключить продукты с тегом № 20. В реальном запросе я также присоединяюсь к другим таблицам (также мне нужен доступ к другим полям таблицы продуктов), поэтому я НЕ МОГУ получить правильный результат, используя только таблицу ProductTags! Я знаю, что я мог бы сделать это как
SELECT product_id ProductTags WHERE tag_id = 10
но это НЕ БУДЕТ правильным решением!
Один из способов — использовать агрегацию.
SELECT product_id id
FROM producttags
WHERE tag_id IN (10,
20)
GROUP BY product_id
HAVING max(tag_id) = 10;
Другой использует NOT EXISTS
и коррелированный подзапрос.
SELECT pt1.product_id
FROM producttag pt1
WHERE pt1.tag_id = 10
AND NOT EXISTS (SELECT *
FROM producttag pt2
WHERE pt2.product_id = pt1.product_id
AND pt2.tag_id = 20);
Обратите внимание, что объединение product
не требуется только для получения идентификаторов продуктов, если только для producttags.product_id
нет надлежащего ограничения внешнего ключа.
SELECT product.id
FROM Products
JOIN ProductTags ON Products.id = ProductTags.product_id
GROUP BY product.id
HAVING SUM(ProductTags.tag_id = 10) > 0 -- at least one
AND SUM(ProductTags.tag_id = 20) = 0 -- none
Эта форма допускает любое количество простых или сложных условий.
Например:
-- strictly one row with tag_id = 30
AND SUM(ProductTags.tag_id = 30) = 1
-- at least one tag_id 40 or 50
AND SUM(ProductTags.tag_id IN (40, 50)) > 0
-- ... and so on
Спасибо! использование NOT EXISTS помогло мне, теперь это работает как отступ!