MySQL“IN”子句中的逗号分隔值

时间:2012-05-07 10:35:49

标签: php mysql sql

我的一个表中有一个列,其中存储了多个由逗号分隔的ID。 有没有办法可以在查询的“IN”子句中使用此列的值。

列(city)的值类似于6,7,8,16,21,2

我需要用作

select * from table where e_ID in (Select city from locations where e_Id=?)

我对Crozin的回​​答感到满意,但我对建议,观点和选择持开放态度。

随意分享您的观点。

11 个答案:

答案 0 :(得分:28)

在@Jeremy Smith的FIND_IN_SET()示例的基础上,您可以使用连接来完成,因此您不必运行子查询。

SELECT * FROM table t
JOIN locations l ON FIND_IN_SET(t.e_ID, l.city) > 0
WHERE l.e_ID = ?

众所周知,这表现得非常糟糕,因为它必须进行表扫描,为{em} tablelocations行的每个组合评估FIND_IN_SET()函数。它无法使用索引,也无法改进它。

我知道你说你正在努力充分利用糟糕的数据库设计,但你必须明白这是多么糟糕。

说明:假设我要求您查看电话簿中的每个人,其首字母,中间字母或最后一个字母是“J”。在这种情况下,书的排序顺序无法帮助,因为无论如何你必须扫描每一页。

@fthiella给出的LIKE解决方案在性能方面存在类似问题。它无法编入索引。

另请参阅我对Is storing a delimited list in a database column really that bad?的回答,了解这种存储非规范化数据的方法的其他缺陷。

如果您可以创建一个存储索引的补充表,您可以将位置映射到城市列表中的每个条目:

CREATE TABLE location2city (
 location INT,
 city INT,
 PRIMARY KEY (location, city)
); 

假设您有一个所有可能城市的查找表(不仅仅是table中提到的那些城市),您可以承受一次低效率来生成映射:

INSERT INTO location2city (location, city)
  SELECT l.e_ID, c.e_ID FROM cities c JOIN locations l
  ON FIND_IN_SET(c.e_ID, l.city) > 0;

现在,您可以运行更有效的查询来查找table中的条目:

SELECT * FROM location2city l
JOIN table t ON t.e_ID = l.city
WHERE l.e_ID = ?;

这可以使用索引。现在您只需要注意locations中的任何INSERT / UPDATE / DELETE行也会在location2city中插入相应的映射行。

答案 1 :(得分:25)

从MySQL的角度来看,你没有存储用逗号分隔的多个ID - 你存储的是一个 text 值,它与“Hello World”或“我喜欢的蛋糕”完全相同!” - 即它没有任何意义。

您需要做的是创建一个将数据库中的两个对象链接在一起的分隔表。阅读有关基于SQL的数据库中many-to-manyone-to-many(取决于您的要求)关系的更多信息。

答案 2 :(得分:24)

不要在查询中使用IN,而是使用FIND_IN_SETdocs):

SELECT * FROM table 
WHERE 0 < FIND_IN_SET(e_ID, (
             SELECT city FROM locations WHERE e_ID=?))

关于第一个表单规范化的常见警告适用(数据库不应该在一个列中存储多个值),但是如果你坚持使用它,那么上面的语句应该会有所帮助。

答案 3 :(得分:21)

这不使用IN子句,但它应该做你需要的:

Select *
from table
where
  CONCAT(',', (Select city from locations where e_Id=?), ',')
  LIKE
  CONCAT('%,', e_ID, ',%')

但您必须确保e_ID不包含任何逗号或任何快乐角色。

e.g。

CONCAT(',', '6,7,8,16,21,2', ',') returns ',6,7,8,16,21,2,'

e_ID=1  --> ',6,7,8,16,21,2,' LIKE '%,1,%'  ? FALSE
e_ID=6  --> ',6,7,8,16,21,2,' LIKE '%,6,%'  ? TRUE
e_ID=21 --> ',6,7,8,16,21,2,' LIKE '%,21,%' ? TRUE
e_ID=2  --> ',6,7,8,16,21,2,' LIKE '%,2,%'  ? TRUE
e_ID=3  --> ',6,7,8,16,21,2,' LIKE '%,3,%'  ? FALSE
etc.

答案 4 :(得分:7)

这个用于oracle ..其中字符串连接由wm_concat

完成
select * from table where e_ID in (Select wm_concat(city) from locations where e_Id=?)

是的我同意raheel shan ..为了把这个“在”条款中我们需要将该列放到代码一下面的行中做那个工作。

select * from table  where to_char(e_ID) 
in (
  select substr(city,instr(city,',',1,rownum)+1,instr(city,',',1,rownum+1)-instr(city,',',1,rownum)-1) from 
  (
  select ','||WM_CONCAT(city)||',' city,length(WM_CONCAT(city))-length(replace(WM_CONCAT(city),','))+1 CNT from locations where e_Id=? ) TST 
  ,ALL_OBJECTS OBJ where TST.CNT>=rownum
    ) ;

答案 5 :(得分:6)

你应该使用

FIND_IN_SET 以逗号分隔值的字符串形式返回值的位置

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

答案 6 :(得分:6)

不知道这是否是你想要完成的。使用MySQL,可以连接组GROUP_CONCAT

中的值

您可以尝试这样的事情:

select * from table where e_ID in (Select GROUP_CONCAT(city SEPARATOR ',') from locations where e_Id=?)

答案 7 :(得分:6)

您需要“拆分”城市列值。它会像:

SELECT *
  FROM table
 WHERE e_ID IN (SELECT TO_NUMBER(
                                 SPLIT_STR(city /*string*/
                                           , ',' /*delimiter*/
                                           , 1 /*start_position*/
                                           )
                                 )
                  FROM locations);

您可以在此处阅读有关MySQL split_str函数的更多信息:http://blog.fedecarg.com/2009/02/22/mysql-split-string-function/

另外,我在这里使用了Oracle的TO_NUMBER函数。请用适当的MySQL函数替换它。

答案 8 :(得分:5)

IN占用行,因此使用逗号分隔列进行搜索将无法执行您想要的操作,但如果您提供此类数据('1','2','3'),这将有效,但您无法保存此类数据在您的字段中,无论您在列中插入什么内容,它都会将整个内容作为字符串。

答案 9 :(得分:2)

您可以像this

一样动态创建预准备语句
set @sql = concat('select * from city where city_id in (',
                  (select cities from location where location_id = 3),
                  ')');
prepare in_stmt from @sql;
execute in_stmt;
deallocate prepare in_stmt;

答案 10 :(得分:0)

参考:Use a comma-separated string in an IN () in MySQL

最近我遇到了同样的问题,这就是我解决的方法。

它对我有用,希望这就是您想要的。

select * from table_name t where (select (CONCAT(',',(Select city from locations l where l.e_Id=?),',')) as city_string) LIKE CONCAT('%,',t.e_ID,',%');

示例: 它看起来像这样

select * from table_name t where ',6,7,8,16,21,2,' LIKE '%,2,%';