SELECT from table with Varying IN list in WHERE 子句
SELECT from table with Varying IN list in WHERE clause
我正在处理的项目中遇到问题,我无法为您提供实际代码,但我创建了一个 executable 示例代码,如下所示
这里temp
和temp_id
是两个table
temp
table 包含逗号分隔的 ID 列表,即 VARCHAR2
temp_id
table 包含实际 ID,即 NUMBER
我想通过从 temp
table
的逗号分隔 ID 列表中获取 ids
来搜索 temp_id
table 中的行
//DDLs to create table
CREATE TABLE temp(ids VARCHAR2(4000));
CREATE TABLE temp_id(data_id NUMBER);
//DMLs to populate test data
INSERT INTO temp VALUES('1, 2, 3');
INSERT INTO temp_id VALUES(1);
INSERT INTO temp_id VALUES(2);
INSERT INTO temp_id VALUES(3);
INSERT INTO temp_id VALUES(4);
INSERT INTO temp_id VALUES(5);
此查询无效
SELECT * FROM temp_id WHERE data_id IN (SELECT to_number(COLUMN_VALUE) FROM XMLTABLE(SELECT ids FROM temp));
工作查询[=46=]
SELECT * FROM temp_id WHERE data_id IN (SELECT to_number(COLUMN_VALUE) FROM XMLTABLE('1, 2, 3'));
以上两个查询的区别是我在第一个查询中使用 temp
table 中的列,在第二个查询中直接引用 varchar2
。不明白为什么不工作的原因?我错过了什么吗?我认为可能存在某些数据类型不匹配但无法弄清楚。
您的要求称为 Varying IN-lists。参见 Varying IN list of values in WHERE clause
原因: IN ('1, 2, 3')
NOT 与 IN (1, 2, 3)
或 IN('1', '2', '3')
[=25 相同=]
因此,
SELECT * FROM temp_id WHERE data_id IN(SELECT ids FROM temp);
等同于
SELECT * FROM temp_id WHERE data_id IN('1, 2, 3');
这会引发 错误 ORA-01722: invalid number
-
SQL> SELECT * FROM temp_id WHERE data_id IN('1, 2, 3');
SELECT * FROM temp_id WHERE data_id IN('1, 2, 3')
*
ERROR at line 1:
ORA-01722: invalid number
SQL> SELECT * FROM temp_id WHERE data_id IN(SELECT ids FROM temp);
SELECT * FROM temp_id WHERE data_id IN(SELECT ids FROM temp)
*
ERROR at line 1:
ORA-01722: invalid number
与
不同
SELECT * FROM temp_id WHERE data_id IN(1, 2, 3);
这会给你正确的输出 -
SQL> SELECT * FROM temp_id WHERE data_id IN(1, 2, 3);
DATA_ID
----------
1
2
3
解法:
根据你的要求,你可以这样实现-
SQL> SELECT * FROM temp;
IDS
--------------------------------------------------------------
1, 2, 3
SQL> SELECT * FROM temp_id;
DATA_ID
----------
1
2
3
4
5
SQL> WITH data AS
2 (SELECT to_number(trim(regexp_substr(ids, '[^,]+', 1, LEVEL))) ids
3 FROM temp
4 CONNECT BY instr(ids, ',', 1, LEVEL - 1) > 0
5 )
6 SELECT * FROM temp_id WHERE data_id IN
7 (SELECT ids FROM data
8 )
9 /
DATA_ID
----------
1
2
3
或者,您可以创建自己的TABLE 函数 或流水线函数 来实现此目的。您的目标应该是 split the comma-separated IN list into multiple rows。怎么做取决于你!
工作演示
让我们以 SCOTT
架构中的标准 EMP table 为例。
我有一个字符串形式的工作列表,我想计算这些工作的员工人数:
SQL> SET serveroutput ON
SQL> DECLARE
2 str VARCHAR2(100);
3 cnt NUMBER;
4 BEGIN
5 str := q'[CLERK,SALESMAN,ANALYST]';
6 SELECT COUNT(*) INTO cnt FROM emp WHERE JOB IN (str);
7 dbms_output.put_line('The total count is '||cnt);
8 END;
9 /
The total count is 0
PL/SQL procedure successfully completed.
哦!发生了什么?标准 emp table 应该给出输出 10。原因是 varying IN list.
让我们看看正确的做法:
SQL> SET serveroutput ON
SQL> DECLARE
2 str VARCHAR2(100);
3 cnt NUMBER;
4 BEGIN
5 str := q'[CLERK,SALESMAN,ANALYST]';
6 SELECT COUNT(*)
7 INTO cnt
8 FROM emp
9 WHERE job IN
10 (SELECT trim(regexp_substr(str, '[^,]+', 1, LEVEL))
11 FROM dual
12 CONNECT BY instr(str, ',', 1, LEVEL - 1) > 0
13 );
14 dbms_output.put_line('The total count is '||cnt);
15 END;
16 /
The total count is 10
PL/SQL procedure successfully completed.
还有另一种方法可以实现,即使用 LIKE
:
SELECT ti.*
FROM temp t, temp_id ti
WHERE ',' || REPLACE(t.ids, ' ') || ',' LIKE '%,' || TO_CHAR(ti.data_id) || ',%'
(我在上面使用了 REPLACE()
来去除 ID 列表中多余的空格 - 让事情变得更简单。)或者,可以使用 REGEXP_LIKE()
:
SELECT ti.*
FROM temp t, temp_id ti
WHERE REGEXP_LIKE(REPLACE(t.ids, ' '), '(^|,)' || TO_CHAR(ti.data_id) || '(,|$)')
[插入符 (^
) 和美元符号 ($
) 字符分别匹配字符串的开头和结尾——因此 ID 可以匹配位于字符串的开头(以逗号或字符串结尾结尾)或以逗号开头的内容(再次以逗号或字符串结尾结尾)。]
我正在处理的项目中遇到问题,我无法为您提供实际代码,但我创建了一个 executable 示例代码,如下所示
这里temp
和temp_id
是两个table
temp
table 包含逗号分隔的 ID 列表,即VARCHAR2
temp_id
table 包含实际 ID,即NUMBER
我想通过从 temp
table
ids
来搜索 temp_id
table 中的行
//DDLs to create table
CREATE TABLE temp(ids VARCHAR2(4000));
CREATE TABLE temp_id(data_id NUMBER);
//DMLs to populate test data
INSERT INTO temp VALUES('1, 2, 3');
INSERT INTO temp_id VALUES(1);
INSERT INTO temp_id VALUES(2);
INSERT INTO temp_id VALUES(3);
INSERT INTO temp_id VALUES(4);
INSERT INTO temp_id VALUES(5);
此查询无效
SELECT * FROM temp_id WHERE data_id IN (SELECT to_number(COLUMN_VALUE) FROM XMLTABLE(SELECT ids FROM temp));
工作查询[=46=]
SELECT * FROM temp_id WHERE data_id IN (SELECT to_number(COLUMN_VALUE) FROM XMLTABLE('1, 2, 3'));
以上两个查询的区别是我在第一个查询中使用 temp
table 中的列,在第二个查询中直接引用 varchar2
。不明白为什么不工作的原因?我错过了什么吗?我认为可能存在某些数据类型不匹配但无法弄清楚。
您的要求称为 Varying IN-lists。参见 Varying IN list of values in WHERE clause
原因: IN ('1, 2, 3')
NOT 与 IN (1, 2, 3)
或 IN('1', '2', '3')
[=25 相同=]
因此,
SELECT * FROM temp_id WHERE data_id IN(SELECT ids FROM temp);
等同于
SELECT * FROM temp_id WHERE data_id IN('1, 2, 3');
这会引发 错误 ORA-01722: invalid number
-
SQL> SELECT * FROM temp_id WHERE data_id IN('1, 2, 3');
SELECT * FROM temp_id WHERE data_id IN('1, 2, 3')
*
ERROR at line 1:
ORA-01722: invalid number
SQL> SELECT * FROM temp_id WHERE data_id IN(SELECT ids FROM temp);
SELECT * FROM temp_id WHERE data_id IN(SELECT ids FROM temp)
*
ERROR at line 1:
ORA-01722: invalid number
与
不同SELECT * FROM temp_id WHERE data_id IN(1, 2, 3);
这会给你正确的输出 -
SQL> SELECT * FROM temp_id WHERE data_id IN(1, 2, 3);
DATA_ID
----------
1
2
3
解法:
根据你的要求,你可以这样实现-
SQL> SELECT * FROM temp;
IDS
--------------------------------------------------------------
1, 2, 3
SQL> SELECT * FROM temp_id;
DATA_ID
----------
1
2
3
4
5
SQL> WITH data AS
2 (SELECT to_number(trim(regexp_substr(ids, '[^,]+', 1, LEVEL))) ids
3 FROM temp
4 CONNECT BY instr(ids, ',', 1, LEVEL - 1) > 0
5 )
6 SELECT * FROM temp_id WHERE data_id IN
7 (SELECT ids FROM data
8 )
9 /
DATA_ID
----------
1
2
3
或者,您可以创建自己的TABLE 函数 或流水线函数 来实现此目的。您的目标应该是 split the comma-separated IN list into multiple rows。怎么做取决于你!
工作演示
让我们以 SCOTT
架构中的标准 EMP table 为例。
我有一个字符串形式的工作列表,我想计算这些工作的员工人数:
SQL> SET serveroutput ON
SQL> DECLARE
2 str VARCHAR2(100);
3 cnt NUMBER;
4 BEGIN
5 str := q'[CLERK,SALESMAN,ANALYST]';
6 SELECT COUNT(*) INTO cnt FROM emp WHERE JOB IN (str);
7 dbms_output.put_line('The total count is '||cnt);
8 END;
9 /
The total count is 0
PL/SQL procedure successfully completed.
哦!发生了什么?标准 emp table 应该给出输出 10。原因是 varying IN list.
让我们看看正确的做法:
SQL> SET serveroutput ON
SQL> DECLARE
2 str VARCHAR2(100);
3 cnt NUMBER;
4 BEGIN
5 str := q'[CLERK,SALESMAN,ANALYST]';
6 SELECT COUNT(*)
7 INTO cnt
8 FROM emp
9 WHERE job IN
10 (SELECT trim(regexp_substr(str, '[^,]+', 1, LEVEL))
11 FROM dual
12 CONNECT BY instr(str, ',', 1, LEVEL - 1) > 0
13 );
14 dbms_output.put_line('The total count is '||cnt);
15 END;
16 /
The total count is 10
PL/SQL procedure successfully completed.
还有另一种方法可以实现,即使用 LIKE
:
SELECT ti.*
FROM temp t, temp_id ti
WHERE ',' || REPLACE(t.ids, ' ') || ',' LIKE '%,' || TO_CHAR(ti.data_id) || ',%'
(我在上面使用了 REPLACE()
来去除 ID 列表中多余的空格 - 让事情变得更简单。)或者,可以使用 REGEXP_LIKE()
:
SELECT ti.*
FROM temp t, temp_id ti
WHERE REGEXP_LIKE(REPLACE(t.ids, ' '), '(^|,)' || TO_CHAR(ti.data_id) || '(,|$)')
[插入符 (^
) 和美元符号 ($
) 字符分别匹配字符串的开头和结尾——因此 ID 可以匹配位于字符串的开头(以逗号或字符串结尾结尾)或以逗号开头的内容(再次以逗号或字符串结尾结尾)。]