2012-07-21 97 views
2

這個查詢大約需要01:30運行:如何優化這個TSQL

select DATEADD(dd, 0, DATEDIFF(dd, 0, t1.[OccurredOn])) 
     , count(t2.UserId) 
     , count(*) - count(t2.UserId) 
from Events t1 
left join (select c.UserId, min(c.OccurredOn) FirstOccurred 
      from Events c 
      where [OccurredOn] between @start and @end 
      group by c.UserId) t2 on t1.OccurredOn = t2.FirstOccurred and t1.UserId = t2.UserId 
where t1.EventType = @eventType 
    and t1.[OccurredOn] between @start and @end 
group by DATEADD(dd, 0, DATEDIFF(dd, 0, t1.[OccurredOn])) 
order by DATEADD(dd, 0, DATEDIFF(dd, 0, t1.[OccurredOn])) 

如果我從子查詢中刪除WHERE條款,它運行瞬間。

自身運行子查詢,用WHERE需要< 1S

如果我SELECT子查詢到表變量第一,並加入到,整個查詢中19S運行。

Events表的樣子:

[Events](
    [EventType] [uniqueidentifier] NOT NULL, 
    [UserId] [uniqueidentifier] NOT NULL, 
    [OccurredOn] [datetime] NOT NULL, 
) 

我有以下primary, nonclustered, nounique指標:

  • 事件類型
  • 用戶ID
  • OccurredOn

這裏S中的執行計劃

enter image description here

使用SQL Server 2008

兩件事情:

  1. 回事請告訴我?發生什麼事會讓這個緩慢?
  2. 如何加快速度?

感謝

+0

請**顯示我們**表結構,列和它們的數據類型,告訴我們你有什麼索引... - 還有什麼** VERSION **的SQL Server使用? – 2012-07-21 08:25:25

+0

好點,意味着添加。待機 – 2012-07-21 08:25:54

+0

你看過執行計劃嗎?它顯示了什麼? – 2012-07-21 08:26:40

回答

1

您所查詢的是緩慢,因爲你的排序取決於對即時計算(DATEADD(dd, 0, DATEDIFF(dd, 0, t1.[OccurredOn]))),SQL Server不能在即時計算利用索引上。

PostgreSQL有index on expression,在PostgreSQL,您可以將表達式的結果基本上堅持到實際柱(背後的場景列)給你,所以到時候你需要排序的表情,PostgreSQL無法使用該表達式的索引。

Sql Server中最接近的類似功能是持久化公式。

您可以輕鬆地通過這個樣本查詢驗證功能:

create table PersonX 
(
Lastname varchar(50) not null, 
Firstname varchar(50) not null 
); 

create table PersonY 
(
Lastname varchar(50) not null, 
Firstname varchar(50) not null 
); 


alter table PersonX add Fullname as Lastname + ', ' + Firstname PERSISTED;  
create index ix_PersonX on PersonX(Fullname); 

declare @i int = 0; 

while @i < 10000 begin 
    insert into PersonX(Lastname,Firstname) values('Lennon','John'); 
    insert into PersonY(Lastname,Firstname) values('Lennon','John'); 
    set @i = @i + 1; 
end; 


select top 1000 Lastname, Firstname 
from PersonX 
order by Fullname; 


select top 1000 Lastname, Firstname 
from PersonY 
order by Lastname + ', ' + Firstname; 

上PersonX全稱做的訂單比PersonY更快。 PersonX擁有的只有32%的查詢成本,同時PersonY是68%

解決您的查詢的性能,做這些:

alter table Events 
    add OccurenceGroup as 
     DATEADD(dd, 0, DATEDIFF(dd, 0, [OccurredOn])) PERSISTED 

create index ix_Events on Events(OccurenceGroup); 

然後做分組和排序OccurenceGroup。


順便說一下,您是否在OccuredOn上添加了一個索引,並且還在EventType上添加了一個索引?

1

你可以嘗試用LEFT MERGE JOIN更換LEFT JOIN使派生表t2只是計算一次,而不是重新計算MIN可能多次爲每個用戶。

您也可以使用下面的排名函數來重寫。它可能更便宜。您需要針對您的數據和索引來測試這些想法。

;WITH T AS 
(
SELECT *, 
     RANK() OVER (PARTITION BY UserId ORDER BY OccurredOn) AS Rnk 
FROM Events 
WHERE [OccurredOn] BETWEEN @start AND @end 
) 
SELECT Dateadd(dd, 0, Datediff(dd, 0, OccurredOn)), 
     COUNT(CASE WHEN Rnk =1 THEN 1 END), 
     COUNT(CASE WHEN Rnk >1 THEN 1 END) 
FROM T 
WHERE EventType = @eventType  
GROUP BY Dateadd(dd, 0, Datediff(dd, 0, OccurredOn)) 
ORDER BY Dateadd(dd, 0, Datediff(dd, 0, OccurredOn))