【问题标题】:PL/SQL rewrite concatenated query with 'IN' clausePL/SQL 用“IN”子句重写连接查询
【发布时间】:2013-01-31 13:29:51
【问题描述】:

目前我的 pl/sql 代码中有以下语句:

-- vList looks like '1,2,3,4'     
vStatement := 'SELECT NAME FROM T_USER WHERE ID IN ( ' || vList || ' ) '; 
Execute Immediate vStatement BULK COLLECT INTO tNames;

我认为如果不好的做法连接查询,所以我想在不使用 stings 的情况下进行此查询。有什么办法改写这个?

附:也许这里的人可以指出为什么查询串联不好,因为我没有足够的理由证明这种风格不好。

【问题讨论】:

  • 字符串的连接通常很“慢”,但除非您连接数千个字符串,否则我认为您的代码没有问题
  • @MattBusche 据我了解这是 pl/sql 代码中的常见做法?因为我有非常大的查询,这些查询通过替换表名、条件等产生串联......
  • 串联容易受到SQL注入攻击。

标签: oracle plsql where-clause dynamic-sql


【解决方案1】:

我的猜测是您之前采取了一些步骤将 vList id 转换为分隔字符串(您没有说明 vList 是如何填充的)。为什么不保留一个查询?

begin
...
select name
bulk collect into tNames
from t_user
where id in (select id from some_table where ...);
...

多次运行时的上下文切换可能会很痛苦,但对我来说最糟糕的是你盲目地接受参数输入是一个数字列表,而实际上它可能是任何东西。它可能(无辜地)是'1,2,X',你会得到一个运行时错误“invalid number”。或者更糟的是,它可能是 SQL 注入攻击。它通常是不好的做法(动态 sql 确实有它的位置),但绝对不是你使用它的方式。

试试这样的:

create or replace type t_num_tab as table of number;

create or replace procedure test_proc(i_list in t_num_tab) as
  type t_name_tab is table of varchar2(100);
  l_names t_name_tab;
begin
  -- get names
  select name
  bulk collect into l_names
  from user_table
  where id in (select * from table(i_list));

  -- do something with l_names
  dbms_output.put_line('Name count: ' || l_names.count);

end;

如果您需要比数字列表更复杂的东西,您可以创建对象类型。

【讨论】:

  • 实际上它是作为参数从外部应用程序传递给存储过程的。 ;(
  • 哇。有一些方法可以传递数据集合,不是这样。您甚至如何验证您的列表是否包含正确/预期的元素?如果我将 '1,2,A' 传递给您,并且您在 ID 为数字时动态创建查询,您将收到运行时错误“无效数字”。对不起,我不会咆哮;-)
  • 是的,如果数据无效 - 存储过程将返回错误。实际上,我很乐意将其重写为传递集合,但我不知道“如何”。
  • 您也可以使用MEMBER OF i_list 代替table 函数的子选择
  • @haki 是的,我想这取决于他想如何访问或使用 i_list,MEMBER OF 是另一个很好的例子。谢谢
【解决方案2】:

不仅仅是连接速度很慢。 plsql 中的动态查询真的很慢。以下是关于如何以及为什么这样做的好文章:

Ask Tom: How can I do a variable "in list"

【讨论】:

  • 这个问题一般称为“动态where子句”。
猜你喜欢
  • 1970-01-01
  • 2016-04-25
  • 2012-05-22
  • 1970-01-01
  • 1970-01-01
  • 2014-01-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多