2010-01-13 140 views
2

我能夠完成這個查詢,但它需要25秒。太長了!我該如何優化這個查詢?聲明外面走進一個變量:(的startDate,間隔1個月)如何優化此SQL選擇查詢?

SELECT COUNT(DISTINCT u1.User_ID) AS total 
FROM UserClicks u1 
INNER JOIN (SELECT DISTINCT User_ID 
       FROM UserClicks 
      WHERE (Date BETWEEN DATE_SUB(:startDate, INTERVAL 1 MONTH) AND :startDate)) u2 
      ON u1.User_ID = u2.User_ID 
WHERE (u1.Date BETWEEN :startDate AND :endDate) 

這是正在一個MySQL數據庫

+0

您的UserClicks.User_ID字段是否不唯一且已建立索引?這應該使你擺脫查詢的兩個DISTINCT部分。無論如何,我認爲@Parrots的答案如下。 – JMD 2010-01-13 18:46:02

+0

@andrew:是否真的有你想要做的事情,在開始日期前一個月以及開始日期和結束日期之間點擊了哪些人? (請參見下面的Quassnoi註釋) – Hogan 2010-01-13 19:01:17

回答

2
SELECT COUNT(*) AS total 
FROM (
     SELECT DISTINCT User_ID 
     FROM UserClicks 
     WHERE Date BETWEEN DATE_SUB(:startDate, INTERVAL 1 MONTH) AND :startDate 
     ) u1 
WHERE EXISTS 
     (
     SELECT NULL 
     FROM UserClicks u2 
     WHERE u2.User_ID = u1.User_ID 
       AND u2.Date BETWEEN :startDate AND :endDate 
     ) 

(User_ID, Date)創建一個綜合指數:

CREATE INDEX ix_userclicks_user_date ON UserClicks (User_ID, Date) 

如果你有

SELECT COUNT(DISTINCT UserClicks.User_ID) AS total 
FROM UserClicks 
WHERE (UserClicks.Date BETWEEN :startDate AND :endDate) 
AND (UserClicks.Date BETWEEN DATE_SUB(:startDate, INTERVAL 1 MONTH) AND :startDate) 

如果日期列上添加一個索引也可能有助於很少用戶,但很多點擊,並有一個表Users,您可以使用Users表代替DISTINCT

SELECT COUNT(*) 
FROM Users u 
WHERE EXISTS 
     (
     SELECT NULL 
     FROM UserClicks uc1 
     WHERE uc1.UserId = u.Id 
       AND uc1.Date BETWEEN DATE_SUB(:startDate, INTERVAL 1 MONTH) AND :startDate 
     ) 
     AND EXISTS 
     (
     SELECT NULL 
     FROM UserClicks uc2 
     WHERE uc2.UserId = u.Id 
       AND u2.Date BETWEEN :startDate AND :endDate 
     ) 
+0

創建複合索引後,我該如何更改? – Andrew 2010-01-13 19:31:51

+0

也......組合索引需要是唯一的嗎? (對不起,如果這是一個愚蠢的問題) – Andrew 2010-01-13 19:33:25

+0

組合索引將幫助查詢運行更快(特別是第二個查詢) – Quassnoi 2010-01-13 19:33:38

0

您是否嘗試過移動DATE_SUB上?您是否有UserClicks.Date的索引?

0

爲什麼不只是使用一條select語句而不是運行嵌套的一對選擇。現在你基本上正在運行兩個查詢。試試這個:

ALTER TABLE `UserClicks` ADD INDEX ( `Date`); 
+0

這將不會返回原始查詢返回的結果。 – Quassnoi 2010-01-13 18:46:05

+0

你是什麼意思添加索引?你能證明嗎? – Andrew 2010-01-13 18:46:11

+0

@Quassnoi查詢結果如何不同?我很難看出差異。嵌套的基本上是說「讓所有的人在開始日期和結束日期之間」,「現在是從開始日期到+1月之間獲得所有人」。這與剛纔和AND操作有何不同? – Parrots 2010-01-13 18:48:38

0

MySQL的趨向處理子查詢時忽略索引,所以它必須處理所有行。如何自我加入呢?這只是我的頭頂,所以它可能不太正確,但它至少應該指向正確的方向。

SELECT COUNT(DISTINCT u1.User_ID) AS total 
FROM UserClicks AS u1 
JOIN UserClicks AS u2 USING (User_ID) 
WHERE u1.Date BETWEEN :startDate AND :endDate 
AND u2.Date BETWEEN DATE_SUB(:startDate, INTERVAL 1 MONTH) AND :startDate)