连接使用表值函数创建的表时优化 TSQL
Posted
技术标签:
【中文标题】连接使用表值函数创建的表时优化 TSQL【英文标题】:Optimize TSQL when joining a table created using a Table-Valued Function 【发布时间】:2021-04-18 23:26:28 【问题描述】:以下代码运行不到一秒:
WITH GRIBS AS
(
SELECT TOP 720
gfm.[ID],
gfm.[FileName],
gfm.[UTCDate] AS [TimeStep]
FROM [hrdps].[GRIBFileMetrics]() gfm
WHERE gfm.[Field] = 'WEARN_SFC_0'
ORDER BY gfm.[UTCDate] DESC
)
SELECT
b.[POIID],
grib.[TimeStep],
AVG(ISNULL(brain.[Amount], 0.0)) AS [Rainfall],
grib.[FileName]
FROM [storm].[BasinRain] brain
LEFT JOIN [storm].[BasinGCSMap] bmap ON brain.[BasinGCSMapID] = bmap.[ID]
LEFT JOIN [storm].[Basin] b ON b.[ID] = bmap.[BasinID]
LEFT JOIN [storm].[POI] poi ON poi.[ID] = b.[POIID]
FULL OUTER JOIN GRIBS grib ON brain.[TimeStep] = grib.[TimeStep]
GROUP BY b.[POIID], grib.[TimeStep], grib.[FileName]
创建 GRIBS 表涉及解析 TVF 中的大字符串,因此任何过滤器或加入它的速度都很慢。这就是我需要 FULL OUTER JOIN 的原因,因为即使没有 Brain.[TimeStep] 值,我也需要 grib.[TimeStep] 值,而反过来查询则需要永远运行。 COALESCE 工作正常,但随后我得到了 brain.[TimeStep] 值超出 grib.[TimeStep] 的范围,这也是不希望的,因为 GRIBS 查询的 TOP 720 ... ORDER BY gfm.[UTCDate] DESC
部分旨在将值限制为最后 30天....
所以我的问题是添加WHERE grib.[TimeStep] IS NOT NULL
或HAVING grib.[TimeStep] IS NOT NULL
会使上面的瞬间查询需要几分钟时间。
但我希望修改查询以排除 grib.[TimeStep] 为空的情况——显然无需在查询中添加任何时间——我总是可以在运行后过滤结果查询并且只添加几微秒,但这并不理想。有什么想法吗?
【问题讨论】:
OR
性能问题通常可以使用UNION ALL
来解决。
对于性能问题,您需要添加完整的表和索引定义,并通过brentozar.com/pastetheplan 共享查询计划请解释您理想中想要的确切连接逻辑(性能无法承受,因为我不是清楚。将GRIBS
转储到临时表并加入该表怎么样?
@Charlieface 临时表的东西可能会起作用。如果我知道什么是表和索引定义以及如何添加它们,我可能会熟练地自己解决这个问题。如果没有人提出更清洁的解决方案,那么将在截止日期前解决这个 javascript 端并测试临时表的想法。
这只是一个带有CREATE TABLE...CREATE INDEX...
定义的脚本。在 SSMS 中,您可以右键单击表格并生成脚本。您还可以从 SSMS 获取查询计划。请执行edit 并详细说明所需的连接逻辑。顺便说一句,如果你也添加 TVF,它可能也可以优化。
临时表解决方案没什么不干净的,哈哈
【参考方案1】:
按照 Charlieface 在评论中的建议,将您的解决方案从 CTE 更改为临时表可能如下所示。
SELECT TOP 720
gfm.[ID],
gfm.[FileName],
gfm.[UTCDate] AS [TimeStep]
INTO #GRIBS
FROM [hrdps].[GRIBFileMetrics]() gfm
WHERE gfm.[Field] = 'WEARN_SFC_0'
ORDER BY gfm.[UTCDate] DESC;
SELECT
b.[POIID],
grib.[TimeStep],
AVG(ISNULL(brain.[Amount], 0.0)) AS [Rainfall],
grib.[FileName]
FROM [storm].[BasinRain] brain
LEFT JOIN [storm].[BasinGCSMap] bmap ON brain.[BasinGCSMapID] = bmap.[ID]
LEFT JOIN [storm].[Basin] b ON b.[ID] = bmap.[BasinID]
LEFT JOIN [storm].[POI] poi ON poi.[ID] = b.[POIID]
FULL OUTER JOIN #GRIBS grib ON brain.[TimeStep] = grib.[TimeStep]
GROUP BY b.[POIID], grib.[TimeStep], grib.[FileName];
DROP TABLE #GRIBS;
把它放在一个存储过程中或使用整个东西即席。
【讨论】:
我也会索引临时表,TimeStep, ID
上的聚集键最有意义以上是关于连接使用表值函数创建的表时优化 TSQL的主要内容,如果未能解决你的问题,请参考以下文章