针对Oracle数据库优化长时间运行的select查询

时间:2018-06-02 00:20:04

标签: sql database oracle

我不是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;

1 个答案:

答案 0 :(得分: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);

我一直在用nvl(co.type_id,0)<>3替换(co.type_id <>3 OR co.type_id IS NULL),仔细监控结果在逻辑上是一样的。

等等......