优化使用“不为空”的左连接查询

时间:2011-12-22 23:25:06

标签: mysql sql query-optimization left-join

我很长时间都在努力解决这个问题。此查询在较小的数据集上运行相当快,但当表增长到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不为null)。此查询由框架以特定方式构建,因此面临一些约束。一个不容易实现的可能解决方案是将其更改为UNION。想避开那条路线。在过去做了“where ... in”条款,但表现更差。我可以根据需要添加到join,where子句或操作索引,但是查询结构的任何其他重大更改都不容易完成。

1 个答案:

答案 0 :(得分:3)

您可以尝试使用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

我没有对此进行测试,因此可能效果不佳,但值得尝试。