where 条件中的可选参数 - 如何提高性能 - PL/SQL
Optional parameter in where condition - How to increase performance - PL/SQL
我有一个存储过程,它从 Json 结构中获取可选参数。如果提供 json 值,则参数将用作游标的条件。如果未提供 json 值,我不希望出现这种情况。
我已经通过在 sql 条件下使用 Coalesce/Nvl 解决了这个问题。问题是该过程使用 Nvl/Coalesce 很长时间 运行。我可以使用更有效的不同方法吗?也许动态 sql?
我的程序:
Create or Replace Procedure findaccounts
(jsonIn IN clob,
jsonOut OUT varchar2)
As
obj json := json(jsonIn);
json_obj_out json;
json_lst json_list := json_list();
--Getstring is a function that sets variable to null if json value is not found
istatus varchar2(10) := GetString(obj json, 'status');
icreatedate := GetString(obj json, 'daysold');
iage int := GetString(obj json, 'age');
irownum int := GetString(obj json, 'rownum');
Begin
For rec in (Select A.accountnumber
From Accounts A
Inner Join Accountowner Ao On A.ownerId = Ao.Id
Where A.Status = iStatus
And A.daysold >= Coalesce(idaysold,A.daysold)
And Ao.Age = Coalesce(iAge,Ao.Age)
And rownum <= Coalesce(iRownum,5))
loop
obj := json();
obj.put('accountnumber',Rec.accountnumber);
json_lst.append(obj.to_json_value);
end loop;
json_lst.print;
jsonOut := json_lst.to_char();
End;
已解决:
动态提升性能 sql。具有动态绑定变量的选项已通过 dbms_sql 包解决,因为立即执行没有此选项。
您可以像这样消除 COALESCE()
函数:
And ( idaysold IS NULL OR A.daysold >= idaysold )
And ( iAge IS NULL OR Ao.Age = iAge )
And ( ( iRownum IS NULL AND ROWNUM <= 5 ) OR rownum <= iRownum )
在功能上,Coalesce
是处理可选搜索条件的好方法。
性能方面它并不好。优化器将无法有效优化,因为它只决定一次执行计划,优化搜索条件,前提是第一次 SQL 是 运行。
如果需要不同的执行计划才能获得可接受的性能,那么您需要对这些不同的搜索模式使用不同的 SQL 语句。
这是我过去使用过的两种不同的解决方案:
创建几个不同的 SQL:s,其中每个处理共享相同执行计划的可能搜索条件的子集。在您的情况下,您可以在 iAge
为 null
时使用一个,在 idaysold
为 null
时使用另一个,在 null
.[=16 都不是时使用第三个=]
使用动态 SQL。
NVL
通常是按可选参数过滤的最有效方法。它的性能通常优于 COALESCE
、OR
、CASE
、DECODE
和其他类似解决方案。
切换回该方法,如果效果不佳,则可能存在其他一些潜在问题。在此处找到声明的解释计划或 SQL 监控报告,并 post 以获得进一步的建议。
NVL
通常效果最好,因为它最有可能创建 CONCATENATION
和 FILTER
操作。 (请注意,FILTER
操作与解释计划底部的 Filter
部分 不同 。)这些操作允许 Oracle 创建两个不同的执行计划和根据绑定变量,在 运行 时间选择一个。
理想情况下,解释计划应如下所示:
----------------------------------------------------
| Id | Operation | Name |
----------------------------------------------------
| 0 | SELECT STATEMENT | |
| 1 | CONCATENATION | |
| 2 | FILTER | |
| 3 | TABLE ACCESS FULL | MYTABLE |
| 4 | FILTER | |
| 5 | TABLE ACCESS BY INDEX ROWID| MYTABLE |
| 6 | INDEX UNIQUE SCAN | MYTABLE_PK |
----------------------------------------------------
请参阅我的回答 here 以获取演示 NVL
如何工作的测试脚本。
我认为您的代码可能存在两个不幸的问题。没有 NVL
它不会生成单独的执行计划。使用 NVL
它可能会生成两个计划,但也许它们都不好。如果 NVL
不会生成单独的计划,那么我建议您查看 Klas Lindbäck 的回答。
我有一个存储过程,它从 Json 结构中获取可选参数。如果提供 json 值,则参数将用作游标的条件。如果未提供 json 值,我不希望出现这种情况。 我已经通过在 sql 条件下使用 Coalesce/Nvl 解决了这个问题。问题是该过程使用 Nvl/Coalesce 很长时间 运行。我可以使用更有效的不同方法吗?也许动态 sql?
我的程序:
Create or Replace Procedure findaccounts
(jsonIn IN clob,
jsonOut OUT varchar2)
As
obj json := json(jsonIn);
json_obj_out json;
json_lst json_list := json_list();
--Getstring is a function that sets variable to null if json value is not found
istatus varchar2(10) := GetString(obj json, 'status');
icreatedate := GetString(obj json, 'daysold');
iage int := GetString(obj json, 'age');
irownum int := GetString(obj json, 'rownum');
Begin
For rec in (Select A.accountnumber
From Accounts A
Inner Join Accountowner Ao On A.ownerId = Ao.Id
Where A.Status = iStatus
And A.daysold >= Coalesce(idaysold,A.daysold)
And Ao.Age = Coalesce(iAge,Ao.Age)
And rownum <= Coalesce(iRownum,5))
loop
obj := json();
obj.put('accountnumber',Rec.accountnumber);
json_lst.append(obj.to_json_value);
end loop;
json_lst.print;
jsonOut := json_lst.to_char();
End;
已解决:
动态提升性能 sql。具有动态绑定变量的选项已通过 dbms_sql 包解决,因为立即执行没有此选项。
您可以像这样消除 COALESCE()
函数:
And ( idaysold IS NULL OR A.daysold >= idaysold )
And ( iAge IS NULL OR Ao.Age = iAge )
And ( ( iRownum IS NULL AND ROWNUM <= 5 ) OR rownum <= iRownum )
在功能上,Coalesce
是处理可选搜索条件的好方法。
性能方面它并不好。优化器将无法有效优化,因为它只决定一次执行计划,优化搜索条件,前提是第一次 SQL 是 运行。
如果需要不同的执行计划才能获得可接受的性能,那么您需要对这些不同的搜索模式使用不同的 SQL 语句。
这是我过去使用过的两种不同的解决方案:
创建几个不同的 SQL:s,其中每个处理共享相同执行计划的可能搜索条件的子集。在您的情况下,您可以在
iAge
为null
时使用一个,在idaysold
为null
时使用另一个,在null
.[=16 都不是时使用第三个=]使用动态 SQL。
NVL
通常是按可选参数过滤的最有效方法。它的性能通常优于 COALESCE
、OR
、CASE
、DECODE
和其他类似解决方案。
切换回该方法,如果效果不佳,则可能存在其他一些潜在问题。在此处找到声明的解释计划或 SQL 监控报告,并 post 以获得进一步的建议。
NVL
通常效果最好,因为它最有可能创建 CONCATENATION
和 FILTER
操作。 (请注意,FILTER
操作与解释计划底部的 Filter
部分 不同 。)这些操作允许 Oracle 创建两个不同的执行计划和根据绑定变量,在 运行 时间选择一个。
理想情况下,解释计划应如下所示:
----------------------------------------------------
| Id | Operation | Name |
----------------------------------------------------
| 0 | SELECT STATEMENT | |
| 1 | CONCATENATION | |
| 2 | FILTER | |
| 3 | TABLE ACCESS FULL | MYTABLE |
| 4 | FILTER | |
| 5 | TABLE ACCESS BY INDEX ROWID| MYTABLE |
| 6 | INDEX UNIQUE SCAN | MYTABLE_PK |
----------------------------------------------------
请参阅我的回答 here 以获取演示 NVL
如何工作的测试脚本。
我认为您的代码可能存在两个不幸的问题。没有 NVL
它不会生成单独的执行计划。使用 NVL
它可能会生成两个计划,但也许它们都不好。如果 NVL
不会生成单独的计划,那么我建议您查看 Klas Lindbäck 的回答。