查询性能:CTE 使用 ROW_NUMBER() 选择第一行
Posted
技术标签:
【中文标题】查询性能:CTE 使用 ROW_NUMBER() 选择第一行【英文标题】:Query performance: CTE using ROW_NUMBER() to select first row 【发布时间】:2021-03-05 15:21:40 【问题描述】:我们有三个环境,当我在其中两个环境中运行我的 SQL 查询时,只需要 30 或 38 秒即可运行,但在另一个环境中运行未完成,我应该取消它。查询基于两部分,一个 CTE 和一个非常简单的从表中选择,在 CTE 和选择中我都使用同一个表。
你能告诉我为什么需要这么长时间吗?如何改进查询?
ALTER VIEW [fact].[vPurchase]
AS
WITH VKPL AS
(
SELECT *
FROM
(SELECT
iv.[Delivery_FK],
1 AS column2,
ROW_NUMBER() OVER(PARTITION BY [Delivery_FK] ORDER BY iv.UpdateDate) AS rk
FROM
[fact].[KRMFact] iv
LEFT JOIN
[dimension].[Product] pr ON iv.Product_FK =pr.Product_SK
LEFT JOIN
[dimension].[Delivery] le ON le.Delivery_FK = iv.Delivery_FK
WHERE
pr.Product_Key = '740') X
WHERE
rk = 1
)
SELECT
-- .... here are some columns
Delivery_FK,
Product_FK,
CAST(column2 AS VARCHAR) AS column2,
f.[UpdateDate] AS [Update date]
FROM
[fact].[KRMFact] f
LEFT JOIN
VKPL v ON f.Delivery_FK = v.Delivery_FK
【问题讨论】:
检查您的执行计划和索引。此外,不同的环境具有不同的数据量,这会影响性能。 Bad Habits to Kick : Declaring VARCHAR without (length) 您对“pr”的外部连接被您在 where 子句中对 pr.Product_Key 的引用所破坏。外部连接到交付(“le”)但不引用任何列是奇怪的。在 cte 中使用整数常量 (1) 但强制转换为字符串也很奇怪 - 为什么不在 cte 中使用字符串常量(适当大小)? “这里有一些列”告诉我这绝不是整个查询。而且您出于完全未知的原因进行了自我加入 请read this 然后edit 您的问题包含更多详细信息。此外,SQL Server Management Studio (SSMS) 具有向您显示查询执行计划的功能。将您的查询放入 SSMS,右键单击并选择“包括实际执行计划”。然后运行查询。执行计划显示可能会建议您创建一个索引,以使该查询运行得更快。 【参考方案1】:这是猜测。
我猜这个查询速度慢的环境是其中有大量生产数据的环境。
我想你的KRMFact
表上的一些索引也许会对你有所帮助。以下是确定您需要什么的方法: SQL Server Management Studio (SSMS) 具有向您显示查询执行计划的功能。将您的查询(请不要简化,实际查询)放入 SSMS,右键单击并选择“包括实际执行计划”。然后运行查询。执行计划显示可能会建议您创建一个索引,以使该查询运行得更快。
我猜您正在尝试查找最早值为 UpdateDate
的行。
您的子查询
SELECT *
FROM
(SELECT
iv.[Delivery_FK],
1 AS column2,
ROW_NUMBER() OVER(PARTITION BY [Delivery_FK] ORDER BY iv.UpdateDate) AS rk
FROM
[fact].[KRMFact] iv
LEFT JOIN
[dimension].[Product] pr ON iv.Product_FK =pr.Product_SK
LEFT JOIN
[dimension].[Delivery] le ON le.Delivery_FK = iv.Delivery_FK
WHERE
pr.Product_Key = '740') X
WHERE
rk = 1
看起来它为KRMFact.Delivery_FK
的每个值挑选出最早KRMFact.UpdateDate
的行。这就是ROW_NUMBER() OVER... WHERE rk=1
语言的作用。
如果我的猜测是正确的,你可以用不同的方式来做,这可能更有效。
SELECT *
FROM
(SELECT
iv.[Delivery_FK],
1 AS column2,
1 AS rk
FROM
[fact].[KRMFact] iv
JOIN ( SELECT Delivery_FK, MIN(UpdateDate) first_update
FROM [fact].[KRMFact]
GROUP BY Delivery_FK
) first_update ON iv.UpdateDate = first_update.first_update
LEFT JOIN
[dimension].[Product] pr ON iv.Product_FK =pr.Product_SK
LEFT JOIN
[dimension].[Delivery] le ON le.Delivery_FK = iv.Delivery_FK
WHERE
pr.Product_Key = '740') X
WHERE
rk = 1
您可能应该尝试新旧版本的子查询,以确定它们是否会产生相同的结果。
如果你使用我建议的这个子查询查询,这个索引将通过优化新的子查询的MIN() ... GROUP BY
操作来帮助它运行得更快。
CREATE INDEX x_KRMFact_Product_Update
ON [fact].[KRMFact]
([Product_FK],[UpdateDate])
顺便说一句,WHERE pr.Product_Key = '740'
将您的 LEFT JOIN [dimension].[Product]
操作变成了普通的内部 JOIN。
【讨论】:
感谢您的回复。我使用了您建议的sql,但得到了相同的结果。我通过为 CTE 创建一个单独的 SQL 视图并将其加入到主视图中解决了这个问题。以上是关于查询性能:CTE 使用 ROW_NUMBER() 选择第一行的主要内容,如果未能解决你的问题,请参考以下文章