优化返回大量记录的查询,避免数百个连接。这是一个聪明的解决方案吗?
Posted
技术标签:
【中文标题】优化返回大量记录的查询,避免数百个连接。这是一个聪明的解决方案吗?【英文标题】:Optimizing a query returning a lot of records, a way to avoid hundreds of join. Is it a smart solution? 【发布时间】:2017-10-07 14:39:48 【问题描述】:我不是那么 int SQL,我对如何优化查询有以下疑问。我正在使用 MySql
我有这个数据库架构:
这是将特定商品的最后价格(Market_Commodity_Price_Series表中的最后日期)返回到特定市场的查询。
它包含很多join来检索所有相关信息:
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 操作的大多数操作是针对上一个查询返回的单个记录,而不是针对我的查询的第一个版本返回的所有记录(可能是数百或数千个) .
可能是一个聪明的解决方案?
如果是...将这个查询的输出(将其视为一个表)与其他表连接起来的正确语法是什么?
【问题讨论】:
***.com/questions/46605178/… 的副本? 你有 10k 代表。你的minimal reproducible example 在哪里?这怎么不是***.com/q/46605178/3404097的副本? 【参考方案1】:JOIN
s——尤其是在主键上——不一定很昂贵。您的联接似乎遵循数据模型。
如果不了解查询的性能特征,我不会开始优化查询。运行需要多长时间?有多少记录被排序以获得最新的?
您的WHERE
子句似乎大大限制了数据。您还可以设置索引来帮助处理 WHERE
子句 - 但是,由于字段来自不同的表,因此使用索引或全部使用索引可能会很棘手。
您有一个复杂的数据模型,有点难以理解。由于多个 n-m 关系,您似乎有可能获得笛卡尔积。如果是这样,那可能会对性能产生很大影响,并且沿每个维度预先聚合数据是可行的方法。
但是,如果不了解当前查询的行为方式,我不会开始优化查询。
【讨论】:
所以...我仍然不知道将来是否会出现性能问题,因为此时我的数据库中几乎没有模拟数据。我不太喜欢数据库,我对您的断言表示怀疑:“您的 WHERE 子句似乎大大限制了数据”。您的意思是在所有联接之前应用此 where 子句吗? (所以这些连接很少)还是什么? @AndreaNobili 。 . .数据库尽其所能提高效率。 @AndreaNobili 你对查询执行有误解,研究一下。【参考方案2】:您在编写高效查询方面做得相当不错。
您没有使用SELECT *
,它会在包含大量连接的查询中搞砸性能,因为它会生成臃肿且冗余的中间结果集。但是你的中间结果集——你应用ORDER BY
的那个——并不臃肿。
您的WHERE col = val
子句大多提到表的主键(我猜)。那挺好的。
您的大桌子Market_Commodity_Price_Series
可能会使用compound covering index。同样,其他一些表可能需要这种索引。但这应该是另一个问题的主题。
如果您使用ORDER BY ... LIMIT
并使用LIMIT
函数丢弃大部分结果,那么您建议的优化(订购一个主要由id
值组成的中间结果集)会很有帮助。但你没有这样做。
如果不详细了解您的数据,就很难提供清晰的意见。但是,如果是我,我会使用您的第一个查询。在您投入生产(以及其他复杂查询)时,我会密切关注它。当(不是如果)性能开始恶化时,您可以执行EXPLAIN
并找出索引表的最佳方法。您已经很好地编写了一个可以让您的应用程序启动并运行的查询。去吧!
【讨论】:
那么你认为第一个原始查询应该没有那么糟糕吗? 好的,最后一个问题。考虑到我的第一个查询,您是否认为应用于 MarketDetails 和 CommodityDetails 表的 WHERE 子句在 JOIN 之前应用? (所以这应该意味着对两个表都应用了单行) 是的,最新版本的 mysql 通过及早应用过滤器在减少搜索空间的基数方面做得很好。 @AndreaNobili 如果您在官方文档网站上搜索“优化”,特别是“在哪里”和/或“加入”,您会自己回答。因此,当人们不为自己采取如此简单的行动时,其他人的时间就被浪费了。同样,您缺少minimal reproducible example。这就是为什么向下投票箭头鼠标悬停文本谈论缺乏研究的原因。【参考方案3】:其中一种方法是创建一个单独的读取模型表,它来自CQRS approach,其中包含仅用于选择的所有必要属性,没有任何连接,但是每次其他表发生更改时,您都需要更新读取模型表 另一种选择是创建一个View
【讨论】:
这曾经被称为“数据集市”。以上是关于优化返回大量记录的查询,避免数百个连接。这是一个聪明的解决方案吗?的主要内容,如果未能解决你的问题,请参考以下文章
改进在 WHERE 子句中包含数百个字符串的 Netezza SQL 查询