所有关系都存在的一对多

时间:2018-03-20 14:24:14

标签: mysql join one-to-many

假设我有三个MySQL表:

╔═════════════════╗     ╔════════════╗     ╔══════════════════════╗
║     animals     ║     ║   colors   ║     ║     animal_color     ║
╠════╦════════════╣     ╠════╦═══════╣     ╠═══════════╦══════════╣
║ id ║    name    ║     ║ id ║ name  ║     ║ animal_id ║ color_id ║
╠════╬════════════╣     ╠════╬═══════╣     ╠═══════════╬══════════╣
║  1 ║ Panther    ║     ║  1 ║ black ║     ║         1 ║        1 ║
║  2 ║ Zebra      ║     ║  2 ║ white ║     ║         2 ║        1 ║
║  3 ║ Polar bear ║     ║  3 ║ blue  ║     ║         2 ║        2 ║
╚════╩════════════╝     ╚════╩═══════╝     ║         3 ║        2 ║
                                           ╚═══════════╩══════════╝

每只动物可以分配一种或多种颜色。我如何寻找同时具有黑色白色颜色(斑马)的动物?我知道有多种方法可以做到,但有哪些简单方法呢?也许没有任何子查询?

我出现了类似的东西:

SELECT *
FROM   `animals`
WHERE  `id` IN (SELECT `animal_id`
                FROM   `animal_color`
                WHERE  `color_id` IN ( 1, 2 )
                GROUP  BY `animal_id`
                HAVING COUNT(*) = 2)

但是我觉得它很笨拙而我也不喜欢我需要计算我感兴趣的颜色数量。

似乎有一些简单的方法可以做到。

更新

了解这个问题的两个版本的简单方法会很棒:

  1. 选择黑色和白色没有任何其他颜色的动物
  2. 选择黑色,白色以及可能更多颜色的动物,但不一定

4 个答案:

答案 0 :(得分:1)

我不认为你的方法太糟糕了,还有你提到的唯一索引,以确保结果是正确的。

有一个更简单的'没有子查询的版本 - 你可以连接两次动物颜色,一次用于黑色,一次用于白色 - 它会自我过滤,但这种方法不可扩展到覆盖3种颜色的动物查询等,所以我不热衷于它作为一种解决方案。

只要SQL生成的查询计划有效,并且查询的含义明显/可维护,那就足够了。那些应该是SQL语句的目标。

答案 1 :(得分:0)

对于黑白颜色的动物:

SELECT a.name 
FROM animals a
INNER JOIN animal_color ac ON (a.id = ac.animal_id)
INNER JOIN colors c ON (ac.color_id = c.id AND c.name IN ('black', 'white'))
GROUP BY a.id
HAVING COUNT(DISTINCT ac.color_id) = 2

话虽如此,你的方法本身并不“不正确”。使用连接过滤实际上可能被认为比where语句内部的可读性差,但是我更喜欢缺少子查询,这是基于意见的,并且两种方法都将在没有性能问题的情况下进行规划。

另请注意,如果您不关心按ID指定颜色,则可以删除连接:

SELECT a.name 
FROM animals a
INNER JOIN animal_color ac ON (a.id = ac.animal_id AND ac.color_id IN (1, 2))
GROUP BY a.id
HAVING COUNT(DISTINCT ac.color_id) = 2

答案 2 :(得分:0)

检查动物是否有2种颜色,这些颜色是白色和黑色。

SELECT a.id, a.name
FROM animals a
JOIN animal_color c on c.animal_id = a.id
GROUP a.id, a.name
HAVING Count(color_id) = 2
   AND Count(CASE WHEN c.color_id IN (1,2) 
                  THEN 1 
              END) = 2

答案 3 :(得分:0)

我提出了自己的解决方案。

选择黑色和白色的动物,但也可以有其他颜色:

SELECT * 
FROM   animals 
WHERE  (SELECT COUNT(DISTINCT colors.id) 
        FROM   colors 
               JOIN animal_color 
                 ON animal_color.color_id = colors.id 
        WHERE  animal_color.animal_id = animals.id 
               AND colors.name IN ( 'black', 'white' )) = 2

选择仅黑色和白色的动物:

SELECT * 
FROM   animals 
WHERE  (SELECT GROUP_CONCAT(DISTINCT colors.name ORDER BY colors.name) 
        FROM   colors 
               JOIN animal_color 
                 ON animal_color.color_id = colors.id 
        WHERE  animal_color.animal_id = animals.id) = 'black,white' 

如你所见,我是按颜色获取的,因为它有时会更有用。将其更改为颜色ID很容易。

另外,我可以轻松获取我想要的任何其他数据,因为只能在WHERE子句中查找动物。

最后,它可以正确使用重复的颜色。获取黑色和白色只需要脚本按字母顺序排序所需的颜色,但它没关系。我对相对简单的查询感到满意。