1

我正在尝试使用 T-Sql 中的 Merge 语句构建优化的渐变维度。我编写了以下代码来处理 SCD1 和 SCD2 更改,以及数据表中的正常插入,数据来自源表,Name 和 Age 是 SCD1 列,Animal 和 Blood 是 SCD2 列:

DECLARE @LoadingDate DATETIME
SET @LoadingDate = '2012-08-20 14:23:29.827'

--Handle SCD1 Changes
MERGE INTO Table_2 AS DIM
 USING SourceTable AS SRC
 ON (DIM.ID1 = SRC.ID1
    AND DIM.ID2 = SRC.ID2)
 WHEN MATCHED
    AND (DIM.Name <> SRC.Name 
      OR DIM.AGE <> SRC.AGE)
    THEN 
    UPDATE 
    SET DIM.Name = SRC.Name,
    DIM.Age = SRC.Age;

--Handle SCD2 Changes
INSERT INTO Table_2
    (ID1, ID2, --Business Key
     Name, Age, --SCD1 Columns
     Animal, Blood, --SCD2 Columns
     DateEffective, DateExpires)
 SELECT
    ID1, ID2, --Business Key
     Name, Age, --SCD1 Columns
     Animal, Blood, --SCD2 Columns
     DateEffective, DateExpires
FROM (
MERGE Table_2 AS DIM
 USING SourceTable AS SRC
 ON (DIM.ID1 = SRC.ID1
    AND DIM.ID2 = SRC.ID2)
 WHEN NOT MATCHED
  THEN INSERT VALUES 
  (SRC.ID1, SRC.ID2,
   SRC.Name, SRC.Age,
   SRC.Animal, SRC.Blood,
   @LoadingDate, NULL)
 WHEN MATCHED 
   AND DIM.DateExpires IS NULL
   AND (DIM.Animal != SRC.Animal
    OR DIM.Blood != SRC.Blood)
   THEN UPDATE SET DIM.DateExpires = @LoadingDate
 OUTPUT $action Action_Out,
   SRC.ID1, SRC.ID2,
   SRC.Name, SRC.Age,
   SRC.Animal, SRC.Blood,
   @LoadingDate AS DateEffective,
   NULL AS DateExpires) AS MERGE_OUT
 WHERE MERGE_OUT.Action_Out = 'UPDATE';

该代码适用于 SCD1 更改(代码的第一部分),但它给了我错误: 在此处输入图像描述 当它尝试插入新行时出现错误,其中业务键 ID1 和 ID2 与任何其他行都不匹配来自数据表,“Labus”是名称字段中的值。

两个表的设计如下图所示,但 SourceTable 没有管理列: 在此处输入图像描述

我真的很感激一些帮助。谢谢!

4

3 回答 3

1

由于该错误表明隐式 nvarchar 到 int 的转换出错,因此解决此问题的初始步骤是显式转换所有可能的 nvarchar 和 int 值(如下所示),然后检查是否仍然出现错误。

如果您没有收到错误,那么您可以开始删除强制转换并归零到特定的 int 或 nvarchar 字段。

如果您确实收到错误,这可能是一个更具体的错误,可帮助您确定它发生在代码的哪个部分。

DECLARE @LoadingDate DATETIME
SET @LoadingDate = '2012-08-20 14:23:29.827'

--Handle SCD1 Changes
MERGE INTO Table_2 AS DIM
    USING SourceTable AS SRC
    ON ( CAST(DIM.ID1 AS INT) = CAST(SRC.ID1 AS INT)
         AND CAST(DIM.ID2 AS INT) = CAST(caSSRC.ID2 AS INT)
       )
    WHEN MATCHED AND ( CAST(DIM.Name AS nvarchar(255)) <> CAST(SRC.Name AS nvarchar(255))
                       OR CAST(DIM.AGE AS nvarchar(255)) <> CAST(SRC.AGE AS nvarchar(255))
                     )
        THEN 
    UPDATE
          SET
            DIM.Name = CAST(SRC.Name AS nvarchar(255)) ,
            DIM.Age = CAST(SRC.Age AS nvarchar(255)) ;

--Handle SCD2 Changes
INSERT  INTO Table_2
        ( ID1 ,
          ID2 , --Business Key
          Name ,
          Age , --SCD1 Columns
          Animal ,
          Blood , --SCD2 Columns
          DateEffective ,
          DateExpires
        )
        SELECT  CAST(ID1 AS INT),
                CAST(ID2 AS INT) , --Business Key
                CAST(Name AS nvarchar(255)) ,
                CAST(Age AS nvarchar(255)) , --SCD1 Columns
                CAST(Animal AS nvarchar(255)) ,
                CAST(Blood AS nvarchar(255)) , --SCD2 Columns
                DateEffective ,
                DateExpires
        FROM    (
MERGE Table_2 AS DIM
    USING SourceTable AS SRC
    ON ( CAST(DIM.ID1 AS INT) = CAST(SRC.ID1 AS INT)
         AND CAST(DIM.ID2 AS INT) =  CAST(SRC.ID2 AS INT)
       )
    WHEN NOT MATCHED 
        THEN INSERT
          VALUES    ( CAST(SRC.ID1 AS INT) ,
                      CAST(SRC.ID2 AS INT) ,
                  CAST(SRC.Name AS NVARCHAR(255)),
                  CAST(SRC.Age AS NVARCHAR(255)),
                  CAST(SRC.Animal AS NVARCHAR(255)),
                  CAST(SRC.Blood  AS NVARCHAR(255)),
                      @LoadingDate ,
                      NULL
                    )
    WHEN MATCHED AND DIM.DateExpires IS NULL
        AND ( CAST(DIM.Animal AS NVARCHAR(255)) != CAST(SRC.Animal AS NVARCHAR(255))
              OR CAST(DIM.Blood AS NVARCHAR(255)) != CAST(SRC.Blood AS NVARCHAR(255))
            )
        THEN UPDATE
          SET       DIM.DateExpires = @LoadingDate
    OUTPUT
        $action Action_Out ,
        SRC.ID1 ,
        SRC.ID2 ,
        SRC.Name ,
        SRC.Age ,
        SRC.Animal ,
        SRC.Blood ,
        @LoadingDate AS DateEffective ,
        NULL AS DateExpires) AS MERGE_OUT
        WHERE   MERGE_OUT.Action_Out = 'UPDATE' ;
    --...
    --...
    --...
于 2012-08-20T14:31:56.617 回答
0

试试这个:基本上,我在 SELECT 列中使用了别名。
(可组合 DML 仍有改进空间。)

此外,当您已经在子查询中合并到 Table_2 中时,是否有理由再次将其他记录(用于更新操作)插入到 Table_2 中?

DECLARE @LoadingDate DATETIME
SET @LoadingDate = '2012-08-20 14:23:29.827'

--Handle SCD1 Changes
MERGE INTO Table_2 AS DIM
    USING SourceTable AS SRC
    ON ( DIM.ID1 = SRC.ID1
         AND DIM.ID2 = SRC.ID2
       )
    WHEN MATCHED AND ( DIM.Name <> SRC.Name
                       OR DIM.AGE <> SRC.AGE
                     )
        THEN 
    UPDATE
          SET
            DIM.Name = SRC.Name ,
            DIM.Age = SRC.Age ;

--Handle SCD2 Changes
INSERT  INTO Table_2
        ( ID1 ,
          ID2 , --Business Key
          Name ,
          Age , --SCD1 Columns
          Animal ,
          Blood , --SCD2 Columns
          DateEffective ,
          DateExpires
        )
        SELECT  MERGE_OUT.ID1 ,
                MERGE_OUT.ID2 , --Business Key
                MERGE_OUT.Name ,
                MERGE_OUT.Age , --SCD1 Columns
                MERGE_OUT.Animal ,
                MERGE_OUT.Blood , --SCD2 Columns
                MERGE_OUT.DateEffective ,
                MERGE_OUT.DateExpires
        FROM    (
MERGE INTO Table_2 AS DIM
    USING SourceTable AS SRC
    ON ( DIM.ID1 = SRC.ID1
         AND DIM.ID2 = SRC.ID2
       )
    WHEN NOT MATCHED 
        THEN INSERT
          VALUES    ( SRC.ID1 ,
                      SRC.ID2 ,
                      SRC.Name ,
                      SRC.Age ,
                      SRC.Animal ,
                      SRC.Blood ,
                      @LoadingDate ,
                      NULL
                    )
    WHEN MATCHED AND DIM.DateExpires IS NULL
        AND ( DIM.Animal != SRC.Animal
              OR DIM.Blood != SRC.Blood
            )
        THEN UPDATE
          SET       DIM.DateExpires = @LoadingDate
    OUTPUT
        $action AS Action_Out ,
        SRC.ID1 AS ID1 ,
        SRC.ID2 AS ID2 ,
        SRC.NAME AS Name ,
        SRC.Age AS Age ,
        SRC.Animal AS Animal ,
        SRC.Blood AS Blood ,
        @LoadingDate AS DateEffective ,
    NULL AS DateExpires) AS MERGE_OUT ( ID1, ID2, Name, Age, Animal, Blood,
                                        DateEffective, DateExpires )
        WHERE   MERGE_OUT.Action_Out = 'UPDATE' ;
于 2012-08-21T15:01:03.587 回答
0

等一下!最后一次合并的 OUTPUT 可能与 Select 子句不匹配。因此 Action_Out 可能被映射到 ID1,因此隐式转换尝试。

尝试像这样修改 OUTPUT 子句:

OUTPUT
    $action Action_Out ,
    SRC.ID1 AS ID1,
    SRC.ID2 AS ID2,
    SRC.Name AS Name,
    SRC.Age AS Age,
    SRC.Animal AS Animal,
    SRC.Blood AS Blood,
    @LoadingDate AS DateEffective ,
    NULL AS DateExpires) AS MERGE_OUT
    WHERE   MERGE_OUT.Action_Out = 'UPDATE' ; 
于 2012-08-20T14:43:26.340 回答