从表中选择 WHERE 子句中具有不同 IN 列表的表

Posted

技术标签:

【中文标题】从表中选择 WHERE 子句中具有不同 IN 列表的表【英文标题】:SELECT from table with Varying IN list in WHERE clause 【发布时间】:2015-02-28 13:27:19 【问题描述】:

我正在处理的项目中遇到问题,我不能给你实际的代码,但我已经创建了一个可执行的示例代码,如下所示

这里temptemp_id是两张表

    temp 表包含逗号分隔的 id 列表,即 VARCHAR2 temp_id 表包含实际 ID,即 NUMBER

我想通过idstemp 表的逗号分隔列表中获取ids 来搜索temp_id 表中的行

//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));

工作查询

SELECT * FROM temp_id WHERE data_id IN (SELECT to_number(COLUMN_VALUE) FROM XMLTABLE('1, 2, 3'));

以上两个查询之间的区别在于我在第一个查询中使用来自temp 表的列,在第二个查询中使用直接引用varchar2。不明白为什么不工作的原因?我错过了什么吗?我认为可能存在一些数据类型不匹配但无法弄清楚。

【问题讨论】:

'1, 2, 3' 不是一个有效的数字,它是一个字符串! 我做了to_number(COLUMN_VALUE)。您可以检查第二个查询是否有效。 这是一个Varying IN lists 问题。看我的回答。 修改了主题标题,使其更加明确。 【参考方案1】:

您的要求称为变化的 IN 列表。见Varying IN list of values in WHERE clause

原因: IN ('1, 2, 3') IN (1, 2, 3)IN('1', '2', '3') 相同

因此,

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 表为例。

我有一个字符串中的工作列表,我想计算这些工作的员工数:

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 表应该给出输出 10。原因是 可变 IN 列表

让我们看看正确的方法:

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.

【讨论】:

【参考方案2】:

还有另一种方法可以实现,即使用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 可以匹配字符串开头的某些内容 (以逗号结尾或字符串结尾)或以逗号开头的内容(同样,以逗号结尾或字符串结尾)。]

【讨论】:

以上是关于从表中选择 WHERE 子句中具有不同 IN 列表的表的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 join 和 where 子句从表中删除? [复制]

SQL Server 使用 in 子句从表中选择字符串

如何选择具有多个选择值的数据作为 where 子句? - Ajax Codeigniter

按确切数字选择 where 子句

sql中,In和where的区别

具有多个带有 AND 类型逻辑连接的 where IN 子句的配置单元查询