我的一个表中有一个列,其中存储了多个用逗号分隔的 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 的回答感到满意,但我愿意接受建议、观点和选择。
随时分享您的观点。
在@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() 函数。它不能利用索引,也没有办法改进它。table
locations
我知道您说过您正在努力充分利用糟糕的数据库设计,但您必须了解这是多么糟糕。
解释:假设我要你在电话簿中查找第一个、中间或最后一个首字母是“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
.
IN
与其在您的查询中使用,不如使用FIND_IN_SET
(docs):
SELECT * FROM table
WHERE 0 < FIND_IN_SET(e_ID, (
SELECT city FROM locations WHERE e_ID=?))
关于第一种形式规范化的常见警告适用(数据库不应在单个列中存储多个值),但如果您坚持使用它,那么上述语句应该会有所帮助。
这不使用 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.
不知道这是否是你想要完成的。使用 MySQL 具有连接组GROUP_CONCAT中的值的功能
你可以尝试这样的事情:
select * from table where e_ID in (Select GROUP_CONCAT(city SEPARATOR ',') from locations where e_Id=?)
这个用于 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
) ;
你应该使用
FIND_IN_SET返回值在逗号分隔值字符串中的位置
mysql> SELECT FIND_IN_SET('b','a,b,c,d');
-> 2
您需要“拆分”城市列值。它会像:
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 函数替换它。
IN 采用行,因此以逗号分隔的列进行搜索不会满足您的要求,但如果您提供这样的数据('1','2','3'),这将起作用,但您不能在您的字段中保存这样的数据无论您在列中插入什么,它都会将整个内容作为一个字符串。
您可以像这样动态创建准备好的语句
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;
最近我遇到了同样的问题,这就是我解决它的方法。
它对我有用,希望这就是你想要的。
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,%';