嘿所有,我有一个需要优化的查询。它有效,但它是一只狗,性能明智。
它是这样的:
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
答案 0 :(得分:8)
我正在跟踪不同页面的视图,我想知道每个会话的最高页面,以便知道他们点击了多远(他们需要一直查看每个页面)在任何特定的会议中。
在分组之前进行排序是一种非常不可靠的方法。
MySQL
扩展GROUP BY
语法:您可以在SELECT
和ORDER 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 KEY
是views
。
答案 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)
您应该使用加入。您的“页面”字段是什么数据类型?