如何优化MySQL查询(组和顺序)

时间:2009-07-29 15:59:22

标签: sql mysql optimization

嘿所有,我有一个需要优化的查询。它有效,但它是一只狗,性能明智。

它是这样的:

SELECT  *
FROM    (
        SELECT  *
        FROM    views
        WHERE   user_id = '1'
        ORDER BY
                page DESC
        ) v
GROUP BY
        v.session

我正在跟踪不同页面的视图,我想知道每个会话的最高页面,以便知道他们点击了多远(他们需要一直查看每个页面)在任何特定的会议中。

基本上我要做的是在GROUP之前对结果进行排序。以上所取得的成本很高。

任何能够如何做到这一点的人都可以拍我的头脑?谢谢你们!

更新

解释:

"1" "PRIMARY"   "<derived2>"    "ALL"   \N  \N  \N  \N  "3545"  "Using temporary; Using filesort"

"2" "DERIVED"   "views" "index" \N  "page"  "5" \N  "196168"    "Using where"

架构:

ID       int(8) unsigned  (NULL)     NO      PRI     (NULL)   auto_increment  select,insert,update,references         
page     int(8)           (NULL)     YES     MUL     (NULL)                   select,insert,update,references         
user_id  int(8)           (NULL)     YES             (NULL)                   select,insert,update,references         
session  int(8)           (NULL)     YES             (NULL)                   select,insert,update,references         
created  datetime         (NULL)     NO                                       select,insert,update,references       

索引信息:

views            0  PRIMARY              1  ID           A               196008    (NULL)  (NULL)          BTREE    

views            1  page                 1  page         A                  259    (NULL)  (NULL)  YES     BTREE 

4 个答案:

答案 0 :(得分:8)

  

我正在跟踪不同页面的视图,我想知道每个会话的最高页面,以便知道他们点击了多远(他们需要一直查看每个页面)在任何特定的会议中。

在分组之前进行排序是一种非常不可靠的方法。

MySQL扩展GROUP BY语法:您可以在SELECTORDER BY子句中使用未分组和未分页的字段。

在这种情况下,每个page输出一个随机值session

Documentation明确指出你不应该对它究竟属于哪个值做出任何假设:

  

如果从GROUP BY部分省略的列在组中不是常量,请不要使用此功能。服务器可以自由地从组中返回任何值,因此除非所有值都相同,否则结果是不确定的。

但是,实际上,会返回扫描的第一行的值。

由于您在子查询中使用ORDER BY page DESC,因此该行恰好是每个会话具有最大page的行。

你不应该依赖它,因为这种行为没有记录,如果在下一个版本中返回一些其他行,它将不会被视为错误。

但你甚至不必做这些讨厌的伎俩。

只需使用聚合函数:

SELECT  MAX(page)
FROM    views
WHERE   user_id = '1'
GROUP BY
        session

这是记录和干净的方式来做你想要的。

(user_id, session, page)上创建一个综合索引,以使查询运行得更快。

如果您需要表中的所有列,而不仅是聚合列,请使用以下语法:

SELECT  v.*
FROM    (
        SELECT  DISTINCT user_id, session
        FROM    views
        ) vo
JOIN    views v
ON      v.id =
        (
        SELECT  id
        FROM    views vi
        WHERE   vi.user_id = vo.user_id
                AND vi.session = vo.session
        ORDER BY
                page DESC
        LIMIT 1
        )

这假设id上的PRIMARY KEYviews

答案 1 :(得分:4)

我认为您的子查询是不必要的。您将从这个更简单(更快)的查询中收到相同的结果:

SELECT *
FROM views 
WHERE user_id = '1' 
GROUP BY session
ORDER BY page DESC

此外,您应该在每个字段上都有一个索引,您要么分组,排序或“在哪里”。在这种情况下,您需要有关user_id,session和page的索引。

答案 2 :(得分:0)

我建议在user_id页面上使用复合(多列)索引。这假设内部查询是缓慢的部分。

答案 3 :(得分:0)

问题在于子选择。 SELECT * FROM(SELECT * FROM)

您应该使用加入。您的“页面”字段是什么数据类型?