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

时间:2014-02-03 14:38:46

标签: 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

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

tblProducts 数:5392

tblBrand 数:194

tblCompany 数:368

tblUser 数:416

tblMedia 数:5724

tblLink 数:24800

tblThumbnail 数: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。

1 个答案:

答案 0 :(得分: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

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