簡體   English   中英

針對包含 7500 萬條記錄的表優化存儲過程查詢

[英]Optimizing stored procedure query for a table that contains 75 million records

我有一個包含 7500 萬行的表AFW_Coverage 還有另一個表AFW_BasicPolInfo包含大約 300 萬行。

我編寫了以下存儲過程來從表中獲取記錄:

CREATE PROCEDURE [ams360].[GetPolicyCoverages]
    @PageStart INT = 0,
    @PageSize INT = 50000,
    @RowVersion TIMESTAMP = NULL
AS
    SET NOCOUNT ON;

    ;WITH LatestCoverage AS
    (
        SELECT 
            PolId,
            MAX(EffDate) AS CoverageEffectiveDate 
        FROM 
            ams360.AFW_Coverage 
        GROUP BY 
            PolId
    ),
    Coverages AS
    (
        SELECT 
            cov.PolId,
            cov.LobId,
            cov.CoverageId,
            cov.EffDate, 
            cov.CoverageCode,
            cov.isCoverage,
            cov.FullTermPrem,
            cov.Limit1,
            cov.Limit2,
            cov.Limit3,
            cov.Deduct1,
            cov.Deduct2,
            cov.Deduct3,
            cov.ChangedDate,
            cov.RowVersion,
        FROM
            ams360.AFW_Coverage cov
        INNER JOIN
            LatestCoverage mcov ON cov.PolId = mcov.PolId
                                AND cov.EffDate = mcov.CoverageEffectiveDate
        WHERE
            cov.Status IN ('A', 'C')
    )
    SELECT
        BPI.PolId,
        BPI.PolEffDate,
        BPI.PolExpDate,
        BPI.PolTypeLOB,
        cov.LobId,
        cov.CoverageId,
        cov.EffDate,
        cov.CoverageCode,
        cov.isCoverage,
        cov.FullTermPrem,
        cov.Limit1,
        cov.Limit2,
        cov.Limit3,
        cov.Deduct1,
        cov.Deduct2,
        cov.Deduct3,
        cov.ChangedDate,
        cov.RowVersion,
    FROM 
        ams360.AFW_BasicPolInfo BPI 
    INNER JOIN 
        Coverages cov ON bpi.PolId = cov.PolId
    WHERE 
        BPI.Status IN ('A','C')
        AND BPI.PolTypeLOB IN ('Homeowners', 'Dwelling Fire')
        AND BPI.PolSubType = 'P'
        AND BPI.RenewalRptFlag IN ('A', 'R', 'I', 'N')
        AND GETDATE() BETWEEN BPI.PolEffDate AND BPI.PolExpDate
        AND (@RowVersion IS NULL OR cov.RowVersion > @RowVersion)
    GROUP BY 
        BPI.PolId,
        BPI.PolEffDate,
        BPI.PolExpDate,
        BPI.PolTypeLOB,
        cov.LobId,
        cov.CoverageId,
        cov.EffDate,
        cov.CoverageCode,
        cov.isCoverage,
        cov.FullTermPrem,
        cov.Limit1, cov.Limit2, cov.Limit3,
        cov.Deduct1, cov.Deduct2, cov.Deduct3,
        cov.ChangedDate,
        cov.RowVersion,
    ORDER BY 
        cov.RowVersion
    OFFSET 
        @PageStart ROWS
    FETCH NEXT 
        @PageSize ROWS ONLY
GO

但是,我發現上述存儲過程將數據庫固定在 100%,盡管我添加了以下索引,我看到它們在執行計划中使用:

CREATE NONCLUSTERED INDEX [IX_AFW_Coverage_PolId_EffDate] 
ON [ams360].[AFW_Coverage] ([PolId] ASC, [EffDate] ASC)
            WITH (STATISTICS_NORECOMPUTE = OFF, DROP_EXISTING = OFF, ONLINE = OFF) ON [PRIMARY]
GO

CREATE NONCLUSTERED INDEX [IX_AFW_Coverage_PolId_EffDate_Status_LobId_CoverageId] 
ON [ams360].[AFW_Coverage] ([PolId] ASC, [EffDate] ASC, [Status] ASC, [LobId] ASC, [CoverageId] ASC)
INCLUDE ([CoverageCode], [IsCoverage], [FullTermPrem], [Limit1], [Limit2],[Limit3], [Deduct1], [Deduct2], [Deduct3], [ChangedDate], [RowVersion]) 
        WITH (STATISTICS_NORECOMPUTE = OFF, DROP_EXISTING = OFF, ONLINE = OFF) ON [PRIMARY]
GO

存儲過程的執行時間在 6 分鍾到 20 分鍾或 50 分鍾之間變化(取決於服務器流量和使用情況)

我的問題:如何在存儲過程中優化此查詢,同時記住覆蓋表包含 7500 萬條記錄這一事實? 我不是 dba,也沒有優化運行緩慢的查詢的經驗。 有關如何解決此問題的任何見解都會有所幫助。 提前致謝。

首先,鏈接公用表表達式可能會導致復雜的執行計划。 我們希望計划簡單易行,便於引擎優化。

所以,讓我們從刪除第一個開始:

DROP TABLE IF EXISTS #LatestCoverage;

CREATE TABLE #LatestCoverage
(
    PolId BIGINT PRIMARY KEY
   ,CoverageEffectiveDate DATETIME2(0)
);

INSERT INTO #LatestCoverage
SELECT 
    PolId,
    MAX(EffDate) AS CoverageEffectiveDate 
FROM 
    ams360.AFW_Coverage 
GROUP BY 
    PolId;

如果ams360.AFW_Coverage表中有很多列,則查詢列上的索引可能會提高性能:

CREATE INDEX IX_AFW_Coverage_EffDate  ON ams360.AFW_Coverage 
(
    polID
    ,EffDate            
)

然后,您正在閱讀大量最近被剪切的數據。 您可以嘗試高級過濾數據,然后讀取行詳細信息。 像這樣的東西:

DROP TABLE if exists #CoveragesFiltered 

CREATE TABLE #CoveragesFiltered
(
     PolId BIGINT PRIMARY KEY
    ,RowVersion ??
);

INSERT INTO #CoveragesFiltered
SELECT 
    cov.PolId,       
    cov.RowVersion,
FROM ams360.AFW_Coverage cov
INNER JOIN #LatestCoverage mcov 
    ON cov.PolId = mcov.PolId
    AND cov.EffDate = mcov.CoverageEffectiveDate
WHERE
    cov.Status IN ('A', 'C')
    AND BPI.Status IN ('A','C')
    AND BPI.PolTypeLOB IN ('Homeowners', 'Dwelling Fire')
    AND BPI.PolSubType = 'P'
    AND BPI.RenewalRptFlag IN ('A', 'R', 'I', 'N')
    AND GETDATE() BETWEEN BPI.PolEffDate AND BPI.PolExpDate
    AND (@RowVersion IS NULL OR cov.RowVersion > @RowVersion)
ORDER BY 
    cov.RowVersion
OFFSET 
    @PageStart ROWS
FETCH NEXT 
    @PageSize ROWS ONLY;

在這里,您可以調試和優化過濾器查詢本身,只為您需要的列創建索引。

然后,有需要返回的行,提取它們的詳細信息 - 因為我們正在使用分頁,我相信它會表現良好並且成本更低 IO。

根據執行計划,您的查詢僅查看Coverage表中不到 1% 的行,因為您只對具有最新EffDate的行感興趣。 如果可能,您可以創建一個單獨的表以僅捕獲基於EffDate的最新行,並在查詢中使用此表而不是Coverage 每當在Coverage表中插入/更新行時,您可能希望插入/更新這個新表。

沒有看到執行計划,很難說出問題。 以下是我的建議:

  • 我看到您在表 AFW_BasicPolInfo 上沒有任何索引。 您還需要對它們進行索引。 如果可能,請在 PolId 上創建聚集索引,因為它看起來是唯一的、窄的、遞增的、非空列。

  • 我看到您在 AFW_Coverage 上沒有聚集索引。 我建議您在 PolId、EffDate 組合上創建聚集索引。 我認為這可能是獨特的組合。 此外,在 JOIN 中使用 PolId,它可以使 JOINS 更快。 它還會使 CTE 更快。

  • 我嚴重懷疑,你是否需要 GROUP By。 如果您確定需要 GROUP BY,請嘗試將 CTE 設置為您需要的分組級別,然后加入它們。 GROUP BY 可能是非常昂貴的操作。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM