使用“不为空”的左连接查询的优化
Posted
技术标签:
【中文标题】使用“不为空”的左连接查询的优化【英文标题】:Optimization of a left join query that uses "is not null" 【发布时间】:2011-12-22 23:25:05 【问题描述】:我已经为此苦苦挣扎了很长时间。此查询在较小的数据集上运行得相当快,但当表增长到 100k+ 行时,运行需要 30 秒到几分钟:
SELECT accounts.id
, accounts.name
, ..etc..
FROM accounts
LEFT JOIN (
SELECT distinct secr.record_id as id
FROM securitygroups secg
INNER JOIN securitygroups_users secu
ON secg.id = secu.securitygroup_id
AND secu.deleted = 0
AND secu.user_id = 'seed_chris_id'
INNER JOIN securitygroups_records secr
ON secg.id = secr.securitygroup_id
AND secr.deleted = 0
AND secr.module = 'Accounts'
WHERE secg.deleted = 0
) securitygroup_join ON securitygroup_join.id = accounts.id
WHERE (( accounts.assigned_user_id ='seed_chris_id'
OR securitygroup_join.id is not null))
AND accounts.deleted=0
ORDER BY
accounts.date_entered
DESC LIMIT 0,21
基本上它应该返回用户拥有记录(accounts.assigned_user_id)或者是与记录关联的组的成员(securitygroup_join.id 不为空)的所有行。此查询由框架以特定方式构建,因此面临一些约束。一个无法轻松实施的可能解决方案是将其更改为 UNION。想避开那条路线。过去做了一个“where...in”子句,但效果更差。我可以根据需要添加到联接、where 子句或操作索引,但对查询结构的任何其他重大更改都不容易完成。
【问题讨论】:
【参考方案1】:您可以尝试使用WHERE EXISTS
而不是LEFT JOIN
。例如:
SELECT accounts.id
, accounts.name
, ..etc..
FROM accounts
WHERE (( accounts.assigned_user_id ='seed_chris_id'
OR EXISTS (SELECT 1
FROM securitygroups secg
INNER JOIN securitygroups_users secu
ON secg.id = secu.securitygroup_id
AND secu.deleted = 0
AND secu.user_id = 'seed_chris_id'
INNER JOIN securitygroups_records secr
ON secg.id = secr.securitygroup_id
AND secr.deleted = 0
AND secr.module = 'Accounts'
WHERE secr.record_id = accounts.id
AND secg.deleted = 0)
))
AND accounts.deleted=0
ORDER BY
accounts.date_entered
DESC LIMIT 0,21
我没有对此进行测试,所以它可能不会表现得更好,但值得一试。
【讨论】:
+1 :当然值得一试。 MS SQL Server 对此进行了很好的优化,mysql 也可以,试试吧:) 针对一个在 securitygroups_records 表中有 700k 行的数据库,您建议更改的执行时间约为 3.2 秒,而原始查询的执行时间为 17.8 秒。我会说这是一个很好的改进:)。不高兴我没想到一开始就存在。谢谢!以上是关于使用“不为空”的左连接查询的优化的主要内容,如果未能解决你的问题,请参考以下文章