27

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

column( city) 的值如下6,7,8,16,21,2

我需要用作

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

我对 Crozin 的回答感到满意,但我愿意接受建议、观点和选择。

随时分享您的观点。

4

11 回答 11

31

在@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 = ?

众所周知,这执行得很差,因为它必须进行表扫描,为和中的每个行组合评估 FIND_IN_SET() 函数。它不能利用索引,也没有办法改进它。tablelocations

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

解释:假设我要你在电话簿中查找第一个、中间或最后一个首字母是“J”的每个人。在这种情况下,书籍的排序顺序没有任何帮助,因为无论如何您都必须扫描每一页。

LIKE@fthiella 给出的解决方案在性能方面也有类似的问题。它不能被索引。

另请参阅我对在数据库列中存储分隔列表真的那么糟糕吗?对于这种存储非规范化数据的方式的其他缺陷。

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

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 = ?;

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

于 2012-11-20T00:49:57.607 回答
25

从 MySQL 的角度来看,您存储的不是多个用逗号分隔的 id - 您存储的是一个文本值,其含义与“Hello World”或“I like cakes!”完全相同。- 即它没有任何意义。

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

于 2012-05-07T10:48:50.003 回答
25

IN与其在您的查询中使用,不如使用FIND_IN_SETdocs):

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

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

于 2012-11-16T14:43:08.617 回答
22

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

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

但你必须确保它e_ID不包含任何逗号或任何快乐字符。

例如

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.
于 2012-11-18T22:58:23.813 回答
8

不知道这是否是你想要完成的。使用 MySQL 具有连接组GROUP_CONCAT中的值的功能

你可以尝试这样的事情:

select * from table where e_ID in (Select GROUP_CONCAT(city SEPARATOR ',') from locations where e_Id=?)
于 2012-11-23T07:01:18.117 回答
7

这个用于 oracle ..here 字符串连接由 wm_concat 完成

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

是的,我同意 raheel shan 的观点。为了将这个“in”子句放入,我们需要将该列放入代码下面的行中,以便完成这项工作。

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
    ) ;
于 2012-11-21T12:22:54.833 回答
6

你应该使用

FIND_IN_SET返回值在逗号分隔值字符串中的位置

mysql> SELECT FIND_IN_SET('b','a,b,c,d');
    -> 2
于 2012-11-22T13:42:06.483 回答
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 函数替换它。

于 2012-11-23T10:33:57.943 回答
5

IN 采用行,因此以逗号分隔的列进行搜索不会满足您的要求,但如果您提供这样的数据('1','2','3'),这将起作用,但您不能在您的字段中保存这样的数据无论您在列中插入什么,它都会将整个内容作为一个字符串。

于 2012-05-07T10:51:18.283 回答
2

您可以像这样动态创建准备好的语句

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;
于 2012-12-30T08:05:19.463 回答
0

参考:在 MySQL中使用逗号分隔的字符串IN ()

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

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

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,%';
于 2020-03-04T13:45:33.230 回答