MYSQL连接结果集在IN()中的where子句中擦除了结果?

时间:2011-01-10 16:41:05

标签: mysql join

严重编辑!

最初的问题是基于对IN()如何处理来自连接的结果集中的列的误解。我认为IN(some_join.some_column)会将结果列视为列表并循环遍历每一行。事实证明它只看第一行。

所以,改编的问题:MySQL中是否有任何东西可以循环来自WHERE子句的连接的结果列?

这是我正在使用的超简化代码,从复杂的crm搜索功能中删除。左连接和一般想法是来自该查询的遗物。因此,对于此查询,它必须是独占搜索 - 查找具有所有指定标记的人,而不仅仅是任何。

首先是数据库

表1:人

+----+------+
| id | name |
+----+------+
|  1 | Bob  |
|  2 | Jill |
+----+------+

表2:标签

+-----------+--------+
| person_id | tag_id |
+-----------+--------+
|         1 |      1 |
|         1 |      2 |
|         2 |      2 |
|         2 |      3 |
+-----------+--------+

美好而简单。所以,自然而然:

SELECT name, GROUP_CONCAT(tag.tag_id) FROM person LEFT JOIN tag ON person.id = tag.person_id GROUP BY name;
+------+--------------------------+
| name | GROUP_CONCAT(tag.tag_id) |
+------+--------------------------+
| Bob  | 1,2                      |
| Jill | 2,3                      |
+------+--------------------------+

到目前为止一切顺利。所以我正在寻找的东西只能在第一种情况下找到Bob而在第二种情况下只能找到Jill - 而不使用HAVING COUNT(DISTINCT ...),因为这在更广泛的查询中不起作用(有一个单独的标签继承缓存和大量其他东西)。

这是我的原始示例查询 - 基于错误的想法,即IN()会一次遍历所有行。

SELECT DISTINCT name FROM person LEFT JOIN tag ON person.id = tag.person_id 
  WHERE ( ( 1 IN (tag.tag_id) ) AND ( 2 IN (tag.tag_id) ) );                            
Empty set (0.00 sec)

SELECT DISTINCT name FROM person LEFT JOIN tag ON person.id = tag.person_id 
  WHERE ( ( 2 IN (tag.tag_id) ) AND ( 3 IN (tag.tag_id) ) );
Empty set (0.00 sec)

这是我最新的失败尝试,以了解我的目标......

SELECT name, GROUP_CONCAT(tag.tag_id) FROM person LEFT JOIN tag ON person.id = tag.person_id 
  GROUP BY person.id HAVING ( ( 1 IN (GROUP_CONCAT(tag.tag_id) ) ) ) AND ( 2 IN (GROUP_CONCAT(tag.tag_id)) );
Empty set (0.00 sec)

所以它似乎采用了一个1,2或2,3的GROUP_CONCAT字符串,并将其视为单个实体而不是表达式列表。有没有办法将分组列转换为IN()或= ANY()将其视为列表的表达式列表?

本质上,我试图在类似于数组或动态表达式列表的东西上迭代地进行IN()循环,该列表包含来自连接的所有数据行。

2 个答案:

答案 0 :(得分:4)

考虑一下您的代码在逻辑上做了什么:

( 1 IN (tag.tag_id) ) AND ( 2 IN (tag.tag_id) )

相当于

( 1 = (tag.tag_id) ) AND (2 = (tag.tag_id) )

tag.tag_id无法同时满足这两个条件,因此AND永远不会成立。

看起来您在问题中引用的OR版本是您真正想要的版本:

SELECT DISTINCT name FROM person LEFT JOIN tag ON person.id = tag.person_id 
  WHERE ( ( 1 IN (tag.tag_id) ) OR ( 2 IN (tag.tag_id) ) );   

更恰当地使用IN子句,您可以将其写为:

SELECT DISTINCT name FROM person LEFT JOIN tag ON person.id = tag.person_id 
  WHERE tag.tag_id in (1,2);   

最后要注意的是,因为你在WHERE子句(tag.tag_id)中引用了LEFT JOINed表中的列,所以你真的强迫它表现得像INNER JOIN。要真正获得LEFT JOIN,您需要将标准移出WHERE并使其成为JOIN条件的一部分:

SELECT DISTINCT name FROM person LEFT JOIN tag ON person.id = tag.person_id 
  AND tag.tag_id in (1,2);   

答案 1 :(得分:0)

 WHERE ( ( 1 IN (tag.tag_id) ) AND ( 2 IN (tag.tag_id) ) );     

由于tag.tag_id不能同时为1和2,因此永远不会返回任何结果。

此外,您使用1 IN (blah)而非blah = 1

的原因还有