2

我在一个包中有 2 个程序。我正在调用一个程序来获取以逗号分隔的用户 ID 列表。

我将结果存储在一个VARCHAR变量中。现在,当我使用这个逗号分隔的列表放入一个IN子句时,它会抛出“ORA-01722:INVALID NUMBER"异常。

这就是我的变量的样子

l_userIds VARCHAR2(4000) := null;

这是我分配价值的地方

l_userIds := getUserIds(deptId);  -- this returns a comma separated list

我的第二个查询就像 -

select * from users_Table where user_id in (l_userIds);

如果我运行这个查询,我会得到INVALID NUMBER错误。

有人可以在这里帮忙。

4

4 回答 4

8

你真的需要返回一个逗号分隔的列表吗?声明一个集合类型通常会好得多

CREATE TYPE num_table
    AS TABLE OF NUMBER;

声明一个返回此集合实例的函数

CREATE OR REPLACE FUNCTION get_nums
  RETURN num_table
IS
  l_nums num_table := num_table();
BEGIN
  for i in 1 .. 10
  loop
    l_nums.extend;
    l_nums(i) := i*2;
  end loop;
END;

然后在查询中使用该集合

SELECT *
  FROM users_table
 WHERE user_id IN (SELECT * FROM TABLE( l_nums ));

也可以使用动态 SQL(@Sebas 演示)。然而,不利的一面是,对过程的每次调用都会生成一个新的 SQL 语句,在执行之前需要再次对其进行解析。它还对库缓存施加压力,这可能导致 Oracle 清除许多其他可重用的 SQL 语句,这可能会产生许多其他性能问题。

于 2013-08-02T00:41:18.613 回答
3

您可以使用like而不是搜索列表in

select *
from users_Table
where ','||l_userIds||',' like '%,'||cast(user_id as varchar2(255))||',%';

这具有简单的优点(没有附加功能或动态 SQL)。但是,它确实排除了在user_id. 对于一张小桌子,这应该不是问题。

于 2013-08-02T01:17:51.737 回答
1

问题是 oracle 不会将您传递的 VARCHAR2 字符串解释为数字序列,它只是一个字符串。

一种解决方案是将整个查询变成一个字符串 (VARCHAR2),然后执行它,以便引擎知道他必须翻译内容:

DECLARE
    TYPE T_UT IS TABLE OF users_Table%ROWTYPE;
    aVar T_UT;
BEGIN
    EXECUTE IMMEDIATE 'select * from users_Table where user_id in (' || l_userIds || ')' INTO aVar;
...

END;

一个更复杂但也更优雅的解决方案是将字符串拆分为表 TYPE 并将其直接用于查询。看看汤姆是怎么想的

于 2013-08-02T00:36:55.307 回答
-1

不要使用这个解决方案!

首先,我想删除它,但我认为,如果有人看到这样一个糟糕的解决方案,它可能会提供有用的信息。像这样使用动态 SQL 会导致创建多个执行计划 - IN 子句中每 1 组数据有 1 个执行计划,因为没有使用绑定,并且对于 DB,每个查询都是不同的(SGA 充满了许多非常相似的执行计划,每次使用不同的参数运行查询时,SGA 中都会不必要地使用更多内存)。

想要更正确地使用动态 SQL 编写另一个答案(使用绑定变量),但无论如何,贾斯汀洞穴的答案是最好的。

您可能还想尝试 REF CURSOR (我自己还没有尝试过确切的代码,可能需要一些小调整):

DECLARE
    deptId                  NUMBER := 2;
    l_userIds               VARCHAR2(2000) := getUserIds(deptId);
    TYPE t_my_ref_cursor IS REF CURSOR;
    c_cursor                t_my_ref_cursor;
    l_row                   users_Table%ROWTYPE;
    l_query                 VARCHAR2(5000);
BEGIN
    l_query := 'SELECT * FROM users_Table WHERE user_id IN ('|| l_userIds ||')';
    OPEN c_cursor FOR l_query;

    FETCH c_cursor INTO l_row;
    WHILE c_cursor%FOUND
    LOOP
        -- do something with your row
        FETCH c_cursor INTO l_row;
    END LOOP;

END;
/

于 2015-09-04T15:23:49.660 回答