具有多个连接和子查询的 MySQL 搜索查询运行缓慢

标签 mysql performance join subquery

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

只是为了快速解释这个查询在做什么。它从与 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

tbl媒体 计数: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 行。

最佳答案

这是一个涉及很多事情的大查询。需要几个步骤来优化它。我冒昧地只介绍几个步骤。

第一步。您能否摆脱 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')

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

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

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

关于具有多个连接和子查询的 MySQL 搜索查询运行缓慢,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21530136/

相关文章:

php - adobe air 应用程序中 iOs 开发的打包文件

mysql - 为什么 Centos 上的 MySQL 5.6.38 打开文件描述符的数量如此之多?

mysql - Mysql跨库触发器

mysql 右连接未按预期工作

MySQL Socket 在数千次连续连接后拒绝连接

html - CSS 模糊视频的性能和浏览器支持注意事项

c# - 快速文本阅读(替代 File.ReadAllText() 和/或 StreamReader.ReadToEnd())

python - Python 首选语法背后的基本原理

MySQL Left Join 表 where 连接表可以为空,但有where条件

php - Symfony2 与学说 : Nested conditions for findBy