使用存储过程通过 SSIS 中 foreach 循环容器中的对象类型迭代 150k 条记录(其中包含 5 列)需要太多时间

问题描述 投票:0回答:1

我有一个用例,我需要遍历表中的所有行并对每一行应用一些转换(也更新)。

以下是我现在的做法:

  1. 创建了一个对象类型变量,并使用“执行 SQL 任务”(有 4 列)的完整结果集将表中的所有 150k 行数据提取到其中
  2. 使用 foreach 循环容器迭代来自步骤 1 的每一行。
  3. 在 foreach 循环容器内,再次使用“执行 SQL 任务”,其中我使用该行的 4 列作为参数来调用存储过程

注意:存储过程有一些验证检查和一个更新语句,它将针对所有 150k 记录运行。

问题是处理所有 150k 行需要花费近 3.5 小时,这可能是不可接受的,因为我可能必须至少每 2 小时运行一次此过程。

我也尝试过使用脚本任务,并在脚本任务中,使用 ADO.NET 迭代对象值,但所花费的时间几乎相同 3 个多小时。

还有 150k 行的表没有主键标识。

请帮助我如何提高此过程的性能。

详细信息:下面是执行 SQL 任务,它从包含 150k 记录的表中获取 4 列并将其存储在对象类型变量中。

select aa, bb, cc, dd 
from xxxx; 

(假设 xxxx 表有 150000 行要处理)

enter image description here

然后我使用这个 foreach 循环容器来迭代表 xxxx 中总共 150 000 行中的每一行

enter image description here

enter image description here

现在我使用“执行SQL任务”来调用包含逻辑的存储过程;存储过程接受 4 个参数并逐行处理它们,直到 foreach 循环耗尽。

enter image description here

存储过程看起来像这样

CREATE PROCEDURE [Omg].[validateError]
    @DID varchar(50),
    @MGN varchar(100),
    @MN varchar(50),
    @SN varchar(20)
AS
    DECLARE @IsValidxx bit = 0,
            @IsValidyy bit = 0,
            @IsValidzz bit = 0,
            @IsValidww bit = 0,
            @HoldErrorCodes varchar(20) = 'Z',
            @InValidxxrErrorCode varchar(20) = 'D',
            @InValidyyErrorCode varchar(20) = 'A',
            @InValidzzErrorCode varchar(20) = 'M',
            @InValidwwErrorCode varchar(20) = 'G';
BEGIN
    BEGIN TRY
        SET @IsValidxx = CASE 
                             WHEN EXISTS(SELECT 1 FROM  aaaaaaaa WHERE DID = @DID AND TerritoryType = 'Level 1') 
                                 THEN 1 
                                 ELSE 0 
                         END;
        SET @IsValidyy = CASE 
                             WHEN EXISTS (SELECT 1 
                                          FROM bbbbbbb OFM 
                                          INNER JOIN ccccccc MG ON MG.Ordf = OFM.Ordf
                                          INNER JOIN ddddddddd M  ON M.Mod = MG.Mod
                                                 AND M.Available = 1 
                                                 AND MG.IsActive = 1
                                                 AND M.MN = @MN 
                                                 AND MG.MGN = @MGN) 
                                 THEN 1 
                                 ELSE 0 
                         END;

        IF @IsValidyy = 1
        BEGIN
            SET @IsValidzz = 1;
            SET @IsValidww = 1;
        END
        ELSE
        BEGIN
            SET @IsValidzz = CASE WHEN EXISTS(SELECT 1 FROM  dddddd  WHERE MN = @MN AND SN=@SN AND Available=1 ) THEN 1 ELSE 0 END;
                SET @IsValidww= CASE WHEN EXISTS(SELECT 1 FROM  ccccc]  WHERE MGN = @MGN AND IsActive=1) THEN 1 ELSE 0 END;
        END

        IF @IsValidxx = 1 AND @IsValidyy = 1 AND 
           @IsValidzz = 1 AND @IsValidww = 1
        BEGIN
            SET @IsValidxx = 1;
            SET @IsValidyy = 1;
            SET @IsValidzz = 1;
            SET @IsValidww = 1;
        END
        ELSE 
            IF @IsValidxx = 0
            BEGIN
                SET @HoldErrorCodes = @HoldErrorCodes + @InValidxxrErrorCode;
            END

            IF @IsValidyy = 0
            BEGIN
                SET @HoldErrorCodes = @HoldErrorCodes + @InValidyyErrorCode;
            END

            IF @IsValidzz = 0
            BEGIN
                SET @HoldErrorCodes = @HoldErrorCodes + @InValidzzErrorCode;
            END

            IF @IsValidww = 0
            BEGIN
                SET @HoldErrorCodes = @HoldErrorCodes + @InValidwwErrorCode;
            END

            UPDATE [Import].[Recommendation] 
            SET ErrorCodes = @HoldErrorCodes 
            WHERE DID = @DID 
              AND MN = @MN 
              AND MGN = @MGN 
              AND SN = @SN;
    END TRY
    BEGIN CATCH
        RETURN ERROR_MESSAGE() ;
    END CATCH

整个过程需要3个多小时才能处理150k条记录

c# sql-server ssis ssis-2012 foreach-loop-container
1个回答
0
投票

你应该能够做这样的事情:

update secondValidation
set ErrorCodes = concat(
            case when isvalidxx = 0 then @InValidxxrErrorCode end
            ,case when isvalidyy = 0 then @InValidyyrErrorCode end
            ,case when isvalidzz = 0 then @InValidzzrErrorCode end
            ,case when isvalidww = 0 then @InValidwwrErrorCode end
        )
--select *
from (
    select *
    ,   case 
            when IsValidYY = 1 THEN 1
            ELSE CASE WHEN EXISTS(SELECT 1 FROM  dddddd  WHERE MN = t.MN AND SN=t.SN AND Available=1 ) THEN 1 ELSE 0 END
        END AS IsValidzz
    ,   case 
            when IsValidYY = 1 THEN 1
            ELSE CASE WHEN EXISTS(SELECT 1 FROM  [ccccc]  WHERE MGN = t.MGN AND IsActive=1) THEN 1 ELSE 0 END
        END AS IsValidww
    from (
        select *
        ,   IsValidxx = CASE 
                                     WHEN EXISTS(SELECT 1 FROM  aaaaaaaa WHERE DID = t.ID -- ??
                            AND TerritoryType = 'Level 1') 
                                         THEN 1 
                                         ELSE 0 
                                 END
        ,   IsValidyy = CASE 
                                     WHEN EXISTS (SELECT 1 
                                                  FROM bbbbbbb OFM 
                                                  INNER JOIN ccccccc MG ON MG.Ordf = OFM.Ordf
                                                  INNER JOIN ddddddddd M  ON M.Mod = MG.Mod
                                                         AND M.Available = 1 
                                                         AND MG.IsActive = 1
                                                         AND M.MN = t.MN  -- ??
                                                         AND MG.MGN = t.MGN)  -- ??
                                         THEN 1 
                                         ELSE 0 
                                 END
        from yourmaintable t
        ) firstValidation
    ) secondValidation
where IsValidxx + IsValidyy + IsValidzz + IsValidww <> 4

只需将变量连接替换为针对主表的连接,就可以开始了。

© www.soinside.com 2019 - 2024. All rights reserved.