我有这个查询,在约300.000行的表上需要大约14秒来提取数据。
该表将在不久的将来增加其规模......超过一百万行。
我使用了EXISTS
子句而不是IN
子句,并且我做了改进。
但查询太慢了。
你有解决方案吗?
提前谢谢。
这是查询:
SELECT
flow,
COUNT(*) tot
FROM
(
SELECT
ff.session_id,
GROUP_CONCAT(ff.page, '#', ff.snippet_params,'$',ff.is_lead SEPARATOR '|') flow
FROM table_a ff
WHERE EXISTS
(
SELECT
f.session_id
FROM table_a f
WHERE f.session_id = ff.session_id
AND f.is_lead = 1
GROUP BY f.user_id
ORDER BY f.user_id, f.`timestamp`
)
GROUP BY ff.user_id
ORDER BY ff.user_id, ff.`timestamp`, ff.session_id
)
AS flow
GROUP BY flow
ORDER BY tot DESC LIMIT 10
这是解释:
id select_type table type possible_keys key key_len ref rows Extra
------ ------------------ ---------- ------ ------------------ ---------- ------- ----------------------------- ------ ----------------------------------------------
1 PRIMARY <derived2> ALL (NULL) (NULL) (NULL) (NULL) 532 Using temporary; Using filesort
2 DERIVED ff ALL (NULL) (NULL) (NULL) (NULL) 322154 Using temporary; Using filesort
3 DEPENDENT SUBQUERY f ref is_lead,session_id session_id 767 ff.session_id 3 Using where; Using temporary; Using filesort
答案 0 :(得分:2)
ORDER BY中的额外表达没有任何意义,因为“GROUP BY user_id
”将保证user_id
的唯一值。
在<{em> ORDER BY
操作之后应用GROUP BY
操作。如果我的目的是为每个session_id
获得最低user_id
,我会使用MIN
聚合。在原始查询中,ORDER BY
对返回session_id
没有任何影响。 session_id
返回的值不确定。
(其他数据库会在此查询中引发错误.GROUP BY特定的扩展允许查询运行,但我们可以通过在sql_mode中包含ONLY_FULL_GROUP_BY来获得更多标准行为。)
EXISTS子查询中的GROUP BY
没有任何意义。如果找到row,则存在一行。没有必要进行GROUP BY并聚合找到的行。
仔细观察,似乎没有必要在SELECT列表中返回session_id
。 (在flow
视图查询中,或在EXISTS子查询中。)
如果我们删除无关的语法并将查询简化为其本质,那么对于实际上重要的部分,我们会留下如下所示的查询:
SELECT flow.flow AS flow
, COUNT(*) AS tot
FROM (
SELECT GROUP_CONCAT(ff.page,'#',ff.snippet_params,'$',ff.is_lead SEPARATOR '|') AS flow
FROM table_a ff
WHERE EXISTS
( SELECT 1
FROM table_a f
WHERE f.is_lead = 1
AND f.session_id = ff.session_id
)
GROUP BY ff.user_id
) flow
GROUP BY flow.flow
ORDER BY tot DESC
LIMIT 10
该查询基本上是为了从table_a
获取所有行({1}},其中session_id
与table_a
中的至少一行匹配session_id
的相同值is_lead
1}}其中user_id
值为1。
然后获取所有找到的行,并根据 SELECT flow.flow AS flow
, COUNT(*) AS tot
FROM (
SELECT GROUP_CONCAT(ff.page,'#',ff.snippet_params,'$',ff.is_lead SEPARATOR '|') AS flow
FROM ( SELECT d.session_id
FROM table_a d
WHERE d.is_lead = 1
GROUP BY d.session_id
) e
JOIN table_a ff
ON ff.session_id = e.session_id
GROUP BY ff.user_id
) flow
GROUP BY flow.flow
ORDER BY tot DESC
LIMIT 10
列中的值汇总它们。
GROUP_CONCAT中没有ORDER BY,这很奇怪,有点奇怪的是没有DISTINCT关键字。
GROUP_CONCAT聚合返回行的不确定顺序,并且可能包含重复值,这很奇怪。 (假设外部查询将根据从GROUP_CONCAT聚合返回的值执行另一个聚合。)
但是,我不确定这个查询应该回答什么问题。而且我不知道什么是独特的,什么不是。
我们知道EXISTS子查询可以重写为JOIN操作:
ORDER BY
我们可以努力使查询运行得更快。但在我这样做之前,我想确保查询返回一个与规范匹配的集合。我需要确保查询实际上回答了它旨在回答的问题。
我怀疑原始查询不正确。也就是说,我认为如果查询返回“正确”的结果,它会意外地这样做,而不是因为它保证了。或者因为表中行的唯一性(基数)或者由于处理行的意外顺序而存在特殊情况。
我想确保查询保证在我花时间调整它并添加索引之前返回正确的结果。
问:为什么GROUP_CONCAT
中没有 GROUP_CONCAT( foo ORDER BY something)
? e.g。
GROUP_CONCAT(DISTINCT foo ORDER BY something)
问:是否有特定原因没有DISTINCT关键字?
group_concat_max_length
问:我们是否应关注GROUP_CONCAT(静默)返回截断值的可能性? (基于 ... ON table_a (session_id, is_lead, page, snippet_params)
变量的设置?)
<强>后续强>
为了在上面的答案中最后一个查询的最佳表现,我建议添加以下索引:
session_id
或任何类似的索引,其中is_lead
和page
为前导列(按此顺序排列),还包括snippet_params
和flow
列。如果将ORDER BY添加到GROUP_CONCAT,我们可能需要稍微不同的索引。
对于外部查询,导出的setUseSystemMenuBar
列没有绕过“使用filesort”操作。 (除非您运行的是更新版本的MySQL,其中可能会创建索引。或者我们可以将查询分解为两个单独的操作。一个查询将内联视图实现为表,第二个查询运行反对。)
答案 1 :(得分:1)
在此子查询中,您使用的是group by但不具备聚合功能。
用于检查具有结果的EXIST {group =}基于group by或not是相同的..你应该删除组和顺序
f.session_id
这样
WHERE EXISTS
(
SELECT
f.session_id
FROM table_a f
WHERE f.session_id = ff.session_id
AND f.is_lead = 1
GROUP BY f.user_id
ORDER BY f.user_id, f.`timestamp`
)
查看您的查询我认为可以重构,例如:
WHERE EXISTS
(
SELECT
f.session_id
FROM table_a f
WHERE f.session_id = ff.session_id
AND f.is_lead = 1
)
答案 2 :(得分:0)
您需要确保将f.session_id和f.is_lead编入索引。它目前正在针对table_a的ff引用对中间结果中的每一行进行f表扫描。
答案 3 :(得分:0)
很难给出此查询(或这些查询)的优化版本。您可能无法更改数据库结构,因此可以进行更简单的查询。也许某些缓存(redis等)用于其他值...