【问题标题】:MySQL search query with multiple joins and subqueries running slow具有多个连接和子查询的 MySQL 搜索查询运行缓慢
【发布时间】:2014-02-03 14:38:46
【问题描述】:

我有以下查询,它实际上是在存储过程中,但我删除了它,因为存储过程中发生了太多事情。基本上这是需要很长时间(超过一分钟)才能运行的最终结果,我知道原因——正如你从解释的结果中看到的那样——但我就是无法对其进行排序。

只是为了快速解释这个查询在做什么。它正在从与li.nToObjectID = 37 所在的公司“连接”的公司获取所有产品。结果还返回有关其他公司的一些其他信息,例如其名称、公司 ID 等。

SELECT DISTINCT
    SQL_CALC_FOUND_ROWS
    p.id,
    p.sTitle,
    p.sTeaser,
    p.TimeStamp,
    p.ExpiryDate,
    p.InStoreDate,
    p.sCreator,
    p.sProductCode,
    p.nRetailPrice,
    p.nCostPrice,
    p.bPublic,
    c.id as nCompanyID,
    c.sName as sCompany,
    m.id as nMID,
    m.sFileName as sHighResFileName,
    m.nSize,
    (
        Select sName
        FROM tblBrand
        WHERE id = p.nBrandID
    ) as sBrand,
    (
        Select t.sFileName
        FROM tblThumbnail t
        where t.nMediaID = m.id AND
            t.sType = "thumbnail"
    ) as sFileName,
    (
        Select t.nWidth
        FROM tblThumbnail t
        where t.nMediaID = m.id AND
            t.sType = "thumbnail"
    ) as nWidth,
    (
        Select t.nHeight
        FROM tblThumbnail t
        where t.nMediaID = m.id AND
          t.sType = "thumbnail"
    ) as nHeight,
    IF (
      (
          SELECT COUNT(id) FROM tblLink
          WHERE
              sType = "company"
              AND sStatus = "active"
              AND nToObjectID = 37
              AND nFromObjectID = u.nCompanyID
      ),
      1,
      0
    ) AS bLinked
FROM tblProduct p
INNER JOIN tblMedia m
    ON (
        m.nTypeID = p.id AND
        m.sType = "product"
    )
INNER JOIN tblUser u
    ON u.id = p.nUserID
INNER JOIN tblCompany c
    ON u.nCompanyID = c.id
LEFT JOIN tblLink li
    ON (
        li.sType = "company"
        AND li.sStatus = "active"
        AND li.nToObjectID = 37
        AND li.nFromObjectID = u.nCompanyID
    )
WHERE c.bActive = 1 
    AND p.bArchive = 0 
    AND p.bActive = 1 
AND NOW() <= p.ExpiryDate 
AND (
    li.id IS NOT NULL 
    OR (
        li.id IS NULL 
        AND p.bPublic = 1
    )
) 
ORDER BY p.TimeStamp DESC 
LIMIT 0, 52

单击此处查看 EXPLAIN 的输出。抱歉,格式不正确。

http://i60.tinypic.com/2hdqjgj.png

最后是此查询中所有表的行数:

tbl产品 计数:5392

tbl品牌 数量:194

tbl公司 数量:368

tbl用户 计数:416

tblMedia 计数:5724

tbl链接 计数:24800

tbl缩略图 计数:22207

所以我有两个问题: 1.是否有另一种编写此查询的方法可能会加快它的速度? 2. tblProducts 需要什么索引组合才能不搜索到所有行?

更新 1

这是删除子查询并改用左连接后的新查询:

SELECT DISTINCT DISTINCT
    SQL_CALC_FOUND_ROWS
    p.id,
    p.sTitle,
    p.sTeaser,
    p.TimeStamp,
    p.ExpiryDate,
    p.InStoreDate,
    p.sCreator,
    p.sProductCode,
    p.nRetailPrice,
    p.nCostPrice,
    p.bPublic,
    c.id as nCompanyID,
    c.sName as sCompany,
    m.id as nMID,
    m.sFileName as sHighResFileName,
    m.nSize,
    brand.sName as sBrand,
    thumb.sFilename,
    thumb.nWidth,
    thumb.nHeight,
    IF (
      (
          SELECT COUNT(id) FROM tblLink
          WHERE
              sType = "company"
              AND sStatus = "active"
              AND nToObjectID = 37
              AND nFromObjectID = u.nCompanyID
      ),
      1,
      0
    ) AS bLinked
FROM tblProduct p
INNER JOIN tblMedia m
    ON (
        m.nTypeID = p.id AND
        m.sType = "product"
    )
INNER JOIN tblUser u
    ON u.id = p.nUserID
INNER JOIN tblCompany c
    ON u.nCompanyID = c.id
LEFT JOIN tblLink li
    ON (
        li.sType = "company"
        AND li.sStatus = "active"
        AND li.nToObjectID = 37
        AND li.nFromObjectID = u.nCompanyID
    )
LEFT JOIN tblBrand AS brand
    ON brand.id = p.nBrandID
LEFT JOIN tblThumbnail AS thumb 
    ON (
        thumb.nMediaID = m.id 
        AND thumb.sType = 'thumbnail'
    )
WHERE c.bActive = 1 
    AND p.bArchive = 0 
    AND p.bActive = 1 
AND NOW() <= p.ExpiryDate 
AND (
    li.id IS NOT NULL 
    OR (
        li.id IS NULL 
        AND p.bPublic = 1
    )
) 
ORDER BY p.TimeStamp DESC 
LIMIT 0, 52;

更新 2

ALTER TABLE tblThumbnail ADD INDEX (nMediaID,sType) USING BTREE;
ALTER TABLE tblMedia ADD INDEX (nTypeID,sType) USING BTREE;
ALTER TABLE tblProduct ADD INDEX (bArchive,bActive,ExpiryDate,bPublic,TimeStamp) USING     BTREE;

完成上述更改后,解释显示它现在只搜索 tblProduct 上的 1464 行而不是 5392。

【问题讨论】:

    标签: mysql performance join subquery


    【解决方案1】:

    这是一个很大的查询,有很多事情要做。这将需要一些工作来优化它。我将冒昧地介绍几个步骤。

    第一步。你能摆脱 SQL_CALC_FOUND_ROWS 并且仍然让你的程序正常工作吗?如果是这样,那就这样做。当您指定 SQL_CALC_FOUND_ROWS 时,有时意味着服务器必须延迟向您发送结果集的第一行,直到最后一行可用。

    第二步。将依赖子查询重构为 JOIN。

    以下是您可以解决的方法。您的部分查询如下所示...

    SELECT DISTINCT SQL_CALC_FOUND_ROWS
        p.id,
        ...
        c.id as nCompanyID,
        ...
        m.id as nMID,
        ...
        (   /* dependent subquery to be removed */
            Select sName
            FROM tblBrand
            WHERE id = p.nBrandID
        ) as sBrand,
        (   /* dependent subquery to be removed */
            Select t.sFileName
            FROM tblThumbnail t
            where t.nMediaID = m.id AND
                t.sType = "thumbnail"
        ) as sFileName,
        (   /* dependent subquery to be removed */
            Select t.nWidth
            FROM tblThumbnail t
            where t.nMediaID = m.id AND
                t.sType = "thumbnail"
        ) as nWidth,
        (   /* dependent subquery to be removed */
            Select t.nHeight
            FROM tblThumbnail t
            where t.nMediaID = m.id AND
              t.sType = "thumbnail"
        ) as nHeight,
        ...
    

    试试这个。注意品牌和缩略图相关的子查询是如何消失的。缩略图有三个从属子查询;它们可以消失在一个 JOIN 中。

    SELECT DISTINCT SQL_CALC_FOUND_ROWS
          p.id,
          ...
          brand.sName,
          thumb.sFilename,
          thumb.nWidth,
          thumb.nHeight,
          ...
     FROM tblProduct p
    INNER JOIN tblMedia AS m     ON (m.nTypeID = p.id AND m.sType = 'product')
         ... (other table joins) ...
     LEFT JOIN tblBrand AS brand ON p.id = p.nBrandID
     LEFT JOIN tblMedia AS thumb ON (t.nMediaID = m.id AND thumb.sType = 'thumbnail')
    

    我使用了 LEFT JOIN 而不是 INNER JOIN,因此如果连接的行丢失,MySQL 将显示 NULL 值。

    编辑

    您正在使用如下所示的连接模式:

     JOIN sometable AS s ON (s.someID = m.id AND s.sType = 'string')
    

    您似乎为几张桌子这样做。您可能可以通过在这些表中创建复合索引来加速 JOIN 操作。例如,尝试将以下索引添加到 tblThumbnail:(sType, nMediaID)。您可以使用此 DDL 语句来做到这一点。

    ALTER TABLE tblThumbnail ADD INDEX  (sType, nMediaID) USING BTREE
    

    您可以对具有相同连接模式的其他表执行类似的操作。

    【讨论】:

    • 您可能会发现复合索引(sType, nMediaID) 在您的tblMedia 表上很有用,因为它可用于驱动这些连接。
    • 谢谢奥利!我会看看我是否能找到另一种方法来绕过 SQL_CALC_FOUND_ROWS,然后尝试你的解决方案。我的结果会尽快回复您。
    • 所以我实现了您的一项更改,即删除子查询并改用左连接,这非常感谢。然后我还在实时服务器上对查询进行了排序,因为我的本地服务器给了我一些问题。完成原始查询耗时 0.18 秒,使用左连接的新查询耗时 0.12 秒。新查询的解释返回与之前完全相同的结果,只是这次它只为 tblTumbnail 返回一行。请注意,我还没有删除 SQL_CALC_FOUND_ROWS,因为我仍然需要想办法解决这个问题。
    • 如果 120 毫秒是生产中可接受的查询时间,您可能会继续下一个优化任务。我不确定 CALC_FOUND_ROWS 优化会做多少工作。这可能不值得。
    • 我最大的问题是 tblProduct 上的索引。这是真正扼杀我的查询的原因。我需要弄清楚这个查询将为 tblProduct 接受什么索引组合。正如您在带有解释输出的图像链接中所注意到的那样, tblProduct 不使用它的任何索引,因此搜索它的所有行。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-01-30
    • 1970-01-01
    • 2012-05-12
    • 1970-01-01
    • 1970-01-01
    • 2017-03-18
    • 1970-01-01
    相关资源
    最近更新 更多