使用VARCHAR2列调整查询

时间:2012-12-14 06:50:06

标签: performance oracle oracle10g

有这个存储过程构建动态查询字符串然后执行它。 sp在开发和测试环境中运行 fine ,但是客户公司的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子句中?

提前致谢。

3 个答案:

答案 0 :(得分:2)

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

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

答案 1 :(得分:1)

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

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

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

答案 2 :(得分: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命令获得的预期的那个!)很难确定,那么慢查询的执行计划和您的表结构。