17

这个问题是从MYSQL 连接结果集擦除结果在 IN () in where 子句中出现的?

所以,这个问题的简短版本。如何将 GROUP_CONCAT 返回的字符串转换为逗号分隔的表达式列表,IN() 将其视为要循环的多个项目的列表?

注意 MySQL 文档似乎将 IN () 使用的“(逗号,分隔,列表)”称为“表达式列表”,有趣的是,IN() 上的页面似乎或多或少是 MySQL 文档中唯一的页面永远引用表达式列表。所以我不确定用于制作数组或临时表的函数是否在这里有用。


问题的基于示例的长版本:来自这样的 2 表数据库:

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

我该如何打开它,因为它使用字符串被视为(1 = X)AND(2 = X)的逻辑等价物......

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.01 sec)

...进入 GROUP_CONCAT 结果被视为列表的地方,因此对于 Bob 来说,它相当于:

SELECT name, GROUP_CONCAT(tag.tag_id) FROM person INNER JOIN tag ON person.id = tag.person_id AND person.id = 1 
GROUP BY person.id HAVING ( ( 1 IN (1,2) ) AND ( 2 IN (1,2) ) );
+------+--------------------------+
| name | GROUP_CONCAT(tag.tag_id) |
+------+--------------------------+
| Bob  | 1,2                      |
+------+--------------------------+
1 row in set (0.00 sec)

...对于吉尔来说,它相当于:

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

...所以总体结果将是一个排他搜索子句,要求所有列出的标签不使用 HAVING COUNT(DISTINCT ...) ?

(注意:此逻辑在没有 AND 的情况下有效,适用于字符串的第一个字符。例如

SELECT name, GROUP_CONCAT(tag.tag_id) FROM person LEFT JOIN tag ON person.id = tag.person_id 
  GROUP BY person.id HAVING ( ( 2 IN (GROUP_CONCAT(tag.tag_id) ) ) );
+------+--------------------------+
| name | GROUP_CONCAT(tag.tag_id) |
+------+--------------------------+
| Jill | 2,3                      |
+------+--------------------------+
1 row in set (0.00 sec)
4

2 回答 2

45

而不是 using IN(), usingFIND_IN_SET()也是一种选择吗?

http://dev.mysql.com/doc/refman/5.0/en/string-functions.html#function_find-in-set

mysql> SELECT FIND_IN_SET('b','a,b,c,d');
    -> 2

这是一个基于问题中示例问题的完整示例,由提问者在之前对问题的编辑中进行了测试:

SELECT name FROM person LEFT JOIN tag ON person.id = tag.person_id GROUP BY person.id 
  HAVING ( FIND_IN_SET(1, GROUP_CONCAT(tag.tag_id)) ) AND ( FIND_IN_SET(2, GROUP_CONCAT(tag.tag_id)) );
+------+
| name |
+------+
| Bob  |
+------+
于 2011-01-10T19:53:51.330 回答
5

您可以使用拆分分隔符将字符串作为数组传递,然后在函数中分解它,这将与结果一起使用。

举个简单的例子,如果你有一个这样的字符串数组:'one|two|tree|four|five',并且想知道数组中是否有两个,你可以这样做:

create function str_in_array( split_index varchar(10), arr_str varchar(200), compares varchar(20) )
  returns boolean
  begin
  declare resp boolean default 0;
  declare arr_data varchar(20);

  -- While the string is not empty
  while( length( arr_str ) > 0  ) do

  -- if the split index is in the string
  if( locate( split_index, arr_str ) ) then

      -- get the last data in the string
    set arr_data = ( select substring_index(arr_str, split_index, -1) );

    -- remove the last data in the string
    set arr_str = ( select
      replace(arr_str,
        concat(split_index,
          substring_index(arr_str, split_index, -1)
        )
      ,'')
    );
  --  if the split index is not in the string
  else
    -- get the unique data in the string
    set arr_data = arr_str;
    -- empties the string
    set arr_str = '';
  end if;

  -- in this trivial example, it returns if a string is in the array
  if arr_data = compares then
    set resp = 1;
  end if;

 end while;

return resp;
end
|

delimiter ;

我想创建一组有用的 mysql 函数来使用这种方法。有意者请联系我。

更多示例,请访问http://blog.idealmind.com.br/mysql/how-to-use-string-as-array-in-mysql-and-work-with/

于 2012-07-22T15:42:25.747 回答