如何在选择语句的“NOT IN”子句中使用逗号分隔的字符串列表作为 pl/sql 存储的函数参数

Posted

技术标签:

【中文标题】如何在选择语句的“NOT IN”子句中使用逗号分隔的字符串列表作为 pl/sql 存储的函数参数【英文标题】:How to use a comma-separated list of strings as pl/sql stored function parameter inside a "NOT IN" clause of a select statement 【发布时间】:2013-01-04 08:48:17 【问题描述】:

我有一个逗号分隔的字符串列表(来自用户输入),我想将此列表用作嵌套 sql 块中的 pl/sql 存储函数中的参数,使用“not in where 子句” .

我找不到一种优雅的方式让它工作......

这就是我的想法:

CREATE TABLE example ( somevalue VARCHAR(36) NOT NULL);
--
INSERT INTO example VALUES ('value1');
INSERT INTO example VALUES ('value2');
INSERT INTO example VALUES ('value3');
--
SELECT * FROM example;
--
CREATE OR REPLACE
  FUNCTION resultmaker(
      ignoreList IN VARCHAR2)
    RETURN VARCHAR2
  IS
    result VARCHAR2(4000);
  BEGIN
    result  := 'Here is my calculated result, using ignorelist=' || ignoreList || ':'     || CHR(10);
    FOR rec IN
    (SELECT DISTINCT somevalue
    FROM example
    WHERE somevalue NOT IN resultmaker.ignoreList -- here's my issue, the NOT IN     clause using the parameter value
    )
    LOOP
      result := result || 'not in ignorelist: ' || rec.somevalue || CHR(10);
    END LOOP;
    result := result || '.' || CHR(10);
    --
    RETURN result;
  END resultmaker;
  /
--
-- simulate function call with user input 'value2, value3'
SELECT resultmaker('value2, value3') FROM dual; -- doesn't work  
--
DROP TABLE example;
DROP FUNCTION resultmaker;

【问题讨论】:

How to convert varchar to numbers in sql developer的可能重复 谢谢,类似的问题!我的基本问题是不了解动态 sql。 【参考方案1】:

只需传递'"value2","value3"' 之类的参数,并让您的语句将双引号替换为REPLACE(@Param1,'"','''') 之类的单引号。

函数调用:SELECT * FROM Function1('"value2","value3"')

内部函数:NOT IN REPLACE(@Param1,'"','''')

【讨论】:

【参考方案2】:

在任何情况下,您都应该解析该输入。由于 PL/SQL 中没有内置的字符串标记器(至少我找不到它)你可能想看看这些选项,

http://blog.tanelpoder.com/2007/06/20/my-version-of-sql-string-to-table-tokenizer/

Does PL/SQL have an equivalent StringTokenizer to Java's?

解析字符串后,您可以创建一个新字符串,如:

not_in_statement varchar2(1000);
CURSOR c1 IS select token from tokenized_strings_table;
BEGIN
    not_in_statement := '('
    FOR rec IN c1 LOOP
        not_in_statement := not_in_statement || '''||rec.token||'''||','
    END LOOP
    not_in_statement := not_in_statement||')'
END

SELECT DISTINCT somevalue
FROM example
WHERE somevalue NOT IN not_in_statement

你可能需要动态SQL,我没时间尝试。

【讨论】:

感谢坎布拉克的回答!将我指向“动态 sql”是关键。我可以控制 pl/sql 之外的逗号分隔列表的表示,所以使用动态 sql 的解决方案对我来说很好。 我会尽快发布我的新解决方案作为答案(我目前没有足够的声誉,必须等待 8 小时)【参考方案3】:

这是我对上述原始问题使用动态 sql 的解决方案:

CREATE TABLE example ( somevalue VARCHAR(36) NOT NULL);
--
INSERT INTO example VALUES ('value1');
INSERT INTO example VALUES ('value2');
INSERT INTO example VALUES ('value3');
--
SELECT * FROM example;
--
CREATE OR REPLACE
  FUNCTION resultmaker(
      ignoreList IN VARCHAR2)
    RETURN VARCHAR2
  IS
    result VARCHAR2(4000);
    example_cursor sys_refcursor;
    rec example.somevalue%type;
  BEGIN
    result := 'Here is my calculated result, using ignorelist=' || ignoreList || ':' || CHR(10);
    OPEN example_cursor FOR ( 'SELECT DISTINCT somevalue FROM example WHERE somevalue NOT IN (' || ignoreList || ')' );
    FETCH example_cursor INTO rec;
    WHILE example_cursor%found
    LOOP
      result := result || 'not in ignorelist: ' || rec || CHR(10);
      FETCH example_cursor INTO rec;
    END LOOP;
    CLOSE example_cursor;
    result := result || '.' || CHR(10);
    --
    RETURN result;
  END resultmaker; 
/
--
-- simulate function call with user input 'value2', 'value3'
SELECT resultmaker('''value2'', ''value3''') FROM dual;
--
DROP TABLE example;
DROP FUNCTION resultmaker;

【讨论】:

【参考方案4】:

经典且可能正确的解决方案是使用 PL/SQL 表将其作为参数传递...

【讨论】:

【参考方案5】:

在 asktom.oracle.com 上有一些很好的解决方案,关于获取一串值并为它们动态创建一个 IN 子句:

http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:210612357425

【讨论】:

以上是关于如何在选择语句的“NOT IN”子句中使用逗号分隔的字符串列表作为 pl/sql 存储的函数参数的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 NOT IN 子句添加超过 1000 个值

如何在“PARTITIONED BY”子句的括号之间以逗号分隔的tick中提取值

重写 NOT IN,但子查询涉及逗号分隔的字符串 (ID)

SQL IN 逗号分隔参数与内部查询

SQL语句中 NOT IN 子句的“正确打开方式”

在主查询的“IN”子句中使用逗号分隔列表的子查询结果