繁体   English   中英

优化查询以返回大量记录,这是避免数百个联接的一种方式。 这是一个聪明的解决方案吗?

[英]Optimizing a query returning a lot of records, a way to avoid hundreds of join. Is it a smart solution?

我不是很聪明的SQL,并且对如何优化查询存在以下疑问。 我正在使用MySql

我有这个数据库架构:

在此处输入图片说明

这是将特定商品进入特定市场的最后价格( Market_Commodity_Price_Series表中的最后日期)返回的查询。

它包含许多联接以检索所有相关信息:

SELECT MCPS.id AS series_id,
        MD_CD.market_details_id AS market_id,
        MD_CD.commodity_details_id AS commodity_id,
        MD.market_name AS market_name,
        MCPS.price_date AS price_date,
        MCPS.avg_price AS avg_price,
        CU.ISO_4217_cod AS currency, 
        MU.unit_name AS measure_unit, 
        CD.commodity_name_en,
        CN.commodity_name 
FROM Market_Commodity_Price_Series AS MCPS
INNER JOIN MeasureUnit AS MU ON MCPS.measure_unit_id = MU.id
INNER JOIN Currency AS CU ON MCPS.currency_id = CU.id
INNER JOIN MarketDetails_CommodityDetails AS MD_CD ON MCPS.market_commodity_details_id = MD_CD.id
INNER JOIN MarketDetails AS MD ON MD_CD.market_details_id = MD.id
INNER JOIN CommodityDetails AS CD ON MD_CD.commodity_details_id = CD.id
INNER JOIN CommodityName AS CN ON CD.id = CN.commodity_details_id
INNER JOIN Languages AS LN ON CN.language_id  = LN.id
WHERE MD.id = 4
AND CD.id = 4 
AND LN.id=1
ORDER BY price_date DESC LIMIT 1

我的疑问是:使用上一个查询,我将从Market_Commodity_Price_Series表中提取与特定商品有关的所有记录到特定市场中,进行大量联接 ,并根据price_date字段整理这些记录并限制为最后一个。

我认为这可能会花费很多,因为我可以拥有很多记录(因为Market_Commodity_Price_Series表包含每日信息)。

该查询有效,但我认为可以通过更智能的方式来完成。

所以我认为我可以做这样的事情:

1)使用类似这样的查询来选择与特定商品的最后价格进入特定市场有关的记录:

SELECT measure_unit_id, 
        currency_id, 
        market_commodity_details_id, 
        MAX(price_date) price_date
FROM Market_Commodity_Price_Series  AS MCPS 
INNER JOIN MarketDetails_CommodityDetails AS MD_CD ON MCPS.market_commodity_details_id = MD_CD.id
WHERE MD_CD.market_details_id = 4
AND MD_CD.commodity_details_id = 4
GROUP BY measure_unit_id, currency_id, market_commodity_details_id

返回与该信息有关的单个记录:

measure_unit_id      currency_id          market_commodity_details_id price_date
--------------------------------------------------------------------------------
1                    2                    24                          05/10/2017

像表一样使用此输出(我不知道确切的名称,也许是视图?),然后将此“表”与MeasureUnit,Currency,MarketDetails,CommodityDetails,CommodityName和Languages表中的其他必需信息连接。

我认为可能会更好,因为这样我将使用MAX(price_date)price_date仅将与最新价格相关的记录提取到Market_Commodity_Price_Series中,而不是获取所有记录,从而对最新记录进行排序和限制。

此外,大多数JOIN操作都在执行上一个查询返回的单个记录,而不是在我的查询的第一个版本返回的所有记录上执行(可能是成百上千个)。

可能是一个聪明的解决方案?

如果是,那么此查询的输出(将其视为表)与其他表的正确语法是什么?

JOIN尤其是在主键上-不一定昂贵。 看起来您的联接正在遵循数据模型。

如果不了解查询的性能特征,就不会开始优化查询。 运行需要多长时间? 要对多少条记录进行排序以获取最新记录?

您的WHERE子句似乎在极大地限制数据。 您还可以设置索引以帮助使用WHERE子句子句-但是,由于字段来自不同的表,因此使用索引或全部使用索引可能很棘手。

您有一个复杂的数据模型,很难遵循。 由于多个nm关系,您似乎正在获得笛卡尔积。 如果是这样,那可能会对性能产生很大的影响,并且沿着每个维度预聚合数据是正确的方法。

但是,如果不了解当前查询的行为,我就不会开始优化查询。

一种方法是制作一个单独的读取模型表,它来自CQRS方法 ,其中包含仅用于选择且不包含任何联接的所有必需属性,但是每次其他一些表更改一个时,您将需要更新读取模型表。创建一个视图

您在编写有效查询方面做得相当不错。

您没有使用SELECT * ,因为它会产生肿且多余的中间结果集,因此可能会破坏具有大量联接的查询的性能。 但是您的中间结果集(您对ORDER BY应用的结果集)并没有肿。

您的WHERE col = val子句主要提到表的主键(我想)。 那很好。

您的大表Market_Commodity_Price_Series可能使用复合覆盖索引 同样,其他一些表可能需要这种索引。 但这应该是另一个问题的话题。

如果您正在执行ORDER BY ... LIMIT并使用LIMIT函数丢弃大部分结果,则建议的优化方法(订购主要由id值组成的中间结果集)将大有帮助。 但是您没有这样做。

如果不了解您的数据,就很难提供明确的意见。 但是,如果是我,我将使用您的第一个查询。 在您投入生产时(以及其他复杂的查询中),我会密切注意。 当(如果不是)性能开始下降时,则可以执行EXPLAIN并找出索引表的最佳方法。 您已经编写了一个可以使您的应用程序启动并运行的查询,已经做得很好。 去吧!

暂无
暂无

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

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