2013-03-07 80 views
3

這(爲簡單起見修改)查詢是較大查詢的一部分,並與其他選擇一起加入日期。不過,我已將此部分固定在狗身上。假設我有一個UserLoginHistory表,記錄用戶的每個登錄信息。對於每一個用戶我想他們第一次登錄的日期。(後來在查詢中,我組由LOGDATE得到多少第一次登錄有每一天都是。)不存在的性能 - t-sql查詢

select 
    LogDate, --(this value is only date, no time) 
    UserId 
from 
    UserLoginHistory ul 
where 
    not exists 
     (
      select 
       * 
      from 
       UserLoginHistory ulPrevious 
      where 
       ulPrevious.LogDate < ul.LogDate 
       and ul.UserId = ulPrevious.UserId 
     ) 
group by ul.LogDate, ul.UserId 

顯然NOT EXISTS部分是慢的。但我無法弄清楚如何用更有效的方式替代它來完成同樣的工作。

對於小UserLogHistory計數,性能沒有問題。當我達到約15 000時,它開始變慢。也許我應該將每天的結果批量放入另一個表格,但是我希望找到更好的解決方案,因爲應該有一個更好的解決方案...

感謝您的時間!

+0

根據定義,NOT EXISTS必須執行表掃描。調整其性能的關鍵是讓必須掃描的關係儘可能小,理想情況下爲非聚集索引。如果不知道桌面上有什麼指數,就不可能提供更具體的建議。 – 2013-03-07 15:52:03

+3

在大多數情況下,這是'not in'和'left outer join之間'key null'之間最好的方法:[Aaron Bertrand測試了它](http://www.sqlperformance.com/2012/12/t-sql -queries /左防半聯接)。 – 2013-03-07 15:53:34

+2

性能問題往往是平臺特定的,您使用的數據庫是:SQL Server還是Sybase?你有什麼指標在桌子上? 15,000行不是很多,所以您的索引似乎可能不是最優的。 – Pondlife 2013-03-07 16:56:20

回答

4

您可以使用一排編號方法:

select LogDate,UserId from (
    select 
     LogDate, 
     UserId 
     row_number() over (partition by UserId order by LogDate) as rown 
    from 
     UserLoginHistory ul 
) 
where rown = 1 

行每個ID被LOGDATE編號,所以最早的一個將永遠編號爲1

注:我不認爲原始查詢中的group by是必要的 - not exists子句應保證您只能獲得UserId和LogDate的唯一組合。

+0

現在,做了一個世界的差異。 – cederlof 2013-03-08 08:00:05

4

如果這些是您感興趣的唯一兩個字段,您能否使用簡單的聚合?

SELECT LogDate = MIN(LogDate), 
     UserID 
FROM UserLoginHistory 
GROUP BY UserID; 
+0

謝謝,這可能適用於一個簡單的場景,但我選擇了dan1111的答案,因爲它對我來說會更靈活。 – cederlof 2013-03-08 08:02:02