【问题标题】:MYSQL join results set wiped results during IN () in where clause?MYSQL连接结果集在where子句中IN()期间擦除结果?
【发布时间】:2011-01-10 16:41:04
【问题描述】:

大量编辑!

最初的问题是基于对 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)

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

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

【问题讨论】:

  • 所以再进行一些测试,如果我使用 = 而不是 IN,这些结果似乎是相同的。所以,也许,MySQL 优化器正在将我的 'IN' 转换为 '=',看到这给出了“P = X AND P = Y”,因此返回一个几乎不接触数据库的空结果集 - 因此超级快速响应时间
  • 超快的响应时间可能是由于 WHERE 条件永远不会为真,因此不需要读取任何行。见乔的回答。

标签: mysql join


【解决方案1】:

想想你的代码在逻辑上做什么:

( 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);   

【讨论】:

  • 是的,听起来我误解了 IN () 在结果集上的工作方式——我认为它会将列视为循环遍历的集合,而不是单行。那么,是否有任何 MySQL 函数可以按照我的预期 IN 工作?例如,我可以通过管道输入 GROUP_CONCAT 的结果吗?
  • @user568458:您在我的回答中尝试了查询的最终版本吗?
  • 感谢您提供更多详细信息 - 不幸的是,我确实需要 AND 例如让 Bob 在 1 和 2,而不是 Bob 和 Jill。不同类型的搜索。同样对于较大的查询,这部分查询需要位于 AND 链中的 OR 旁边 - 简而言之,它需要循环到位。是否有任何东西能够像这样在一个 WHERE 子子句中循环一列 - 例如 GROUP_CONCAT 上的 IN 或使用 LOOP 和 CASE 逻辑制作的东西?
  • 我已经把问题的那一边移到了stackoverflow.com/questions/4078838/…——经过反思,这是一个单独的问题。
【解决方案2】:
 WHERE ( ( 1 IN (tag.tag_id) ) AND ( 2 IN (tag.tag_id) ) );     

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

另外你使用1 IN (blah)而不是blah = 1还有什么原因吗?

【讨论】:

  • 我要做的是遍历 tag.tag_id 中的结果列。一行 tag.tag_id 不能是 1 和 2 - 但我的理解是 IN 循环遍历所有结果并在找到任何匹配项时返回 true。听起来我错了 - 所以如果 IN () 和 = 在这里基本上是一样的,我怎么能遍历我的连接结果列?
  • 您的 where 子句只是对数据的行过滤器,如果您想过滤给定的一组 tag_id,请使用 tag_id IN (1,2),它类似于 tag_id=1 OR tag_id=2 。 IN 匹配您在列表中提供的任何值,例如tag_id in (1,2,3) 与标签 id 匹配,如果它是 1 、 2 或 3 。但这与说 tag_id in (1) AND tag_id in (2) AND tag_id in (3) 有很大不同。将子句转为1 IN (tag_id) 并不会使其在所有行中的tag_id 与1 匹配,它仍然是一个行过滤器。
猜你喜欢
  • 2020-06-23
  • 2012-01-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-11-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多