我在包中有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
错误。
有人可以帮忙吗。
答案 0 :(得分: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语句,这些语句可能会产生许多其他性能问题。
答案 1 :(得分:2)
您可以使用like
代替in
搜索列表:
select *
from users_Table
where ','||l_userIds||',' like '%,'||cast(user_id as varchar2(255))||',%';
这具有简单的优点(没有附加功能或动态SQL)。但是,它确实排除了user_id
上索引的使用。对于一个小桌子,这不应该是一个问题。
答案 2 :(得分: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并将其直接用于查询。查看Tom thinks about it。
答案 3 :(得分:-1)
请勿使用此解决方案!
首先,我想删除它,但我认为,有人看到这样一个糟糕的解决方案可能会提供信息。像这样使用动态SQL会导致多个执行计划创建 - 在IN子句中每1组数据有1个执行计划,因为没有使用绑定,对于DB,每个查询都是不同的(SGA充满了许多非常类似的执行)计划,每次使用不同的参数运行查询时,在SGA中不必要地使用更多内存。
想要更正确地使用动态SQL编写另一个答案(使用绑定变量),但无论如何 Justin Cave's 答案是最好的。
您可能还想尝试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;
/
击> <击> 撞击>