带子查询的OR子句占用太多时间

时间:2018-04-03 08:26:39

标签: sql oracle oracle11g query-optimization

日期范围查询花费了太多时间。 只是我删除了一个条件然后它工作正常需要2秒。如果加入 然后30秒。

SELECT UserName,COUNT ('t') TOTAL
FROM TRANSACTIONS E1
WHERE E1.START_DATE BETWEEN TO_DATE ('20130101', 'YYYYMMDD') AND TO_DATE ('20140101', 'YYYYMMDD')
AND 
( 
    TO_CHAR (E1.START_DATE, 'D') in ( 7) 
    OR Exists(SELECT 1 FROM HOLIDAYS TT
    WHERE E1.START_DATE BETWEEN TT.DATE_FROM AND TT.DATE_TO )
)
AND EXISTS (SELECT 't' FROM TRANSACTIONS_ORG E2 WHERE E1.TRANTYPE = E2.tran_type)
GROUP BY UserName;

HOLIDAYS表

Id  FromDate    ToDate  Description
1   1-Feb-11    3-Feb-11    Maintance
3   2-Sep-09    5-Sep-09    Eid Holiday Fine Block
4   3-Dec-09    4-Dec-09    Due to System Problem
5   4-Dec-07    04-Dec-07   National Day

EIDTED 我发现问题不在日期范围内。但是

中的 OR 子句
TO_CHAR (E1.START_DATE, 'D') in ( 5,6) 
OR 
Exists(SELECT 1 FROM HOLIDAYS TT
WHERE E1.START_DATE BETWEEN TT.DATE_FROM AND TT.DATE_TO )

如果删除并将 AND 设置为罚款,如果与OR的随机条件仍然存在相同的问题。

1 个答案:

答案 0 :(得分:3)

OR <subquery>构造可能存在问题。 如果特定日期只能有一个假期,那么您可以使用以下内容:

select username
      ,count(*)
  from transactions  e1
  left join holidays tt on(e1.start_date between tt.date_from and tt.date_to)
 where e1.start_date between date '2017-02-01' and date '2018-02-01'
   and (   to_char(e1.start_date, 'D') in(5, 6) 
        or tt.date_from is not null)
       )
   and exists(
      select * 
        from transactions_org e2 
       where e1.trantype = e2.tran_type
   )
 group 
    by username;

通过实施Calendar table可以解决整个问题类别。如果你有一个这样的表,每个日期有一个记录,你可以轻松添加表示星期几和假日标志等的列。如果你的日历表看起来像这样:

DAY        DAYNAME   IS_WEEKEND IS_WEEKDAY HOLINAME
---------- --------- ---------- ---------- ------------
2017-02-01 WEDNESDAY          0          1
2017-02-02 THURSDAY           0          1
2017-02-03 FRIDAY             0          1 Some holiday
2017-02-04 SATURDAY           1          0
2017-02-05 SUNDAY             1          0
2017-02-06 MONDAY             0          1
2017-02-07 TUESDAY            0          1
2017-02-08 WEDNESDAY          0          1

您的查询可以改写为:

  from transactions  e1
  join calendar      c on(c.day = trunc(e1.start_date, 'DD')) -- Remove hours, minutes
 where e1.start_date between date '2017-02-01' and date '2018-02-01'
   and (   c.weekday in('THURSDAY', 'FRIDAY') -- Either specific weekdays
        or c.holiname is not null             -- or there is a holiday 
       )
   and exists(
      select * 
        from transactions_org e2 
       where e1.trantype = e2.tran_type
   )