【问题标题】:How to have Mysql query (with multiple join) faster and more efficient如何让 Mysql 查询(多连接)更快更高效
【发布时间】:2018-05-17 18:46:08
【问题描述】:

MySql 查询的执行速度非常慢……太慢……无法使用!

阅读 1000 种产品的价格需要 20 多秒!!!

$dati = mysqli_query($mysqli_connect, "
SELECT      *  
FROM        $tb_products
LEFT JOIN   $tb_categories ON $tb_products.product_category = $tb_categories.category_id_master
LEFT JOIN   $tb_subcategories ON $tb_products.product_subcategory = $tb_subcategories.subcategory_id_master
LEFT JOIN   $tb_logos ON $tb_products.product_logo = $tb_logos.logo_id_master

LEFT JOIN   $tb_prices ON ( 
            $tb_products.product_brand = $tb_prices.price_brand
            AND $tb_products.product_code = $tb_prices.price_code
            AND $tb_prices.price_validity = (
                SELECT  MAX($tb_prices.price_validity) 
                FROM    $tb_prices 
                WHERE   $tb_prices.price_validity<=DATE_ADD(CURDATE(), INTERVAL +0 DAY)
                        AND $tb_products.product_code = $tb_prices.price_code
            )
        )

WHERE       $tb_products.product_language='$activeLanguage' AND $tb_products.product_category!=0
GROUP BY    $tb_products.product_code
ORDER BY    $tb_products.product_brand, $tb_categories.category_rank, $tb_subcategories.subcategory_rank, $tb_products.product_subcategory, $tb_products.product_rank
");

编辑:
根据 Mr.Alvaro 的建议,我已经更改了 SELECT *,它具有更高效的 SELECT [值列表],并且执行时间从 20 秒下降到 14 秒。还是太慢了...
END EDIT

每个产品可以有不同的价格,所以我使用 (select max...) 来获取最近(但不是未来)的价格。 也许这个功能会减慢一切?您认为有更好的解决方案吗?

考虑到相同的查询没有与价格的联接只需要 0.2 秒。 所以我确信问题出在代码的那一部分。

$dati = mysqli_query($mysqli_connect, "
SELECT      *  
FROM        $tb_products
LEFT JOIN   $tb_categories ON $tb_products.product_category = $tb_categories.category_id_master
LEFT JOIN   $tb_subcategories ON $tb_products.product_subcategory = $tb_subcategories.subcategory_id_master
LEFT JOIN   $tb_logos ON $tb_products.product_logo = $tb_logos.logo_id_master

WHERE       $tb_products.product_language='$activeLanguage' AND $tb_products.product_category!=0
GROUP BY    $tb_products.product_code
ORDER BY    $tb_products.product_brand, $tb_categories.category_rank, $tb_subcategories.subcategory_rank, $tb_products.product_subcategory, $tb_products.product_rank
");

我也考虑过它可能取决于服务器的能力,但我倾向于排除它,因为第二个查询(没有价格)作为速度是完全可以接受的。

价格表如下

+----------------+-------------+
| price_id       | int(3)      |
| price_brand    | varchar(5)  |
| price_code     | varchar(50) |
| price_value    | float(10,2) |
| price_validity | date        |
| price_language | varchar(2)  |
+----------------+-------------+

【问题讨论】:

  • 哪个数据库+版本?如果我没记错的话,MySQL 5.5.21 和 5.6.4 之间有显着的性能提升。
  • 我认为WHERE $tb_prices.price_validity&lt;=DATE_ADD(CURDATE(), INTERVAL +0 DAY) 行正在放慢速度,因为它需要为每一行计算这一点。或者您可能需要向表中添加一些索引
  • PHP:5.6.30 - MySql:5.6.38
  • 仅用于使用@Phate01 建议的测试目的,自行运行此查询并检查时间。 SELECT MAX($tb_prices.price_validity) FROM $tb_prices WHERE $tb_prices.price_validity
  • 如果我使用 SELECT MAX() 查询不起作用.. 但如果我只使用 SELECT $tb_prices.price_validity ..... 它需要 0.006 秒。显然这个查询只读取 1 个产品

标签: php mysql performance


【解决方案1】:

可能是因为您使用的是 SELECT *,这被称为不好的做法。在堆栈溢出中检查此问题。

Is there a difference between Select * and Select [list each col]

在那里,米奇小麦写道:

您应该指定一个明确的列列表。 SELECT * 将带回比创建更多 IO 和网络流量所需的更多列,但更重要的是,即使存在非聚集覆盖索引(在 SQL Server 上),它也可能需要额外的查找。 块引用

【讨论】:

  • 感谢您的建议!我使用 select 列表更改了查询,执行时间从 20 秒下降到 14 秒。但是它仍然太慢..我必须找到其他解决方案..
  • 您能否告诉我们您用于数据库服务器的硬件配置,特别是与存储相关的硬件配置。
【解决方案2】:

已解决

问题出在与价格表的最后一次 JOIN 中。 按照建议,我设法单独执行了 SELECT MAX (...),执行时间为 0.1 秒。

所以我决定在没有价格的情况下运行主查询,然后在 WHILE cicle 中获取数组,我运行第二个查询来获取每个产品的价格! 这项工作完美无缺,我的页面从 20 秒下降到了十分之几秒。

所以,代码变成了这样:

$dati = mysqli_query($mysqli_connect, "
SELECT      *  
FROM        $tb_products
LEFT JOIN   $tb_categories ON $tb_products.product_category =     $tb_categories.category_id_master
LEFT JOIN   $tb_subcategories ON $tb_products.product_subcategory =     $tb_subcategories.subcategory_id_master
LEFT JOIN   $tb_logos ON $tb_products.product_logo = $tb_logos.logo_id_master

WHERE       $tb_products.product_language='$activeLanguage' AND     $tb_products.product_category!=0
GROUP BY    $tb_products.product_code
ORDER BY    $tb_products.product_brand, $tb_categories.category_rank,     $tb_subcategories.subcategory_rank, $tb_products.product_subcategory,     $tb_products.product_rank
");

然后..

while ($array = mysqli_fetch_array($dati)) {

    $code = $array['product_code'];

    $dati_prices = mysqli_query($mysqli_connect, "
    SELECT  * 
    FROM    $tb_prices 
    WHERE   $tb_prices.price_brand = '$brand' AND $tb_prices.price_code = '$code' AND $tb_prices.price_validity = (
            SELECT  MAX($tb_prices.price_validity) 
            FROM    $tb_prices 
            WHERE   $tb_prices.price_validity<=DATE_ADD(CURDATE(), INTERVAL +0 DAY) AND $tb_prices.price_code = '$code'
    )
    GROUP BY    $tb_prices.price_code           
    ") ;

}

可能不是最好和优雅的解决方案,但对我来说效果很好!

【讨论】:

  • 如果您需要多次迭代,将查询放在一段时间的迭代中可能会导致性能问题!我看到第二个查询的唯一参数是price_code。没有 while 的单个查询,带有 IN 子句呢?
  • @Phate01,我知道这不是更好的解决方案,但它的效果很好,也有大量的重复(数千个项目)。你能建议我使用IN 子句更好的解决方案吗?因为,正如您在主要问题中所读到的,我从一个查询开始(使用 JOIN)但它不起作用!
  • 我的经验导致了这个解决方案:场景是我做一个查询,对于从这个查询返回的每个项目,我需要做另一个查询。在您的情况下,对于每个“product_code”,我需要查询以检索价格数据。您可以根据第一个查询的结果创建产品代码集合。然后只执行第二次查询,在 where 子句中设置 'IN' 而不是 '=',选择 'product_code', tb_prices.* 并稍后检索数据。像这样,您只访问数据库两次,而不是 1 + n 次,其中 n 是第一个查询的记录数
  • 我理解你的解释和操作逻辑..但我不确定我是否理解如何将它付诸实践。如果我让你举个例子,是不是太过分了?这是一个非常有趣的解决方案,我想了解如何利用它!非常感谢!
猜你喜欢
  • 1970-01-01
  • 2017-12-19
  • 2011-06-14
  • 1970-01-01
  • 1970-01-01
  • 2015-05-25
  • 1970-01-01
  • 2012-02-07
  • 1970-01-01
相关资源
最近更新 更多