如何使用表扫描优化查询?
Posted
技术标签:
【中文标题】如何使用表扫描优化查询?【英文标题】:How to optimize query with table scans? 【发布时间】:2012-07-07 00:48:03 【问题描述】:这是迄今为止我的网络应用程序中最慢的查询。
SELECT prof.user_id AS userId,
prof.first_name AS first,
prof.last_name AS last,
prof.birthdate,
prof.class_string AS classes,
prof.city,
prof.country,
prof.state,
prof.images,
prof.videos,
u.username,
u.avatar,
(SELECT Count(*)
FROM company_member_sponsorship
WHERE member_id = prof.user_id
AND status = 'sponsored') AS sponsor_count,
(SELECT Count(*)
FROM member_schedules
WHERE user_id = prof.user_id) AS sched_count
FROM member_profiles prof
LEFT JOIN users u
ON u.id = prof.user_id
ORDER BY ( prof.images + prof.videos * 5 + (
CASE
WHEN prof.expire_date > :time THEN 50
ELSE 0
end ) + sponsor_count * 20 + sched_count * 4
) DESC,
prof.last_name ASC
LIMIT :start, :records
即使在所有级别上都发生了大量查询,网站上的所有其他内容也只需不到一秒钟的时间即可加载。这大约需要 3-4 秒。
显然是表扫描导致了速度变慢。我能理解为什么;第一个表有 50,000+ 行,第二个表有 160,000+ 行。
有什么办法可以优化这个查询以使其运行得更快?
如果情况变得更糟,我总是可以检查我的代码并在个人资料表中维护赞助和活动的记录,就像我对图像和视频所做的那样,尽管我想避免这样做。
编辑:我在查询中添加了 EXPLAIN 的结果。
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY prof ALL NULL NULL NULL NULL 44377 Using temporary; Using filesort
1 PRIMARY u eq_ref PRIMARY PRIMARY 3 mxsponsor.prof.user_id 1
3 DEPENDENT SUBQUERY member_schedules ref user_id user_id 3 mxsponsor.prof.user_id 6 Using index
2 DEPENDENT SUBQUERY company_member_sponsorship ref member_id member_id 3 mxsponsor.prof.user_id 2 Using where; Using index
EDIT2:
我最终通过在会员资料中维护计数来解决这个问题。无论在何处添加/删除赞助/活动,我只需调用一个扫描赞助/活动表并更新该成员计数的函数。可能还有一种方法可以优化这样的查询,但我们很快就会发布这个网站,所以我现在使用快速而肮脏的解决方案。
【问题讨论】:
如果不是EXPLAIN
...,真的需要查看带有索引的架构
发到dba.stackexchange.com/?as=1可能会得到更好的回复
【参考方案1】:
不保证有效,但尝试使用 join
和 group by
而不是内部选择:
SELECT prof.user_id AS userId,
prof.first_name AS first,
prof.last_name AS last,
prof.birthdate,
prof.class_string AS classes,
prof.city,
prof.country,
prof.state,
prof.images,
prof.videos,
u.username,
u.avatar,
Count(cms.id) AS sponsor_count,
Count(ms.id) AS sched_count
FROM member_profiles prof
LEFT JOIN users u
ON u.id = prof.user_id
LEFT JOIN company_member_sponsorship cms
ON cms.member_id = prof.user_id
AND cms.status = 'sponsored'
LEFT JOIN member_schedules ms
ON ms.user_id = prof.user_id
GROUP BY u.id
ORDER BY ( prof.images + prof.videos * 5 + (
CASE
WHEN prof.expire_date > :time THEN 50
ELSE 0
end ) + sponsor_count * 20 + sched_count * 4
) DESC,
prof.last_name ASC
LIMIT :start, :records
如果没有更好,那么该查询的 explain
会有所帮助。
【讨论】:
感谢您的建议。我尝试了查询,它比原来的查询长了大约 5 倍……很奇怪。我也一直听说加入也比子查询好。 @Peronix 向我展示了该查询的解释,我可能会提供更多帮助。听起来像是索引问题。 -id|select_type|table|type|possible_keys|key|key_len|ref|rows|Extra- -1|SIMPLE|prof|ALL|NULL NULL|NULL|NULL|44377|使用临时;使用文件排序- -1|SIMPLE|u|eq_ref|PRIMARY|PRIMARY|3|mxsponsor.prof.user_id|1- -1|SIMPLE|cms|ref|member_id|member_id|3|mxsponsor.prof.user_id|4|Using在哪里;使用索引--1|SIMPLE|ms|ref|user_id|user_id|3|mxsponsor.prof.user_id|3|使用索引- 所以它似乎是你的order by
导致了问题,你可能需要简化它,或者非规范化表结构。以上是关于如何使用表扫描优化查询?的主要内容,如果未能解决你的问题,请参考以下文章
Oracle数据库查询优化方案(处理上百万级记录如何提高处理查询速度)