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

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 秒。我会说这是一个很好的改进:)。不高兴我没想到一开始就存在。谢谢!

以上是关于使用“不为空”的左连接查询的优化的主要内容,如果未能解决你的问题,请参考以下文章

MySQL:优化格式化日期的左连接

太多的左连接是代码味道吗?

使用 LINQ 查询语法 EF Core C# 的左外连接

LINQ查询中的左外连接[重复]

EF的左连接查询

删除...在sqlite中的左连接查询[重复]