MySQL 从数组中有条件地查询多对多关系(内部连接表)

MySQL conditional querying many to many relations (inner joined tables) from an array

我有一个 colors table 和一个 items table,它们之间有 多对多 关系2 table 秒(通过 items_colors table)。一个项目可以有多种颜色,一种颜色可以有很多项目。

items
   id

colors
   id
   name

items_colors
    item_id [foreign key: items(id)]
    color_id [foreign key: colors(id)]

我想从一组颜色中获取所有只匹配一种或多种提供的颜色的项目。 如果某个项目还与数组中未指定的其他颜色相关联,则不应检索它。

SELECT
    `*`
FROM
    `items`
    INNER JOIN `items_colors` ON `items_colors`.`item_id` = `items`.`id`
    INNER JOIN `colors` ON `colors`.`id` = `items_colors`.`color_id`
WHERE
    `colors`.`name` IN('green', 'blue')

在我上面的示例中,我想获得与给定数组匹配的 all 项,因此所有具有 green 的项,或者blue,或 greenblue 颜色。 但是 如果一个项目有 蓝色红色 颜色(或只有 红色,或无颜色),它应该从结果中排除。

目前没有找到合适的方法。上面示例中的查询检索的数据比我预期的要多。感谢您的帮助!

一种方法使用聚合:

SELECT i.*
FROM items i JOIN
     items_colors ic
     ON ic.item_id = i.id JOIn
     colors c
     ON c.id = ic.color_id
GROUP BY i.id
HAVING SUM( c.name NOT IN ('green', 'blue') ) = 0;

这不是 return 颜色,但你可以 return 那些 GROUP_CONCAT(c.name)

您还可以更积极地表达 HAVING 从句:

HAVING COUNT(*) = SUM( c.name IN ('green', 'blue') )

也就是说,使用 NOT EXISTS:

可能更有效
select i.*
from items i
where not exists (select 1
                  from item_colors ic join
                       colors c
                       on ic.color_id = c.id
                  where ic.item_id = i.id and
                        c.name NOT IN ('green', 'blue')
                 );

如果你是 运行 MySQL 8.0,你可以使用 window 函数进行过滤:

SELECT *
FROM (
    SELECT 
        i.*, 
        c.*, 
        ic.*, 
        SUM(c.name NOT IN ('green', 'blue')) OVER(PARTITION BY i.id) cnt 
    FROM items i
    INNER JOIN items_colors ic ON ic.item_id = i.id
    INNER JOIN colors c ON c.id = ic.color_id
) t
WHERE c.name IN ('green', 'blue') and cnt = 0