如何优化此SQL选择查询?

时间:2010-01-13 18:35:21

标签: sql mysql

我可以完成此查询,但需要25秒。太长了!如何优化此查询?

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数据库上使用

4 个答案:

答案 0 :(得分: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)

如果您的用户很少,但点击次数很多,并且有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
        )

答案 1 :(得分:0)

您是否尝试将语句外的DATE_SUB(:startDate,INTERVAL 1 MONTH)移动到变量中?你有UserClicks.Date索引吗?

答案 2 :(得分:0)

为什么不使用一个select语句而不是运行嵌套的选择对。现在你基本上运行两个查询。试试这个:

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)

如果您在日期列上添加索引,可能会有所帮助:

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

答案 3 :(得分: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)