2017-08-29 149 views
0

我正在使用一個存儲過程,它爲SystemTree表中的所有可用節點生成等級。SQL Server存儲過程需要優化

我的存儲過程工作得很好,但問題是執行時間太長。

下面是表的詳細信息:

  • SystemTree - 14000+行
  • PaymentSchedule - 5000+行
  • MasterRankChart - 只有15行

我需要優化我的存儲過程。至少需要20分鐘才能執行。

這是我的存儲過程:

ALTER PROCEDURE[dbo].[RankGeneration] 
    @CreatedUser nvarchar(128), 
    @CreatedOn datetime 
AS 
BEGIN 
    DECLARE @NodeKeyId nvarchar(128) 

    DECLARE MY_CURSOR CURSOR LOCAL STATIC READ_ONLY FORWARD_ONLY FOR 
     SELECT TOP 1000 NodeKeyId 
     FROM SystemTree 

    DECLARE @RankContainer TABLE 
          (
           NodeKeyId nvarchar(128), 
           [Rank] nvarchar(512), 
           RankId int, 
           [LargestLeg] nvarchar(128), 
           [LargestLegNV] decimal(18, 2), 
           [SecondLargestLeg] nvarchar(128), 
           [SecondLargestLegNV] decimal(18, 2), 
           [ThirdPlusLeg] nvarchar(max), 
           [ThirdPlusLegNV] decimal(18, 2) 
          ) 

    OPEN MY_CURSOR 

    FETCH NEXT FROM MY_CURSOR INTO @NodeKeyId 

    WHILE @@FETCH_STATUS = 0 
    BEGIN 
     DECLARE @YearlyMinPNV bigint 
     DECLARE @CurrentNodeId nvarchar(128) 
     DECLARE @TempNodeKeyId nvarchar(128) 
     DECLARE @TempChildKeyId nvarchar(128) 
     DECLARE @TempParentKeyId nvarchar(128) 
     DECLARE @TempPlacementNode hierarchyid 

     DECLARE @IMMEDIATEIDs TABLE 
           (
            NodeKeyId nvarchar(128), 
            PlacementNode hierarchyid 
          ) 

     INSERT INTO @IMMEDIATEIDs 
      SELECT NodeKeyId, PlacementNode 
      FROM SystemTree 
      WHERE PlacementNode.GetAncestor(1) = (SELECT PLACEMENTNODE 
                FROM SystemTree 
                WHERE NodeKeyId = @NodeKeyId) 

     DECLARE @ChildIDs TABLE 
          (
           ParentNodeId nvarchar(128), 
           NodeKeyId nvarchar(128) 
         ) 

     DECLARE @FinalNV TABLE 
         (
           ParentNodeId nvarchar(128), 
           NodeKeyId nvarchar(128), 
           TotalNV decimal(18, 2) 
         ) 

     DECLARE @ResultNV TABLE 
         (
          ImmediateNodeID nvarchar(128), 
          TotalNV decimal(18, 2) 
         ) 

     DECLARE @StorageNV TABLE 
          (
           NodeKeyId nvarchar(128), 
           NV decimal(18, 2) 
         ) 

     INSERT INTO @StorageNV 
      SELECT NodeKeyId, SUM(NV) 
      FROM PaymentSchedule 
      WHERE ClearDate IS NOT NULL 
      AND NV IS NOT NULL 
      GROUP BY NodeKeyId 

     DECLARE @i INT 
     DECLARE @count INT 

     SET @i = 0 

     SELECT @count = COUNT(*) 
     FROM @IMMEDIATEIDs 

     WHILE @i < @count 
     BEGIN 
      SELECT 
       @TempNodeKeyId = NodeKeyId, 
       @TempPlacementNode = PlacementNode 
      FROM 
       @IMMEDIATEIDs 
      ORDER BY 
       NodeKeyId 
       OFFSET (@i) ROWS FETCH NEXT 1 ROWS ONLY 

     INSERT @ChildIDs 
      SELECT 
       @TempNodeKeyId AS ParentNodeId, 
       t.NodeKeyId 
      FROM SystemTree t 
      WHERE PlacementNode.IsDescendantOf(@TempPlacementNode) = 1 
      AND t.NodeKeyId IN (SELECT NodeKeyId 
           FROM @StorageNV) 
     SET @i = @i + 1 
    END 

    SET @i = 0 

    SELECT @count = COUNT(*) 
    FROM @ChildIDs 

    WHILE @i < @count 
    BEGIN 
     SELECT 
      @TempChildKeyId = NodeKeyId, 
      @TempParentKeyId = ParentNodeId 
     FROM 
      @ChildIDs 
     ORDER BY 
      NodeKeyId 
      OFFSET (@i) ROWS FETCH NEXT 1 ROWS ONLY 

     INSERT INTO @FinalNV 
     VALUES(@TempParentKeyId, @TempChildKeyId, 
      (SELECT SUM(NV) 
       FROM @StorageNV 
       WHERE NodeKeyId = @TempChildKeyId)); 

     SET @i = @i + 1 
    END 

    INSERT INTO @ResultNV 
     SELECT ParentNodeId, SUM(TotalNV) 
     FROM @FinalNV 
     GROUP BY ParentNodeId 

    DECLARE @MainCheckResult decimal(18, 2); 
    DECLARE @LargestLeg nvarchar(128); 
    DECLARE @MiddleCheckResult decimal(18, 2); 
    DECLARE @SecondLargestLeg nvarchar(128); 
    DECLARE @ThirdCheckResult decimal(18, 2); 
    DECLARE @ThirdPlusLeg nvarchar(128) = NULL; 

    SET @MainCheckResult = ISNULL((SELECT TOP(1) TotalNV 
           FROM @ResultNV 
           ORDER BY TotalNV DESC), 0); 

    SET @LargestLeg = (SELECT TOP(1) ImmediateNodeID 
        FROM @ResultNV 
        ORDER BY TotalNV DESC) 

    SET @MiddleCheckResult = ISNULL((SELECT TOP(1) TotalNV 
            FROM @ResultNV 
            WHERE ImmediateNodeID NOT IN (SELECT TOP(1) ImmediateNodeID 
                   FROM @ResultNV 
                   ORDER BY TotalNV DESC) 
            ORDER BY TotalNV DESC), 0); 

    SET @SecondLargestLeg = (SELECT TOP(1) ImmediateNodeID 
          FROM @ResultNV 
          WHERE ImmediateNodeID NOT IN (SELECT TOP (1) ImmediateNodeID 
                 FROM @ResultNV 
                 ORDER BY TotalNV DESC) 
          ORDER BY TotalNV DESC) 

    SET @ThirdCheckResult = ISNULL((SELECT SUM(TotalNV) 
            FROM @ResultNV 
            WHERE ImmediateNodeID NOT IN (SELECT TOP(2) ImmediateNodeID 
                   FROM @ResultNV 
                   ORDER BY TotalNV DESC)), 0); 

    SET @ThirdPlusLeg = NULL; 

    INSERT INTO @RankContainer 
     SELECT TOP(1) 
      @NodeKeyId AS NodeKeyId, 
      [Rank], 
      Id AS RankId, 
      @LargestLeg AS [LargestLeg], 
      @MainCheckResult AS [LargestLegNV], 
      @SecondLargestLeg AS [SecondLargestLeg], 
      @MiddleCheckResult AS [SecondLargestLegNV], 
      @ThirdPlusLeg AS [ThirdPlusLeg], 
      @ThirdCheckResult AS [ThirdPlusLegNV] 
     FROM 
      MasterRankChart 
     WHERE 
      LargestLegNV + SecondLegNV + ThirdLegNV <= @MainCheckResult + @MiddleCheckResult + @ThirdCheckResult 
      AND SecondLegNV + ThirdLegNV <= @MiddleCheckResult + @ThirdCheckResult 
      AND ThirdLegNV <= @ThirdCheckResult 
     ORDER BY 
      Priority 

    FETCH NEXT FROM MY_CURSOR INTO @NodeKeyId 
END 

CLOSE MY_CURSOR 
DEALLOCATE MY_CURSOR 

MERGE TrackRank AS Target 
USING(SELECT 
      NodeKeyID, [Rank], [RankId], 
      [LargestLeg], [LargestLegNv], 
      SecondLargestLeg, SecondLargestLegNV, 
      ThirdPlusLeg, ThirdPlusLegNV 
     FROM @RankContainer) AS Source ON (Target.NodeKeyId = Source.NodeKeyId) 

WHEN MATCHED THEN 
    UPDATE 
     SET Target.[Rank] = Source.[Rank], 
      Target.[RankId] = Source.[RankId], 
      Target.[LargestLeg] = Source.[LargestLeg], 
      Target.[LargestLegNv] = Source.[LargestLegNv], 
      Target.SecondLargestLeg = Source.SecondLargestLeg, 
      Target.SecondLargestLegNV = Source.SecondLargestLegNV, 
      Target.ThirdPlusLeg = Source.ThirdPlusLeg, 
      Target.ThirdPlusLegNV = Source.ThirdPlusLegNV 

WHEN NOT MATCHED BY TARGET THEN 
    INSERT (NodeKeyId, [Rank], [RankId], LargestLeg, LargestLegNV, 
      SecondLargestLeg, SecondLargestLegNV, 
      ThirdPlusLeg, ThirdPlusLegNV, 
      CreatedOn, UpdatedOn, IsDeleted, CreatedBy) 
    VALUES (Source.NodeKeyID, Source.[Rank], Source.[RankId], 
      Source.[LargestLeg], Source.[LargestLegNv], 
      Source.SecondLargestLeg, Source.SecondLargestLegNV, 
      Source.ThirdPlusLeg, Source.ThirdPlusLegNV, 
      @CreatedOn, @CreatedOn, 0, @CreatedUser) 

OUTPUT $ACTION, INSERTED.*, DELETED.*; 

END 

請看一看任何給我建議,我該如何優化它。

感謝&問候,

GO先生

回答

1

首先sugestion是避免CURSOR - 每次當你能避免CURSOR,你應該這樣做。另請教,看看執行計劃,也許你預訂購能夠檢測瓶頸等

+0

感謝您的建議,一定會對它有所幫助。 –

0

下面將有助於優化查詢執行時間:

  1. 儘量不使用遊標
  2. 運行
  3. 在引用表上創建索引
  4. 打開SQL Server Profiler,然後運行查詢並將跟蹤文件保存在Profiler工具中。然後在SQL Server Tuning Advisor中打開該跟蹤文件(*.trc)並對其進行評估,這將指導我們儘量減少執行時間。
+0

我曾試過你的方式。但它將執行時間增加了10倍。 –

+0

哦!真的很抱歉@ Mr.Go,對於我的查詢,SQL調優顧問顯示了一些要點。我跟着那個,我感覺好多了,因此我建議你。 –