简体   繁体   English

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

[英]MySQL search query with multiple joins and subqueries running slow

I have the following query which is actually within a stored procedure, but I removed it as there is too much going on inside the stored procedure. 我有以下查询,该查询实际上在存储过程中,但是由于存储过程中发生了太多事情,因此将其删除。 Basically this is the end result which takes ages (more than a minute) to run and I know the reason why - as you will also see from looking at the result of the explain - but I just cannot get it sorted. 基本上,这是最终结果,需要一定的时间(超过一分钟)才能运行,而且我知道原因-从说明的结果中也可以看出-但我无法对它进行排序。

Just to quickly explain what this query is doing. 只是为了快速解释此查询在做什么。 It is fetching all products from companies that are "connected" to the company where li.nToObjectID = 37 . 它从“连接”到li.nToObjectID = 37的公司的所有公司获取所有产品。 The result also returns some other information about the other companies like its name, company id, etc. 结果还返回有关其他公司的其他信息,例如其名称,公司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

Click here to see the output for EXPLAIN. 单击此处查看EXPLAIN的输出。 Sorry, just couldn't get the formatting correct. 抱歉,无法正确设置格式。

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

And lastly the number of rows for all the tables in this query: 最后,此查询中所有表的行数:

tblProducts Count: 5392 tbl产品数:5392

tblBrand Count: 194 tbl品牌数:194

tblCompany Count: 368 公司数量:368

tblUser Count: 416 tbl用户数量:416

tblMedia Count: 5724 tbl媒体数:5724

tblLink Count: 24800 tblLink计数:24800

tblThumbnail Count: 22207 tbl缩略图计数:22207

So I have 2 questions: 1. Is there another way of writing this query which might potentially speed it up? 因此,我有两个问题:1.是否存在另一种编写此查询的方法,它可能会加快它的速度? 2. What index combination do I need for tblProducts so that not all the rows are searched through? 2.我需要tblProducts的哪种索引组合,以便不能搜索所有行?

UPDATE 1 更新1

This is the new query after removing the subqueries and making use of left joins instead: 这是在删除子查询并使用左联接之后的新查询:

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;

UPDATE 2 更新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;

After doing the above changes the explain showed that it is now only searching through 1464 rows on tblProduct instead of 5392. 完成上述更改后,说明显示现在仅在tblProduct上搜索1464行,而不是5392行。

That's a big query with a lot going on. 这是一个很大的查询,正在进行中。 It's going to take a few steps of work to optimize it. 将需要一些步骤来对其进行优化。 I will take the liberty of just presenting a couple of steps. 我将只介绍几个步骤。

First step. 第一步。 Can you get rid of SQL_CALC_FOUND_ROWS and still have your program work correctly? 您可以摆脱SQL_CALC_FOUND_ROWS并仍然使程序正常工作吗? If so, do that. 如果是这样,请执行此操作。 When you specify SQL_CALC_FOUND_ROWS it sometimes means the server has to delay sending you the first row of your resultset until the last row is available. 当您指定SQL_CALC_FOUND_ROWS时,有时意味着服务器必须延迟将结果集的第一行发送给您,直到最后一行可用为止。

Second step. 第二步。 Refactor the dependent subqueries to be JOINs instead. 将依赖子查询重构为JOIN。

Here's how you might approach that. 这是您可能的处理方式。 Part of your query looks like this... 您查询的一部分看起来像这样...

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,
    ...

Try this instead. 试试这个吧。 Notice how the brand and thumbnail dependent subqueries disappear. 请注意品牌和缩略图相关子查询如何消失。 You had three dependent subqueries for the thumbnail; 缩略图有三个相关的子查询。 they can disappear into a single JOIN. 它们可以消失为单个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')

I used LEFT JOIN rather than INNER JOIN so MySQL will present NULL values if the joined rows are missing. 我使用LEFT JOIN而不是INNER JOIN,所以如果缺少所连接的行,MySQL将显示NULL值。

Edit 编辑

You're using a join pattern that looks like this: 您正在使用如下所示的联接模式:

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

You seem to do this for a few tables. 您似乎在几个表上执行此操作。 You probably can speed up the JOIN operations by creating compound indexes in those tables. 您可能可以通过在那些表中创建复合索引来加快JOIN操作。 For example, try adding the following index to tblThumbnail: (sType, nMediaID). 例如,尝试将以下索引添加到tblThumbnail:(sType,nMediaID)。 You can do that with this DDL statement. 您可以使用此DDL语句执行此操作。

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

You can do similar things to other tables with the same join pattern. 您可以使用相同的连接模式对其他表执行类似的操作。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM