我不是这样的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
像桌子一样使用这个输出(我不知道确切的名字,也许是视图,是吗?)并加入这个" table"到 MeasureUnit,Currency,MarketDetails,CommodityDetails,CommodityName和Languages 表中的其他所需信息。
我认为它可能更好,因为通过这种方式我使用 MAX(price_date)price_date 仅将与最新价格相关的记录提取到 Market_Commodity_Price_Series 而是获取所有记录,订购和限制到最新的记录。
此外,大多数 JOIN 操作正在执行上一个查询返回的单个记录,而不是我的查询的第一个版本返回的所有记录(可能是数百或数千)
可能是一个聪明的解决方案吗?
如果是的话......将此查询的输出(将其视为表格)与其他表格连接的正确语法是什么?
答案 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
并找出索引表的最佳方法。您已经很好地编写了一个可以启动并运行应用程序的查询。跟着它去吧!