【问题标题】:T-SQL Conversion error in T-SQL Merge statementT-SQL Merge 语句中的 T-SQL 转换错误
【发布时间】:2012-08-20 12:07:56
【问题描述】:

我正在尝试使用 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 没有管理列:

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

【问题讨论】:

  • @Johan:我认为您不能将可为空的列设计为 SQL Server 中的标识列。所以 ID1 和 ID2 不能自动递增。其中一种 nvarchar 数据类型被隐式转换为 int,这是导致此问题的原因。
  • 删除插入并让选择显示。确保您具有所需的 ID1、ID2 和名称的值。
  • 如果我删除插入,我得到错误:消息 10729,级别 15,状态 1,第 30 行嵌套的 INSERT、UPDATE、DELETE 或 MERGE 语句不允许在不是的 SELECT 语句中INSERT 语句的直接行源。

标签: sql-server tsql ssis


【解决方案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' ;
    --...
    --...
    --...

【讨论】:

    【解决方案2】:

    哦,等等!最后一次合并的 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' ; 
    

    【讨论】:

    • 我在表设计中将 ID1 和 ID2 字段更改为 nvarchar(255) 以查看是否匹配,但是当我执行脚本时,我得到:Msg 241, Level 16, State 1,从字符串转换日期和/或时间时,第 18 行转换失败。
    【解决方案3】:

    试试这个: 基本上,我在 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' ;
    

    【讨论】:

    • 我不太擅长T-Sql。我从dimensionmergescd.codeplex.com 中获取代码,第五个视频youtube.com/watch?v=OB27rN7PQi8 并将其改编为我的问题。插入也被更新的行是必要的,因为这就是缓慢变化维度类型 2 属性的工作方式,保持历史记录(“更新”与到期日期)并使用新更改的数据插入新行。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-13
    • 1970-01-01
    • 1970-01-01
    • 2018-11-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多