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

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

我不是很懂 SQL,我对如何优化查询有以下疑问。我正在使用 MySql

我有这个数据库模式:

而这是查询returns某商品的最后价格(最后日期进入Market_Commodity_Price_Seriestable)进入特定市场。

它包含很多连接来检索所有相关信息:

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 table 中提取与特定商品相关的所有记录到特定市场,做很多连接,根据 price_date 字段对这些记录进行排序并限制到最后一个。

我认为它可以扩展,因为我可以有很多记录(因为 Market_Commodity_Price_Series table 包含日常信息)。

这个查询有效,但我认为可以用更聪明的方式完成。

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

1) Select 与特定商品进入特定市场的最后价格相关的记录,使用如下查询:

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

returns与此信息相关的单条记录:

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

像 table 一样使用此输出(我不知道确切的名称,也许是视图,是吗?)并将此 "table" 加入到 MeasureUnit、Currency、MarketDetails、CommodityDetails、CommodityName 和 Languagestables.

我认为它可能会更好,因为通过这种方式我使用 MAX(price_date) price_date 来仅提取与最新价格进入Market_Commodity_Price_Series反而获取所有记录,排序并限制为最新的。

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

这可能是一个明智的解决方案吗?

如果是...将此查询的输出(将其视为 table)与其他 table 连接起来的正确语法是什么?

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

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

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

您有一个复杂的数据模型,有点难以理解。由于多个 n-m 关系,您似乎可能得到笛卡尔积。如果是这样,那会对性能产生很大影响,并且沿着每个维度预先聚合数据是可行的方法。

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

其中一种方法是创建一个单独的读取模型 Table,它来自 CQRS approach,包含仅用于 select 的所有必要属性,没有任何连接,但您需要每次其他 table 更改时更新读取模型 table 另一种选择是创建 View

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

您没有使用 SELECT *,这可能会影响具有大量连接的查询的性能,因为它会生成臃肿和冗余的中间结果集。但是你的中间结果集——你应用 ORDER BY 的结果集——并没有膨胀。

您的 WHERE col = val 子句主要提到 table 的主键(我猜)。那挺好的。

你的大 table Market_Commodity_Price_Series 也许可以使用 compound covering index。同样,其他一些 table 可能需要这种索引。但这应该是另一个问题的主题。

您建议的优化——对主要由 id 值组成的中间结果集进行排序——如果您正在执行 ORDER BY ... LIMIT 并使用 LIMIT 函数来丢弃,将会有很大帮助你的大部分结果。但是你没有那样做。

如果不进一步了解您的数据,就很难给出明确的意见。但是,如果是我,我会使用您的第一个查询。当您投入生产(以及其他复杂查询)时,我会密切关注它。当(而不是如果)性能开始下降时,您可以 EXPLAIN 并找出索引您的 table 的最佳方法。您已经很好地编写了一个查询来启动您的应用程序 运行。随它去吧!