需要帮助优化存储过程
Need help optimizing a stored procedure
最近在一家公司上班,发现一个存储过程在爬取。目前在这里工作的人都不知道如何优化它,但它对我来说看起来很有趣。想知道是否有人看到任何明显的东西:
--------- [Debug Block] ------------
SET @DebugMessage = 'Execution Started'
EXEC [dbo].[MarkInProcessDebugPoint]
@ProcId = @@PROCID
,@DebugType = 0
,@Message = @DebugMessage
--------- [Debug Block] ------------
BEGIN TRY
DECLARE @tbl_Downloads as TABLE (
Revision bigint,
AssignedStores varchar (max),
Id varchar (64),
DownloadId int
)
-- Get Id, and legacy Id
INSERT @tbl_Downloads (Revision, DownloadId, Id)
SELECT
isnull(DCG.[DOWNLOAD_ID], 0), [Value],
isnull(DCG.[GUID], newid())
FROM
dbo.fn_Valid_Split(@Downloads, ';') AS CD
LEFT JOIN
[DIGITAL_CONTENT_GUID] DCG ON CD.Value = DCG.[DOWNLOAD_ID]
INSERT [DIGITAL_CONTENT_GUID]
SELECT
DownloadId, 1, max(Id)
FROM
@tbl_Downloads
WHERE
Revision = 0 and DownloadId > 0
GROUP BY
DownloadId
-- Create track GUID values if the album is in the list
INSERT [DIGITAL_CONTENT_GUID] (DOWNLOAD_ID, CONTENT_TYPE_ID, [GUID])
SELECT
A.DOWNLOAD_ID, 1, NEWID()
FROM
(SELECT DISTINCT
P.DOWNLOAD_ID
FROM
DOWNLOAD_PACK P (NOLOCK)
INNER JOIN
@tbl_Downloads T ON P.DOWNLOAD_PACK_ID = T.DownloadId
WHERE
NOT EXISTS (SELECT 1
FROM [DIGITAL_CONTENT_GUID] G (nolock)
WHERE G.DOWNLOAD_ID = P.DOWNLOAD_ID)
) A
--------- [Debug Block] ------------
SELECT @DebugMessage = 'Get Id, and legacy Id'
EXEC [dbo].[MarkInProcessDebugPoint]
@ProcId = @@PROCID
,@DebugType = 0
,@Message = @DebugMessage
--------- [Debug Block] ------------
SELECT
--TA.Revision,
[dbo].[Fn_ContentExport_GetAssignedStores_Json] (TA.DownloadId, 1) AS AssignedStores,
TA.Id,
TA.DownloadId As LegacyId,
[dbo].[Fn_ContentExport_GetContentNameSpace] (CD.Download_Type_Id) AS [Namespace],
[dbo].[Fn_ContentExport_GetContentTypeName] (CD.Download_Type_Id) AS [Type],
T.TITLE AS Title,
T.DISPLAY_ARTIST_NAME AS Subtitle,
CD.DESCRIPTION AS [Description],
[dbo].[Fn_ContentExport_GetContentArtworks] (TA.DownloadId, CD.Download_Type_Id, 0) AS Artwork,
[dbo].[Fn_ContentExport_GetTitleArtists_Json] (CD.TITLE_ID, 0) AS Artists,
[dbo].[Fn_ContentExport_GetContentTypeData] (TA.DownloadId, CD.Download_Type_Id, 2240) AS TypeEntity,
'' AS PublicTags,
[dbo].[Fn_ContentExport_GetInternalTags_Json] (CD.Global_Content_Id) AS InternalTags,
CD.SUPPLIER_ID AS SupplierId,
S.NAME AS SupplierName,
CD.RELEASE_DATE AS [ReleaseDateTime2],
CD.VALID_TO_DATE As [ExpiryDateTime2],
CASE
WHEN CD.IS_DELETED = 1 THEN 1
WHEN CD.IS_DELETED = 0 AND [dbo].[Fn_ContentExport_GetContentIsSearchable] (TA.DownloadId) = 0 THEN 1
ELSE 0
END As Archived,
CD.DATE_CREATED AS CreationDateTime,
CD.DATE_MODIFIED AS ModifiedDateTime,
[dbo].[Fn_ContentExport_GetContentTierPriceId] (CD.Download_Type_Id, TA.DownloadId) As [PricingTierId],
CD.TITLE_ID As [LegacyTitleId]
-- [dbo].[Fn_ContentExport_GetContentIsSearchable] (TA.DownloadId) AS IsSearchable
FROM
@tbl_Downloads AS TA
INNER JOIN
CONTENT_DOWNLOAD CD (NOLOCK) ON TA.DownloadId = CD.DOWNLOAD_ID
INNER JOIN
TITLE T (NOLOCK) ON T.TITLE_ID = CD.TITLE_ID
INNER JOIN
SUPPLIER S (NOLOCK) ON CD.SUPPLIER_ID = s.SUPPLIER_ID
WHERE
CD.DOWNLOAD_TYPE_ID <> 125
END TRY
BEGIN CATCH
-- Use RAISERROR inside the CATCH block to return error
-- information about the original error that caused
-- execution to jump to the CATCH block.
DECLARE @ErrorMessage NVARCHAR(2048);
DECLARE @ErrorSeverity INT;
DECLARE @ErrorState INT;
SELECT
@ErrorMessage = ERROR_MESSAGE(),
@ErrorSeverity = ERROR_SEVERITY(),
@ErrorState = ERROR_STATE();
--------- [Debug Block] ------------
SET @DebugMessage = CAST(@ErrorSeverity AS VARCHAR(100)) + ':' + CAST(@ErrorState AS VARCHAR(100)) + ':' + @ErrorMessage
EXEC [dbo].[MarkInProcessDebugPoint]
@ProcId = @@PROCID
,@DebugType = 1
,@Message = @DebugMessage
--------- [Debug Block] ------------
RAISERROR (@ErrorMessage, -- Message text.
@ErrorSeverity, -- Severity.
@ErrorState -- State.
);
END CATCH
非常感谢
缺少 fn_Valid_Split 和@Downloads 的内容,因此假设分隔列表中只有很少的项目(10-20 或类似内容)。
我要检查的几件事:
从计划缓存中检查这些语句中的哪一个实际上是导致问题的语句(高 I/O,高 CPU 时间)。您还应该检查 "set statistics io" 之后的查询 returns 是什么。统计数据 I/O 将不会显示 I/O 在函数中创建,因此您也需要计划缓存。如果所有已连接的 table 都具有正确的连接索引,至少还要查看最后一个 select。
一定要看最后select中的用户自定义函数。摆脱它们会很好,如果不行,请检查是否可以将它们更改为内联 table 值函数。
如果 fn_Valid_Split 被调用的次数超过应有的次数(例如,每次执行不止一次),将其从连接中取出并填充临时 table(或 table 变量)和它的内容 returns。您还应该检查 Jeff Moden 的 DelimitedSplit8k,以防您的实现不那么好:http://www.sqlservercentral.com/articles/Tally+Table/72993/
如果@tbl_Downloads 中有很多行,请尝试将其更改为临时 table,因为这样它将包含统计信息。当它是临时 table 时,您还可以向 DownloadId 添加一个(聚集?)索引。这可能有助于联接。
我也肯定会建议删除 nolock。它比 "fixes" 更让人头疼。如果存在阻塞,我会尝试正确修复它。
最近在一家公司上班,发现一个存储过程在爬取。目前在这里工作的人都不知道如何优化它,但它对我来说看起来很有趣。想知道是否有人看到任何明显的东西:
--------- [Debug Block] ------------
SET @DebugMessage = 'Execution Started'
EXEC [dbo].[MarkInProcessDebugPoint]
@ProcId = @@PROCID
,@DebugType = 0
,@Message = @DebugMessage
--------- [Debug Block] ------------
BEGIN TRY
DECLARE @tbl_Downloads as TABLE (
Revision bigint,
AssignedStores varchar (max),
Id varchar (64),
DownloadId int
)
-- Get Id, and legacy Id
INSERT @tbl_Downloads (Revision, DownloadId, Id)
SELECT
isnull(DCG.[DOWNLOAD_ID], 0), [Value],
isnull(DCG.[GUID], newid())
FROM
dbo.fn_Valid_Split(@Downloads, ';') AS CD
LEFT JOIN
[DIGITAL_CONTENT_GUID] DCG ON CD.Value = DCG.[DOWNLOAD_ID]
INSERT [DIGITAL_CONTENT_GUID]
SELECT
DownloadId, 1, max(Id)
FROM
@tbl_Downloads
WHERE
Revision = 0 and DownloadId > 0
GROUP BY
DownloadId
-- Create track GUID values if the album is in the list
INSERT [DIGITAL_CONTENT_GUID] (DOWNLOAD_ID, CONTENT_TYPE_ID, [GUID])
SELECT
A.DOWNLOAD_ID, 1, NEWID()
FROM
(SELECT DISTINCT
P.DOWNLOAD_ID
FROM
DOWNLOAD_PACK P (NOLOCK)
INNER JOIN
@tbl_Downloads T ON P.DOWNLOAD_PACK_ID = T.DownloadId
WHERE
NOT EXISTS (SELECT 1
FROM [DIGITAL_CONTENT_GUID] G (nolock)
WHERE G.DOWNLOAD_ID = P.DOWNLOAD_ID)
) A
--------- [Debug Block] ------------
SELECT @DebugMessage = 'Get Id, and legacy Id'
EXEC [dbo].[MarkInProcessDebugPoint]
@ProcId = @@PROCID
,@DebugType = 0
,@Message = @DebugMessage
--------- [Debug Block] ------------
SELECT
--TA.Revision,
[dbo].[Fn_ContentExport_GetAssignedStores_Json] (TA.DownloadId, 1) AS AssignedStores,
TA.Id,
TA.DownloadId As LegacyId,
[dbo].[Fn_ContentExport_GetContentNameSpace] (CD.Download_Type_Id) AS [Namespace],
[dbo].[Fn_ContentExport_GetContentTypeName] (CD.Download_Type_Id) AS [Type],
T.TITLE AS Title,
T.DISPLAY_ARTIST_NAME AS Subtitle,
CD.DESCRIPTION AS [Description],
[dbo].[Fn_ContentExport_GetContentArtworks] (TA.DownloadId, CD.Download_Type_Id, 0) AS Artwork,
[dbo].[Fn_ContentExport_GetTitleArtists_Json] (CD.TITLE_ID, 0) AS Artists,
[dbo].[Fn_ContentExport_GetContentTypeData] (TA.DownloadId, CD.Download_Type_Id, 2240) AS TypeEntity,
'' AS PublicTags,
[dbo].[Fn_ContentExport_GetInternalTags_Json] (CD.Global_Content_Id) AS InternalTags,
CD.SUPPLIER_ID AS SupplierId,
S.NAME AS SupplierName,
CD.RELEASE_DATE AS [ReleaseDateTime2],
CD.VALID_TO_DATE As [ExpiryDateTime2],
CASE
WHEN CD.IS_DELETED = 1 THEN 1
WHEN CD.IS_DELETED = 0 AND [dbo].[Fn_ContentExport_GetContentIsSearchable] (TA.DownloadId) = 0 THEN 1
ELSE 0
END As Archived,
CD.DATE_CREATED AS CreationDateTime,
CD.DATE_MODIFIED AS ModifiedDateTime,
[dbo].[Fn_ContentExport_GetContentTierPriceId] (CD.Download_Type_Id, TA.DownloadId) As [PricingTierId],
CD.TITLE_ID As [LegacyTitleId]
-- [dbo].[Fn_ContentExport_GetContentIsSearchable] (TA.DownloadId) AS IsSearchable
FROM
@tbl_Downloads AS TA
INNER JOIN
CONTENT_DOWNLOAD CD (NOLOCK) ON TA.DownloadId = CD.DOWNLOAD_ID
INNER JOIN
TITLE T (NOLOCK) ON T.TITLE_ID = CD.TITLE_ID
INNER JOIN
SUPPLIER S (NOLOCK) ON CD.SUPPLIER_ID = s.SUPPLIER_ID
WHERE
CD.DOWNLOAD_TYPE_ID <> 125
END TRY
BEGIN CATCH
-- Use RAISERROR inside the CATCH block to return error
-- information about the original error that caused
-- execution to jump to the CATCH block.
DECLARE @ErrorMessage NVARCHAR(2048);
DECLARE @ErrorSeverity INT;
DECLARE @ErrorState INT;
SELECT
@ErrorMessage = ERROR_MESSAGE(),
@ErrorSeverity = ERROR_SEVERITY(),
@ErrorState = ERROR_STATE();
--------- [Debug Block] ------------
SET @DebugMessage = CAST(@ErrorSeverity AS VARCHAR(100)) + ':' + CAST(@ErrorState AS VARCHAR(100)) + ':' + @ErrorMessage
EXEC [dbo].[MarkInProcessDebugPoint]
@ProcId = @@PROCID
,@DebugType = 1
,@Message = @DebugMessage
--------- [Debug Block] ------------
RAISERROR (@ErrorMessage, -- Message text.
@ErrorSeverity, -- Severity.
@ErrorState -- State.
);
END CATCH
非常感谢
缺少 fn_Valid_Split 和@Downloads 的内容,因此假设分隔列表中只有很少的项目(10-20 或类似内容)。
我要检查的几件事:
从计划缓存中检查这些语句中的哪一个实际上是导致问题的语句(高 I/O,高 CPU 时间)。您还应该检查 "set statistics io" 之后的查询 returns 是什么。统计数据 I/O 将不会显示 I/O 在函数中创建,因此您也需要计划缓存。如果所有已连接的 table 都具有正确的连接索引,至少还要查看最后一个 select。
一定要看最后select中的用户自定义函数。摆脱它们会很好,如果不行,请检查是否可以将它们更改为内联 table 值函数。
如果 fn_Valid_Split 被调用的次数超过应有的次数(例如,每次执行不止一次),将其从连接中取出并填充临时 table(或 table 变量)和它的内容 returns。您还应该检查 Jeff Moden 的 DelimitedSplit8k,以防您的实现不那么好:http://www.sqlservercentral.com/articles/Tally+Table/72993/
如果@tbl_Downloads 中有很多行,请尝试将其更改为临时 table,因为这样它将包含统计信息。当它是临时 table 时,您还可以向 DownloadId 添加一个(聚集?)索引。这可能有助于联接。
我也肯定会建议删除 nolock。它比 "fixes" 更让人头疼。如果存在阻塞,我会尝试正确修复它。