如何将CTE返回的逗号分隔ID字符串用作IN子句参数?
问题描述
现有一张名为Table的表,创建及数据插入语句如下:
Create table Table ( ID Number , Name varchar2(100) ); insert all into Table (ID, Name) values (1, 'Alex') into Table (ID, Name) values (2, 'Amy') into Table (ID, Name) values (3, 'Jim') select * from dual;
表中数据如下:
| ID | Name |
|---|---|
| 1 | Alex |
| 2 | Amy |
| 3 | Jim |
通过以下SQL可得到逗号分隔的ID列表(结果为1,2):
select substr( listagg(Table.ID || ',') within group (order by null) , 1 , length(listagg(Table.ID || ',') within group (order by null)) - 1 ) IDs from Table where Name like 'A%'
尝试将该结果用于另一查询的IN子句,编写SQL如下:
with CTE as ( select substr( listagg(tbl.ID || ',') within group (order by null) , 1 , length(listagg(tbl.ID || ',') within group (order by null)) - 1 ) IDs from Table where Name like 'A%' ) select * from Table where cast(ID as varchar2(1000)) in (select IDs from CTE) -- 尝试在这里使用结果 -- 原以为需要转换类型,否则会触发ORA-01722错误
期望返回ID为1、2的两条数据,但执行时仍触发ORA-01722错误,请问如何将CTE返回的IDs字符串作为IN子句参数?
解决方案
方法一:直接使用子查询(推荐)
完全没必要把ID拼接成字符串再拆分,直接在IN子句里用子查询筛选符合条件的ID就行,这是最简洁高效的方式,还能避开类型转换的坑:
select * from Table where ID in ( select ID from Table where Name like 'A%' );
方法二:拆分拼接后的字符串(仅当必须处理字符串时使用)
如果因为某些限制必须用拼接好的字符串,就得把这个字符串拆成单个ID值,再用于查询。可以用REGEXP_SUBSTR配合CONNECT BY来拆分逗号分隔的字符串:
with CTE as ( select substr( listagg(tbl.ID || ',') within group (order by null) , 1 , length(listagg(tbl.ID || ',') within group (order by null)) - 1 ) IDs from Table where Name like 'A%' ), SplitIDs as ( select trim(regexp_substr(CTE.IDs, '[^,]+', 1, level)) as ID from CTE connect by level <= regexp_count(CTE.IDs, ',') + 1 ) select t.* from Table t join SplitIDs s on t.ID = to_number(s.ID);
原代码触发ORA-01722错误的原因是:IN子句里传入的是单个字符串'1,2',不是两个独立的数值1和2。就算把ID转成字符串,cast(ID as varchar2(1000)) in ('1,2')其实是在判断ID的字符串形式是否等于'1,2',显然没有匹配项,同时Oracle尝试把字符串'1,2'隐式转为数值时失败,就抛出了错误。
内容的提问来源于stack exchange,提问作者user12571241
相关产品推荐
相关产品推荐

