0

有这个存储过程构建一个动态查询字符串然后执行它。sp 在开发和测试环境中运行良好,但客户公司的 DBA 已告知此查询对生产中的数据库造成了很大的影响。IT 部门已要求我们调整查询。到目前为止一切顺利,我们已经将几乎所有这些 sp 从动态构建查询字符串转移到执行非常快的单个大查询(与旧查询相比)。

我们发现(除其他外)sp 通过评估参数是否具有默认值或实际值来构建查询字符串的 where 子句,即

IF P_WORKFLOWSTATUS <> 0 THEN
    L_SQL := TRIM(L_SQL) || ' AND WORKFLOW.STATUS = ' || TO_CHAR(P_WORKFLOWSTATUS);
END IF;

所以我们优化了这个行为

WHERE
    ...
    AND (WORKFLOW.STATUS = P_WORKFLOWSTATUS OR P_WORKFLOWSTATUS = 0)

这种更改改进了影响数字列的查询,但我们发现 VARCHAR2 参数和列存在问题。当前的行为是

--CLIENT.CODE is a VARCHAR2(14) column and there is an unique index for this column.
--The data stored in this field is like 'N0002077123', 'E0006015987' and similar
IF NVL(P_CLIENT_CODE, '') <> '' THEN
    L_SQL := TRIM(L_SQL) || ' AND CLIENT.CODE = ''' || P_CLIENT_CODE || '''';
END IF;

我们试图通过这样做将其更改为我们优化的查询版本

WHERE
    ...
    AND (CLIENT.CODE = P_CLIENT_CODE OR NVL(P_CLIENT_CODE, '') = '')

但是这种变化使查询失去了性能。有没有办法优化这部分查询,或者我们应该将我们的大查询(再次)变成一个动态查询,只是为了评估是否VARCHAR2应该将这个参数添加到 where 子句中?

提前致谢。

4

3 回答 3

2

Oracle 将空字符串''视为 NULL。所以这个条件NVL(P_CLIENT_CODE, '') = ''并没有多大意义。此外,它总是错误的,因为这里我们正在检查 NULL 的相等性,这总是错误的。为此,您可能并且可能应该将查询的该部分重新编码为:

WHERE
    ...
    AND ( (CLIENT.CODE = P_CLIENT_CODE) OR (CLIENT IS NULL) )
于 2012-12-14T07:05:50.160 回答
1

我建议或将此 varchar2 参数移回动态,或使用以下内容:

WHERE
    ...
    AND CLIENT.CODE = nvl(P_CLIENT_CODE,CLIENT.CODE)

并确保您在 client.code 上有索引。(或在 client.code 上分区的表,如果可能的话。)

于 2012-12-14T07:13:46.197 回答
0

当然,正如已经说过的,您需要执行正确的空值检查。

然而,诀窍是,两者之间的区别

AND (CLIENT.CODE = P_CLIENT_CODE OR NVL(P_CLIENT_CODE, '') = '') 

AND ( (CLIENT.CODE = P_CLIENT_CODE) OR (CLIENT IS NULL) )

仅其本身不太可能导致性能问题。我什至会说带有第二个子句的查询可能比第一个子句执行得更差,因为它将为更多行产生 true,从而为后续的连接/订单/文件管理器等产生更大的结果集。

我敢打赌,将此子句添加到您的查询中会以某种方式破坏其最佳执行计划。例如,具有过时的统计信息,优化器可能会做出次优决定来选择非选择性索引client.code而不是其他可用索引。

explain plan但是,如果不查看慢查询的实际执行计划(不是您通过命令获得的预期执行计划!)和您的表结构,就很难确定。

于 2012-12-14T07:29:07.600 回答