优化日期之间的查询联接

时间:2018-09-28 22:41:08

标签: sql sql-server query-optimization

3张桌子

  • WorkRecordfact-具有工作日期(日期)-约300000行
  • EmployeeStatus-开始日期(日期),结束日期(日期),职位ID-450行
  • 位置-PositionID,PositionCode-10行

在WorkRecordFact中按位置查找数据的查询要花费很长时间。基本样本查询

SELECT workrecordfact.* 
FROM workrecordfact 
INNER JOIN Employeestatus on
  Employeestatus.EmployeeID = workrecordfact.EmployeeID and
  employeestatus.startdate <= workrecordfact.workdate and
  employeestatus.enddate >= workrecordfact.workdate
INNER JOIN Positions on
  employeestatus.PositionID = positions.PositionID
Where workrecordfact.workdate >= '20180601' 
  and workrecordfact.workdate <= '20180930' 
  and PositionCode = 'CSR'

Workrecordfact在Workdate上具有聚集索引

Employeestatus有4个索引

  • EmployeeID
  • EmployeeID + StartDate
  • EmployeeID +结束日期
  • EmployeeID + StartDate + EndDate

在查询Statistics中,我看到了很多500%的元素。从聚簇索引开始寻找WorkRecordFact索引。一些突出的数字

  • 估计的行数250
  • 预计要读取的行数667
  • 执行次数381
  • 读取的行数49525952 ??!?!?
  • 实际行数112018

结果花费的时间太长,以致在某些情况下发送查询的.net应用接收超时。

我已经重建/重组了碎片索引,并刷新了统计信息,但这并不能解决问题。

有什么想法吗?

更新:似乎该查询在SMSS中运行得很好,并且仅在应用程序中超时。日期作为参数BTW传入,目前正在调查参数嗅探可能出现的问题:-/

2 个答案:

答案 0 :(得分:0)

由于workrecordfact具有比EmployeeStatus多得多的记录,并且是日期范围查询。 首先索引它们总是很痛苦。

在我看来,第二次删除关于“员工身份”的索引对于此查询没有用。

EmployeeID
EmployeeID+ StartDate
EmployeeID+ EndDate
EmployeeID+ StartDate + EndDate

在事实表上为EmployeeID再创建一个。 我认为应该有帮助。

答案 1 :(得分:0)

游戏的名称是限制workrecordfact表上的IO。当前设置查询和索引的方式是,我们将在第三季度扫描100%的工作,然后将其过滤到仅属于CSR的工作。我想知道CSR标准是否可能更具选择性,并使我们读取的行数更少?

此查询非常简洁,对吧?

SELECT es.EmployeeID, es.StartDate, es.EndDate
FROM Positions p
  JOIN Employeestatus es
  ON p.PositionID = es.PositionID
WHERE PositionCode = 'CSR'

“ CSR”可能恰好与“排名”行匹配。假设职位的工作大致相等,我们可能只需要阅读工作表的Q3部分的10%。

这样考虑以后再介绍工作事实。

SELECT w.*
FROM
(
  SELECT es.EmployeeID, es.StartDate, es.EndDate
  FROM Positions p
    JOIN Employeestatus es
    ON p.PositionID = es.PositionID
  WHERE PositionCode = 'CSR'
) es2
  JOIN workrecordfact w
  ON es2.EmployeeID = w.EmployeeID
    AND es2.startdate <= w.workdate AND w.workdate <= es2.enddate
WHERE 
  '2018-06-01' <= w.workdate AND w.workdate <= '2018-09-30' 

此索引将最好地支持此查询:

CREATE INDEX WorkRecordFact_EmployeeId_WorkDate ON WorkRecordFact(EmployeeId, WorkDate)

将工作日期的条件选择逻辑上更早地移入查询可能会有所帮助:

SELECT w.*
FROM
(
  SELECT es.EmployeeID,
    CASE WHEN es.StartDate <= '2018-06-01' THEN '2018-06-01' ELSE es.StartDate END as StartDate,
    CASE WHEN '2018-09-30' <= es.EndDate THEN '2018-09-30' ELSE es.EndDate END as EndDate
  FROM Positions p
    JOIN Employeestatus es
    ON p.PositionID = es.PositionID
  WHERE PositionCode = 'CSR'
) es2
  JOIN workrecordfact w
  ON es2.EmployeeID = w.EmployeeID
    AND es2.startdate <= w.workdate AND w.workdate <= es2.enddate