Oracle绑定变量的问题没有正确使用索引
在我的方案中,以下查询快速运行(在具有7000万行的表上0.5秒): select * from Purchases where (purchase_id = 1700656396) 而且,它甚至可以使用绑定变量快速运行: var purchase_id number := 1700656396 select * from Purchases where (purchase_id = :purchase_id) 这些运行得很快,因为我在purchase_id列上有一个索引. (继续阅读…) 我需要创建一个允许在任意列上“过滤”的查询.这意味着提供几个输入变量,并对每个输入变量进行过滤,除非它为空.这起初工作正常. 例如,以下查询也快速运行(0.5秒): select * from Purchases where (1700656396 IS NULL OR purchase_id = 1700656396) and (NULL IS NULL OR purchase_name = NULL) and (NULL IS NULL OR purchase_price = NULL) 但是,当我尝试通过绑定变量或存储过程来参数化查询时,查询会显着减慢(1.5分钟),就好像它忽略了任何索引: var purchase_id number := 1700656396 var purchase_name varchar2 := NULL var purchase_price number := NULL select * from Purchases where (:purchase_id IS NULL OR purchase_id = :purchase_id) and (:purchase_name IS NULL OR purchase_name = :purchase_name) and (:purchase_price IS NULL OR purchase_price = :purchase_price) 现在,在我的应用程序中,我被迫在运行时动态构造我的查询,以获得不错的性能.这意味着我失去了参数化查询的所有优点,并迫使我担心SQL注入. 是否有可能在保持相同逻辑的同时避免动态构造的查询? 解决方法这真的是一个更大的话题,但这是我认为最容易实现并且运行良好的方法.诀窍是使用动态SQL,但实现它以便总是传递相同数量的参数(需要),并且当你没有参数值时你允许Oracle短路(你缺少的是什么)你目前的做法).例如:set serveroutput on create or replace procedure test_param(p1 in number default null,p2 in varchar2 default null) as l_sql varchar2(4000); l_cur sys_refcursor; l_rec my_table%rowtype; l_ctr number := 0; begin l_sql := 'select * from my_table where 1=1'; if (p1 is not null) then l_sql := l_sql || ' and my_num_col = :p1'; else -- short circuit for optimizer (1=1) l_sql := l_sql || ' and (1=1 or :p1 is null)'; end if; if (p2 is not null) then l_sql := l_sql || ' and name like :p2'; else -- short circuit for optimizer (1=1) l_sql := l_sql || ' and (1=1 or :p2 is null)'; end if; -- show what the SQL query will be dbms_output.put_line(l_sql); -- note always have same param list (using) open l_cur for l_sql using p1,p2; -- could return this cursor (function),or simply print out first 10 rows here for testing loop l_ctr := l_ctr + 1; fetch l_cur into l_rec; exit when l_cur%notfound OR l_ctr > 10; dbms_output.put_line('Name is: ' || l_rec.name || ',Address is: ' || l_rec.address1); end loop; close l_cur; end; 要测试,只需运行它.例如: set serveroutput on -- using 0 param exec test_param(); -- using 1 param exec test_param(123456789); -- using 2 params exec test_param(123456789,'ABC%'); 在我的系统上,使用的表超过100毫米行,在数字字段和名称字段上有索引.几乎立即返回.另请注意,如果您不需要所有列,则可能不想执行select *,但我有点懒,并且在此示例中使用%rowtype. 希望有所帮助 (编辑:92站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |