【问题标题】:OR clause with subquery taking too much time带有子查询的 OR 子句花费了太多时间
【发布时间】:2018-09-12 12:11:55
【问题描述】:

日期范围查询耗时过长。 只是我删除了一个条件,然后它工作正常需要 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 )

如果删除 OR 并放入 AND 则很好,如果使用 OR 的随机条件仍然是相同的问题。

【问题讨论】:

  • HOLIDAYS table START_DATE 被索引了吗?
  • @RehanAzher 是在 id 上编入索引
  • 也尝试在 START_DATE 建立索引。
  • 为什么需要"count(TT.DATE_FROM)" 也许只用1(从...中选择1)
  • 注意,TO_CHAR (..., 'D') 的结果取决于当前用户会话NLS_TERRITORY 设置,该设置可能随时更改。更好地使用TO_CHAR(..., 'Dy', 'NLS_DATE_LANGUAGE=American') IN ('Sat','Sun')

标签: sql oracle oracle11g query-optimization


【解决方案1】:

问题可能出在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
   )

【讨论】:

  • +1 用于提供带有日历表的解决方案! 99% 的日历相关性能问题都可以通过一个好的日历表来解决:stackoverflow.com/questions/8374959/…
  • @Oliver - 公平地说,OP 的问题包括他们的 HOLIDAYS 表
猜你喜欢
  • 1970-01-01
  • 2013-07-11
  • 2019-12-22
  • 2018-07-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-08-04
相关资源
最近更新 更多