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

时间:2017-10-07 14:39:49

标签: mysql sql database query-optimization rdbms

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

我有这个数据库架构:

enter image description here

这是一个查询,它将特定商品的最后价格( 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

像桌子一样使用这个输出(我不知道确切的名字,也许是视图,是吗?)并加入这个" table"到 MeasureUnit,Currency,MarketDetails,CommodityDetails,CommodityName和Languages 表中的其他所需信息。

我认为它可能更好,因为通过这种方式我使用 MAX(price_date)price_date 仅将与最新价格相关的记录提取到 Market_Commodity_Price_Series 而是获取所有记录,订购和限制到最新的记录。

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

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

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

3 个答案:

答案 0 :(得分:3)

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

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

您的WHERE子句似乎大大限制了数据。您还可以设置索引来帮助WHERE子句 - 但是,因为字段来自不同的表,所以使用索引或所有索引都很棘手。

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

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

答案 1 :(得分:1)

其中一种方法是创建一个单独的读取模型表,它来自CQRS approach,包含所有必需的属性,仅用于select而不包含任何连接,但每次其他一些表时都需要更新Read Model表变化 还有一个选项是创建View

答案 2 :(得分:1)

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

您没有使用SELECT *,这会在具有大量连接的查询中破坏性能,因为它会生成膨胀和冗余的中间结果集。但是您的中间结果集 - 您应用ORDER BY的结果集 - 并不会膨胀。

你的WHERE col = val条款主要提到表格的主键(我猜)。这很好。

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

如果您正在执行id并使用ORDER BY ... LIMIT函数丢弃大多数你的结果。但你不是那样做的。

如果不了解您的数据,很难提供清晰的意见。但是,如果是我,我会使用你的第一个查询。在您投入生产(以及其他复杂查询)时,我会密切关注它。当(不是)性能开始恶化时,您可以执行LIMIT并找出索引表的最佳方法。您已经很好地编写了一个可以启动并运行应用程序的查询。跟着它去吧!