使用许多联接优化 sql 选择

Posted

技术标签:

【中文标题】使用许多联接优化 sql 选择【英文标题】:optimize sql select with many Joins 【发布时间】:2016-02-25 05:05:09 【问题描述】:

如何优化这条mysql语句?

SELECT DISTINCT p.name
FROM Something_Meta s1
JOIN Something_Meta s2 ON s1.fk_somethingId = s2.fk_somethingId
JOIN Products p ON s2.fk_productId = p.id
JOIN 
(
     select fk_id from Restricted where fk_foo != 233 and fk_id NOT IN 
        (
            Select fk_id  from Restricted where  fk_foo = 233 
        )

)
r ON r.fk_id = p.id
WHERE s1.fk_somethingId = 63 AND s2.fk_somethingId <> s1.fk_somethingId

order by p.name ASC

我的桌子是这样的

Product (id,name ) Restricted (id,fk_id,fk_foo ) Something_Meta (id,fk_id,fk_somethingId )

fk_id 是产品 (id) 的外键

可能sql语句需要优化..

 select fk_id from Restricted where fk_foo != 233 and fk_id NOT IN 
    (
        Select fk_id  from Restricted where  fk_foo = 233 
    )

整个查询语句需要超过 1.5 秒才能运行,对于一个网站来说,单个查询需要很多秒。

【问题讨论】:

一些上下文会有所帮助,例如有多少记录在 Restricted NOT IN 在空值的情况下是危险的。提前警告 @Drew 是的,感谢您的观点,但根据定义,所有单元格都不是空的。 ;) 我希望解决 NOT IN 问题。对吗? 是的,是knowing your data的情况,很多人都没有 但我们不能真诚地在不知道您的架构的情况下就索引更改提出建议,以及我们将通过说尝试这个索引或那个索引来打击您系统的其他部分。晚上打字后睡觉很有趣 【参考方案1】:

您可以尝试在 (fk_foo, fk_id) 上创建一个索引,该索引将涵盖您的整个查询:

create index ix_restricted_fk_foo_fk_id(fk_foo, fk_id) on restricted

【讨论】:

感谢您的帮助,但我之前的问题不完整。您知道如何针对我的查询优化甚至实现 create index 吗? 我对mysql不太熟悉;但是索引字段顺序fk_idfk_foo 不是更有意义吗?你总是在fk_id 上输入相等,但在fk_foo 上只有一次,如果所述字段在前面,fk_foo &lt;&gt; 233 可能会导致整个索引的索引扫描。在前面有fk_id 至少应该导致该部分被搜索; fk_foo 上剩余的 etnries 应该“少很多”...(一如既往,这也很大程度上取决于数据)【参考方案2】:

首先:DISTINCT 有点危险。如果您需要过滤掉双打,那么可能是某处(查询或设计中)导致双打的错误。

第二;你可以这样写你的查询:

SELECT DISTINCT p.name
FROM Something_Meta s1
JOIN Something_Meta s2 ON s1.fk_somethingId = s2.fk_somethingId
JOIN Products p ON s2.fk_productId = p.id

WHERE s1.fk_somethingId = 63 AND s2.fk_somethingId <> s1.fk_somethingId
AND NOT EXISTS ( SELECT *
                   FROM Restricted r
                  WHERE r.fk_foo != 233 
                    AND r.fk_id  = p.id )
AND EXISTS ( SELECT *
               FROM Restricted r2
              WHERE r2.fk_foo = 233
                AND r2.fk_id = p.id )

order by p.name ASC

由于我对Something_Meta 表一无所知,因此我将只关注Restricted 表并建议您在fk_foo 和fk_id 上放置一个索引。所述索引不是查询的一部分,而是表的一部分,因此您必须预先定义一次。

CREATE INDEX idx_Restricted ON Restricted (fk_id, fk_foo)

一旦索引存在;任何可能从中受益的查询都会在后台自动使用它;无需您为它调整查询。

旁注;因为您显然正在寻找产品,所以我很好奇您不会将查询“集中”在Products上。

SELECT p.name
  FROM Products p
  JOIN etc...

【讨论】:

以上是关于使用许多联接优化 sql 选择的主要内容,如果未能解决你的问题,请参考以下文章

SQL优化

MySQL系列- MySQL执行计划

MySQL系列- MySQL执行计划

SQL 优化 where 子句中的条件

DBA的五款最佳SQL查询优化工具

用dataframe重写sql查询;如何从选择中混合不同的来源