in()运算符的局限性是万恶之源。
它适用于琐碎的情况,您可以通过“自动生成预处理语句”来扩展它,但是它总是有其局限性。
- 如果要创建带有可变数量参数的语句,则将在每次调用时进行sql解析开销
- 在许多平台上,in()运算符的参数数量受到限制
- 在所有平台上,SQL文本的总大小是有限的,因此无法为in params发送2000个占位符
- 不能向下发送1000-10k的绑定变量,因为JDBC驱动程序有其局限性
在某些情况下,in()方法可能就足够了,但不能证明是火箭的:)
防止火箭攻击的解决方案是在一个单独的调用中传递任意数量的参数(例如,通过传递参数块),然后使用视图(或任何其他方式)在SQL中表示它们并在您的where中使用标准。
这里是蛮力变体,网址为http://tkyte.blogspot.hu/2006/06/varying-in-lists.html
但是,如果可以使用PL / SQL,则此混乱情况会变得非常整洁。
function getCustomers(in_customerIdList clob) return sys_refcursor is
begin
aux_in_list.parse(in_customerIdList);
open res for
select *
from customer c,
in_list v
where c.customer_id=v.token;
return res;
end;
然后,您可以在参数中传递任意数量的逗号分隔的客户ID,并且:
- 由于select的SQL是稳定的,因此不会获得解析延迟
- 没有管道函数的复杂性-这只是一个查询
- SQL使用简单的联接,而不是IN运算符,这非常快
- 毕竟,这是一个很好的经验法则不使用任何普通选择或DML来访问数据库,因为它是Oracle,比MySQL或类似的简单数据库引擎提供了光年。PL / SQL允许您有效地从应用程序域模型中隐藏存储模型。
这里的窍门是:
- 我们需要一个接受长字符串的调用,并将其存储在db会话可以访问它的地方(例如,简单的包变量或dbms_session.set_context)
- 那么我们需要一个可以将其解析为行的视图
- 然后,您将拥有一个包含要查询的ID的视图,因此您所需要的只是对查询表的简单连接。
该视图如下所示:
create or replace view in_list
as
select
trim( substr (txt,
instr (txt, ',', 1, level ) + 1,
instr (txt, ',', 1, level+1)
- instr (txt, ',', 1, level) -1 ) ) as token
from (select ','||aux_in_list.getpayload||',' txt from dual)
connect by level <= length(aux_in_list.getpayload)-length(replace(aux_in_list.getpayload,',',''))+1
其中aux_in_list.getpayload指向原始输入字符串。
一种可能的方法是传递pl / sql数组(仅由Oracle支持),但是您不能在纯SQL中使用它们,因此始终需要转换步骤。转换无法在SQL中完成,因此,毕竟,将带有所有参数的clob传递给字符串并在视图中进行转换是最有效的解决方案。