where 条件中的可选参数 - 如何提高性能 - PL/SQL

Posted

技术标签:

【中文标题】where 条件中的可选参数 - 如何提高性能 - PL/SQL【英文标题】:Optional parameter in where condition - How to increase performance - PL/SQL 【发布时间】:2017-02-03 13:09:04 【问题描述】:

我有一个从 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 包解决,因为立即执行没有此选项。

【问题讨论】:

【参考方案1】:

从功能上讲,Coalesce 是处理可选搜索条件的好方法。 从性能上来说它并不好。优化器将无法有效优化,因为它只决定执行计划一次,针对 SQL 首次运行时提供的搜索条件进行优化。

如果需要不同的执行计划来获得可接受的性能,那么您需要针对这些不同的搜索模式使用不同的 SQL 语句。

这是我过去使用过的两种不同的解决方案:

    创建几个不同的 SQL:s,每个 SQL:s 处理将共享相同执行计划的可能搜索条件的子集。在您的情况下,当iAgenull 时,您可以使用一个,当idaysoldnull 时使用另一个,当null 两者都不是时使用第三个。

    使用动态 SQL。

【讨论】:

谢谢。通过 dbms_sql 解决的问题。我找不到通过立即执行来获得可选绑定变量的方法。【参考方案2】:

您可以像这样消除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 )

【讨论】:

WHERE 子句中使用函数可能意味着使用基于函数的索引(或无索引)而不是列索引。在这个版本中,没有函数,应该使用列索引(如果存在)——OP 可以在两个查询上运行EXPLAIN PLAN,看看使用的索引是否存在差异。 我认为 Oracle 优化器会查看 coalesce/nvl 并在将其交给优化器之前对其进行扩展。 这将对性能造成巨大影响。动态 SQL 或表函数似乎是多个参数的唯一方法【参考方案3】:

NVL 通常是通过可选参数进行过滤的最有效方法。它的性能通常优于COALESCEORCASEDECODE 和其他类似解决方案。

切换回该方法,如果效果不佳,则可能存在其他一些潜在问题。找到该语句的解释计划或 SQL 监控报告,并在此处发布以获取更多建议。

NVL 通常效果最好,因为它最有可能创建CONCATENATIONFILTER 操作。 (请注意,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,它可能会生成两个计划,但也许它们都不好。 Ff 即使NVL 也不会生成单独的计划,那么我建议您查看 Klas Lindbäck 的答案。

【讨论】:

以上是关于where 条件中的可选参数 - 如何提高性能 - PL/SQL的主要内容,如果未能解决你的问题,请参考以下文章

游标 where 子句中的可选参数

TypeORM queryBuilder 中的可选参数

SQLAlchemy中的可选参数

SQL Server 存储过程中的可选参数?

如何匹配url中的可选参数? [复制]

在 C# linq 中结合多个具有可选参数的 where 条件?