如何使用大量和语句优化 Oracle 查询

Posted

技术标签:

【中文标题】如何使用大量和语句优化 Oracle 查询【英文标题】:How to optimize Oracle Query with lots of and statements 【发布时间】:2019-08-19 11:44:40 【问题描述】:

我正在使用 Select 查询中的多个 and 语句从 oracle 表中选择数据。另外我正在使用 Like 语句。问题是当我使用大表时,执行查询需要花费太多时间。如何更改以下查询的某些部分。

SELECT t.co_filial as fil_code, t.emp_birth as emp_code, to_char(t.curr_day, 'YYYY-MM-DD') as operation_date, 
TRUNC(t.Sum_Pay/100) As summa
FROM operation_history t 
WHERE Substr(t.Co_Acc, 8) LIKE '12294%' And Substr(t.Co_Acc, -3) > 599 And Substr(t.Co_Acc, -3) != 683 And Substr(t.Co_Acc, -3) < 696
AND t.state_id = 41
And t.curr_day >= to_date('12.08.2019', 'DD.MM.YYYY')
And t.curr_day <  to_date('13.08.2019', 'DD.MM.YYYY')

【问题讨论】:

任何解释计划向我们展示? 为什么是Substr(t.Co_Acc, 8) LIKE '12294%' 而不是Substr(t.Co_Acc, 8, 5) = '12294' 为了优化查询,我们需要知道表结构,例如指标 - 你也可以提供吗? 你可以用trunc(t.curr_day) = to_date('12.08.2019', 'DD.MM.YYYY')代替t.curr_day &gt;= to_date('12.08.2019', 'DD.MM.YYYY') And t.curr_day &lt; to_date('13.08.2019', 'DD.MM.YYYY') 可能更多的是添加有用索引然后重写查询的问题。但要进行优化,我们还需要知道查询的哪些值是固定的,哪些会被更改,因此 Substr(t.Co_Acc, 8, 5) = '12294'trunc(t.curr_day) = to_date('12.08.2019', 'DD.MM.YYYY') 仅在某些情况下才有意义。 【参考方案1】:

一个明显的改进是:Substr 获取最后 3 个字符的字符串可以使用一次,而不是 3 次,并在子查询中使用如下。

SELECT fil_code,
  emp_code,
  operation_date,
  summa
From
  (SELECT t.co_filial as fil_code, 
     t.emp_birth as emp_code, 
     to_char(t.curr_day, 'YYYY-MM-DD') as operation_date, 
     TRUNC(t.Sum_Pay/100) As summa,
     Substr(t.Co_Acc, -3) AS SUBSTR_3 -- ADDED THIS
   FROM operation_history t 
  WHERE Substr(t.Co_Acc, 8, 5) = '12294' -- used direct equals operator 
  --And Substr(t.Co_Acc, -3) > 599 
  --And Substr(t.Co_Acc, -3) != 683 
  --And Substr(t.Co_Acc, -3) < 696
  AND t.state_id = 41
  And t.curr_day >= to_date('12.08.2019', 'DD.MM.YYYY')
  And t.curr_day <  to_date('13.08.2019', 'DD.MM.YYYY'))
-- added following where clause
WHERE SUBSTR_3 BETWEEN 600 AND 695
AND SUBSTR_3 != 683

我使用了between,它包含上限和下限,因此从各自的限制中添加和减去 1。

干杯!!

【讨论】:

另外,使用 Equals 代替 LIKE @Tejash 只是一个小的复制错误:应该是 Substr(t.Co_Acc, 8, 5),因为在原始查询中起点是 8... @Tejash 如果可能的话,你能否用列名而不是点来更新查询,因为它在子查询中给出错误,oracle 看不到 t.Sum_Pay。 @AbdusoliErgashev - 所以您要求某人修复您的语法错误,即使他们无权访问您的架构,因此无法运行此代码而您这样做?此外,修复您尚未发布的代码中的语法错误。 是的,APC 是对的。无论如何,我已经解决了你的问题。请查看我的更新答案【参考方案2】:

对于这个查询(我已经清理了一点):

SELECT t.co_filial as fil_code, t.emp_birth as emp_code, to_char(t.curr_day, 'YYYY-MM-DD') as operation_date, 
        TRUNC(t.Sum_Pay/100) As summa
FROM operation_history t 
WHERE Substr(t.Co_Acc, 8) LIKE '45294%' And Substr(t.Co_Acc, -3) > 599 And
      Substr(t.Co_Acc, -3) <> 683 And
      Substr(t.Co_Acc, -3) < 696 AND 
      t.state_id = 41 And
      t.curr_day >= date '2019-08-12' and
      t.curr_day <  date '2019-08-13';

为了加快运行速度,您希望在WHERE 子句上使用索引。这可能是唯一会带来显着改善的事情。

我会推荐一个关于operation_history(state_id, curr_date, Substr(t.Co_Acc, 8)) 的索引。

因为您只在结果中寻找一天,所以您还可以做一件事:

SELECT t.co_filial as fil_code, t.emp_birth as emp_code, to_char(t.curr_day, 'YYYY-MM-DD') as operation_date, 
        TRUNC(t.Sum_Pay/100) As summa
FROM operation_history t 
WHERE Substr(t.Co_Acc, 8) LIKE '45294%' and
      Substr(t.Co_Acc, -3) > 599 and
      Substr(t.Co_Acc, -3) <> 683 and
      Substr(t.Co_Acc, -3) < 696 and 
      t.state_id = 41 and
      trunc(t.curr_day) = date '2019-08-12';

那么,你想要的索引在operation_history(state_id, trunc(curr_date), Substr(t.Co_Acc, 8))上。

【讨论】:

正如我在 cmets 中所说,构建一个基于函数的索引仅用于支持此查询可能值得维护它的开销。但是我们对这个查询的运行频率、表的写入频率以及查询的重要性一无所知。更不用说为什么数据模型这么坏了。 @APC 。 . .正确的。我们只知道 OP 询问了优化 this 查询。【参考方案3】:

一些建议:

1) 不是动态计算 operation_date substr_3 和 summa 字段,而是创建 3 个生成(计算)列,每个表达式一个。这些将在数据库中预先计算,您只需选择计算的列。快多了。列应该是持久的(不是虚拟的)

https://oracle-base.com/articles/11g/virtual-columns-11gr1

2) 检查是否可以对这些 substr 表达式执行相同操作

3) 为您在 2) 中创建的计算列创建索引(如果有)

4) 为 stateid 和 curr_day 创建索引

问候

【讨论】:

以上是关于如何使用大量和语句优化 Oracle 查询的主要内容,如果未能解决你的问题,请参考以下文章

Oracle大量数据查询优化

ORACLE中这个运行4秒左右的SQL语句如何优化?我想查询少用点时间

oracle速度变慢,怎样解决

如何对Oracle sql 进行性能优化的调整

索引优化原则及Oracle中索引总结

Oracle创建索引SQL简单的例子,在表中的指定字段和如何使用索引呢?