提高重复主约会及其异常的 SELECT 查询性能

Posted

技术标签:

【中文标题】提高重复主约会及其异常的 SELECT 查询性能【英文标题】:Improve SELECT query performance for recurring master appointments and their exceptions 【发布时间】:2014-02-14 14:23:53 【问题描述】:

我正在优化以下查询(现在需要 1:15 分钟),但我最好先解释一下我在做什么(长):

我正在为tt_emp_id 标识的员工选择tt_actualstart - tt_actualfinish 期间内的(开发人员快递)日历事件。 这些事件的特征是 tt_type 值:

0: single event<br />
1: master recurring event<br />
other: exceptions on the recurring events.
  (These have a `tt_parentid` referencing `tt_calendar_id` of the master event)

4 个 'OR'-ed 查询部分是:

    员工期间的单个和主事件 master 满足 1 要求的所有例外情况 主事件(任何地方)属于员工的期间的例外情况 属于 3 中发现的异常的主事件。此查询部分是耗时最多的部分。

示意图:

Legend:   |-------------------| = selection period
          CAPITALS = selected event
          lowercase = related event

1.                               MASTER
                          |-------------------|

2.        EXCEPTION              master
                          |-------------------|

3.        master                EXCEPTION
                          |-------------------|

4.        MASTER                exception                  
                          |-------------------|

影响这些查询的一个复杂因素是:我无法选择具有所需员工 ID 的异常。这是因为当异常的约会属性与主父级的属性相同时,Developer Express 代码不会存储这些异常的约会属性。因此,只能在主父级上选择员工 ID。

如您所见,第二个 OR-ed 查询使用第一个作为子查询;对于 4 和 3 也是如此。

select c.*
from tt_calendar c
where 
( 
   ( 
      (c.tt_type in (0,1)) and (c.tt_actualstart <= '2014-03-31') and (c.tt_actualfinish >= '2014-01-01') and (c.tt_emp_id= 20652)
   )

     or 

   (  
      (c.tt_type > 1) and (c.tt_parentid is not null) and  
      (c.tt_parentid in (select c2.tt_calendar_id from tt_calendar c2
                         where (c2.tt_calendar_id=c.tt_parentid) and (c2.tt_actualstart <= '2014-03-31') and (c2.tt_actualfinish >=  '2014-01-01') and (c2.tt_emp_id=20652)
                         )
      )
   )

     or

   (  
      (c.tt_type > 1) and (c.tt_parentid is not null) and  
      (c.tt_actualstart <= '2014-12-31') and (c.tt_actualfinish >= '2014-01-01') and
      (c.tt_parentid in (select c2.tt_calendar_id from tt_calendar c2 where c2.tt_emp_id=20652))
   )

     or

   (  
      (c.tt_type = 1) and
      (c.tt_calendar_id in 
        (select tt_parentid from tt_calendar c2 
         where (c2.tt_type > 1) and (c2.tt_parentid is not null) and  
               (c2.tt_actualstart <=  '2014-03-31') and (c2.tt_actualfinish >= '2014-01-01') and (c2.tt_emp_id=20652)))
   )
)

tt_actualstarttt_actualfinish 上添加索引使速度提高了 15%。 (FireBird) 查询计划显示这些索引确实被使用了。 除了主键tt_calendar_id,没有其他索引。

我尝试过的另一件事是使用一个整数 TAG 字段,该字段由 4 个单独的 UPDATE 查询设置,然后选择 TAGged 记录。 这实际上会增加一点执行时间。

我不确定converting the IN to EXISTS 是否有用 - Firebird/SQL/Oracle(我三个都需要它)是否已经优化了它?

关于如何改进此查询的更多想法?

2014 年 2 月 17 日添加:

我的测试数据中有一个异常情况,到目前为止所有答案都没有找到,而我的(繁重的)第四个查询发现:

(未找到)主 ID 27274,用于仅出现两次的重复事件:2013 年 12 月 10 日 - 2013 年 12 月 11 日(找到) 异常 27275 将事件(从 2013 年 12 月 11 日)移至 2014 年 1 月 11 日(tt_parent_id=27274) 在这种情况下,事件被移至选择期,但主事件不在其中。我也要找师傅。

顺便说一句,四个结果集有一些重叠,所以在所有情况下都应该是 UNION 而不是 UNION ALL,但我的问题并不清楚。

【问题讨论】:

每当你在 Firebird 中使用 IN 子句时,它都会执行一次表扫描。我会尝试其他人提供的 UNION ALL 查询,有时让 Firebird 自己决定查询比尝试为您的案例提供 OR 子句更快。 【参考方案1】:
select c.*
from tt_calendar c

where 
   ( 
      (c.tt_type in (0,1)) and (c.tt_actualstart <= '2014-12-31') and (c.tt_actualfinish >= '2014-01-01') and (c.tt_emp_id= 20652)
   )

UNION

select c.*
from tt_calendar c 
inner join tt_calendar c2 on c2.tt_calendar_id=c.tt_parentid
where
   (  
      (c.tt_type > 1) and (c.tt_parentid is not null) and  
       (c2.tt_actualstart <= '2014-12-31') and (c2.tt_actualfinish >= '2014-01-01') and (c2.tt_emp_id=20652)
   )

UNION

select c.*
from tt_calendar c 
inner join tt_calendar c2 on c2.tt_calendar_id=c.tt_parentid
where
   (  
      (c.tt_type > 1) and (c.tt_parentid is not null) and  
      (c.tt_actualstart <= '2014-12-31') and (c.tt_actualfinish >= '2014-01-01') and (c2.tt_emp_id=20652)
   )

UNION

select c.*
from tt_calendar c 
inner join tt_calendar c2 on c.tt_calendar_id=c2.tt_parentid
where

   (  
      (c.tt_type = 1) and (c.tt_emp_id=20652) and
      (c2.tt_type > 1) and (c2.tt_parentid is not null) and  
      (c2.tt_actualstart <=  '2014-12-31') and (c2.tt_actualfinish >= '2014-01-01')
   )
)

【讨论】:

结果集恰好错过了我的第四个查询捕获的异常测试记录之一 ;-) 更新我的问题。 已解决。我冒昧地用我的最终查询替换了您的整个答案。你的有一些错误,但它引导我朝着正确的方向前进。再加上对索引的一些试验(现在 tt_parent_id 上有 一个 索引),查询时间缩短到 4 秒。 @JanDoggen,我已经注意到你必须更正语法。我相信只有一个条件是正确的。所以我写了“union all”,它会比“union”更快.所以如果只有条件对任何给定参数成立,那么使用“联合”是错误的。如果你使用“联合所有”会发生什么 我必须使用 UNION 查看我的问题更新中的最后一句话。 UNION ALL 给出了重复的结果

以上是关于提高重复主约会及其异常的 SELECT 查询性能的主要内容,如果未能解决你的问题,请参考以下文章

求助大神,数据库查询结果的去重除了distinct关键字,还有啥别的方法没?

如何提高 H2 Select 查询性能?

oracle查询重复数据

如何提高查询性能?

使用实体框架提高大型查询的性能 [重复]

提高查询的性能以删除重复项