【问题标题】:Optimize long running select query against Oracle database针对 Oracle 数据库优化长时间运行的选择查询
【发布时间】:2018-06-02 15:07:06
【问题描述】:

我不是 DBA 专家,我们有一个现有的 Oracle 查询来提取特定日期的数据,我们遇到的问题是如果一天的业务量非常大,查询需要 8 多个小时并且超时.我们无法在数据库本身内部进行优化,那么我们通常如何处理这种极端情况呢?我粘贴了下面的查询,内容被屏蔽以显示 SQL 结构,寻找有关如何优化此查询或任何替代方法以避免超时的建议。

WHENEVER SQLERROR EXIT 1
SET LINESIZE 9999
SET ECHO OFF
SET FEEDBACK OFF
SET PAGESIZE 0
SET HEADING OFF
SET TRIMSPOOL ON
SET COLSEP ","

SELECT co.cid
  || ',' || DECODE(co.cid,'xxxxx','xxxx',null,'N/A','xxxxx')
  || ',' || d.name
  || ',' || ti.rc
  || ',' || DECODE(cf.side_id,1,'x',2,'xx',5,'xx','')
  || ',' || cf.Quantity
  || ',' || cf.price
  || ',' || TO_CHAR(time,'YYYY-mm-dd hh24:mi:ss')
  || ',' || DECODE(co.capacity_id,1,'xxxx',2,'xxxx','')
  || ',' || co.type
  || ',' || cf.id
  || ',' || CASE
              WHEN (cf.account_id = xxx OR cf.account_id = xxx) THEN SUBSTR(cf.tag, 1, INSTR(cf.tag, '.')-1) || '_' || ti.ric || '_' || DECODE(cf.side_id,1,'xx',2,'xx',5,'xx','')
              WHEN INSTR(cf.clientorder_id, '#') > 0 THEN SUBSTR(cf.clientorder_id, 1, INSTR(cf.clientorder_id, '#')-1)
              ELSE cf.clientorder_id
            END
  || ',' || co.tag
  || ',' || t.description
  || ',' || CASE
              WHEN cf.id = xxx THEN 'xxxx'
              ELSE (SELECT t.name FROM taccount t WHERE t.account_id = cf.account_id)
            END as Account
FROM clientf cf, tins ti, thistory co, tdk d, tra t
WHERE cf.sessiondate = TO_DATE('xxxxxx','YYYYMMDD')
AND cf.orderhistory_id = co.orderhistory_id
AND cf.reporttype_id = 1
AND ti.inst_id = cf.inst_id
AND (ti.rc LIKE '%.xx' or ti.rc LIKE '%.xx' or ti.rc LIKE '%.xx' )
AND d.de_id = t.de_id
AND t.tr_id = co.tr_id
AND nvl(co.type_id,0) <> 3
AND cf.trid not in (SELECT v2.pid FROM port v2 WHERE v2.sessiondate = cf.sessiondate AND v2.exec_id = 4)
ORDER BY co.cid, time, cf.quantity;

【问题讨论】:

  • “我们无法在数据库本身内部进行优化” ...如果您不能这样做,则很难优化查询。唯一让我印象深刻的是AND (ti.rc LIKE '%.xx' or ti.rc LIKE '%.xx' or ti.rc LIKE '%.xx' ) 这一行,因为它显然不使用索引,但很难说它最终是否重要。
  • 你制定了解释计划吗?
  • 你有一个相关的子查询..AND cf.trid not in (SELECT v2.pid FROM port v2 ..这将被重复执行。尝试使用端口上的临时表删除它(过滤掉 exec_id)或尝试以某种方式在连接中包含端口。
  • 如果没有解释计划,我们将只是猜测。请出示解释计划。
  • +1 请求解释计划(或者,更好的是,请参阅:stackoverflow.com/a/50605440/5174436 了解如何使用 DBMS_XPLAN 了解更多详细信息)。同时,CLIENTF(reportype_id,sessiondate)上是否有索引?

标签: sql database oracle


【解决方案1】:

我会首先与需要此查询输出的人交谈,并询问他们有关报告和每个单独列的信息。有时,不再需要某些列,有时是整个报告。 8 小时以上的运行时间是一个很好的讨价还价点 ;-)

接下来,我会将原始查询放在一边,然后一点一点地从头开始构建一个测试查询,例如从 clientf 开始,获取 WHERE 子句中的所有列:

SELECT *
  FROM clientf SAMPLE (0.1) cf 
 WHERE cf.sessiondate = TO_DATE('xxxxxx','YYYYMMDD')
   AND cf.reporttype_id = 1;

如果没问题,我会将样本量增加到 99%。如果运行时已经很长,您可能会建议在 clientf.sessiondate 上建立一个索引(或者可能在 clientf.reporttype_id 上,但这不太可能有帮助,因为它看起来有太少的不同值)。

完成后,我将加入第一个表:

SELECT *
  FROM clientf SAMPLE (0.1) cf 
 WHERE cf.sessiondate = TO_DATE('xxxxxx','YYYYMMDD')
   AND cf.reporttype_id = 1
   AND cf.trid NOT IN (SELECT v2.pid 
                         FROM port v2 
                        WHERE v2.sessiondate = cf.sessiondate 
                          AND v2.exec_id = 4);

我会比较 NOT INWHERE NOT EXISTS,没想到会有太大差异。

然后我会加入下一个表(个人更喜欢 ANSI 语法),再次从一个小样本开始,再次将它的列添加到 where 子句:

SELECT *
  FROM clientf SAMPLE (0.1) cf 
  FROM thistory co 
    ON cf.orderhistory_id = co.orderhistory_id
 WHERE cf.sessiondate = TO_DATE('xxxxxx','YYYYMMDD')
   AND cf.reporttype_id = 1
   AND nvl(co.type_id,0) <> 3
   AND cf.trid NOT IN (SELECT v2.pid 
                         FROM port v2 
                        WHERE v2.sessiondate = cf.sessiondate 
                          AND v2.exec_id = 4);

我会尝试用(co.type_id &lt;&gt;3 OR co.type_id IS NULL) 替换nvl(co.type_id,0)&lt;&gt;3,仔细监控结果是否在逻辑上相同。

等等……

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-09-13
    • 2023-03-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-01-12
    相关资源
    最近更新 更多