如何识别MERGE语句,为什么在此期间,行/行导致错误(How to identify which

2019-10-19 07:42发布

我使用下面的代码在我的C#批处理作业来处理更新和插入的大量(20K +)。 然而,在测试过程中,我可以看到,如果有问题,如违反约束,我只得到了第一个错误消息回来,没有信息上记录(或记录),这是导致该问题。

是否有一个错误处理方法或技术,可以使用任何实现.NetT-SQL ,可以给我们这个功能?

C#

    private static string insertCommand =
        "INSERT (ChannelCode, DrmTerrDesc, IndDistrnId, StateCode, ZipCode, EndDate, EffectiveDate, LastUpdateId, LastUpdateDate, ErrorCodes, Status) " +
        "VALUES(Source.ChannelCode, Source.DrmTerrDesc, Source.IndDistrnId, Source.StateCode, Source.ZipCode, Source.EndDate, Source.EffectiveDate, Source.LastUpdateId, Source.LastUpdateDate, Source.ErrorCOdes, Source.Status)";

    private static string updateCommand = "UPDATE SET Target.ChannelCode = Source.ChannelCode, Target.DrmTerrDesc = Source.DrmTerrDesc, Target.IndDistrnId = Source.IndDistrnId," +
                                                "Target.StateCode = Source.StateCode, Target.ZipCode = Source.ZipCode, Target.EndDate = Source.EndDate, Target.EffectiveDate = Source.EffectiveDate," +
                                                "Target.LastUpdateId = Source.LastUpdateId, Target.LastUpdateDate = Source.LastUpdateDate, Target.ErrorCodes = Source.ErrorCodes," +
                                                "Target.Status = Source.Status ";

    public static int Update(List<ZipCodeTerritory> updates, Dictionary<object, string> errorList)
    {
        int results = 0;
        try
        {
            //Load updates into datatable
            DataTable table = LoadData(updates, true);

            //Script to create temp table
            string tmpTable =   "CREATE TABLE [dbo].[ZipCodeTerritoryTemp]( " +
                                "[ChannelCode] [char](1) NOT NULL, " +
                                "[DrmTerrDesc] [nvarchar](30) NOT NULL, " +
                                "[IndDistrnId] [char](3) NULL, " +
                                "[StateCode] [char](3) NOT NULL, " +
                                "[ZipCode] [char](9) NULL, " +
                                "[EndDate] [date] NOT NULL, " +
                                "[EffectiveDate] [date] NOT NULL, " +
                                "[LastUpdateId] [char](8) NULL, " +
                                "[LastUpdateDate] [date] NULL, " +
                                "[Id] [int] NULL, " +               
                                "[ErrorCodes] [varchar](255) NULL, " +
                                "[Status] [char](1) NULL)";

            using (SqlConnection connection = new SqlConnection(connString))
            {
                connection.Open();

                //Create temp table
                SqlCommand cmd = new SqlCommand(tmpTable, connection);
                cmd.ExecuteNonQuery();

                try
                {

                    using (SqlBulkCopy bulkCopy = new SqlBulkCopy(connection))
                    {
                        //Write to temp table
                        bulkCopy.DestinationTableName = "ZipCodeTerritoryTemp";
                        bulkCopy.WriteToServer(table);

                        //Merge changes in temp table with ZipCodeTerritory
                        string mergeSql = "merge ZipCodeTerritory as Target " +
                                          "using ZipCodeTerritoryTemp as Source " +
                                          "on " +
                                          "Target.Id = Source.Id " +
                                          "when matched then " +
                                          updateCommand +
                                          "when not matched then " +
                                          insertCommand + ";";

                        cmd.CommandText = mergeSql;
                        results = cmd.ExecuteNonQuery();
                    }
                }
                catch (Exception ex)
                {
                    SendEmail.ErrorMail(ex.Message);
                }
                finally
                {
                    //Drop temp table
                    SqlCommand final = new SqlCommand("DROP TABLE [dbo].[ZipCodeTerritoryTemp]", connection);
                    final.ExecuteNonQuery();
                }
            }
        }
        catch (Exception ex)
        {
            SendEmail.ErrorMail(ex.Message);
        }
        return results;
    }

Answer 1:

简短的回答是,你不能通过执行确定这MERGE语句,你必须之前执行检查这些碰撞MERGE

换句话说(我不能强调这一个就够了): 始终验证输入。

这里有两点,你可以做验证:您将数据批量复制到临时表之前,你合并临时表到目标之前。 根据您的数据问题的性质的数据命中服务器之前,你也许可以做你的大部分验证。

还有的我一般遇到有问题的三个主要类别MERGE语句:

  1. 键冲突(源重复行)
  2. 数据格式错误(例如日期的字符串表示,并没有转化为DATE正确)
  3. 约束失败(空在那里他们不允许,外键错误等等)

前两个一般可以前推数据到服务器检测到。 第三取决于你的约束性质......但我们一般可以解决这些他们到达服务器之前。

在您的数据键冲突可以通过(通过密钥对数据进行分组进行检测Id在这种情况下)。 让我们假设你有两个记录使用相同的Id值,但要在一个最高合并LastUpdateDate 。 这其中的一个选项:

var cleanupdates = 
    from update in updates
    group update by update.Id into grp
    select grp.OrderByDescending(u => u.LastUpdateDate).First();

如果你的约束问题都与空值,使用过滤掉那些有无效的空的记录where的条款。 如果他们与外键约束,这些密钥加载到一个列表,并对其进行筛选。 你可以涵盖了很多使用LINQ查询验证。

最重要的一点是,你了验证。 否则,你的MERGE会失败,你会不知道为什么。



Answer 2:

为了您的MERGE语句你缺少一个小东西

merge ZipCodeTerritory as Target " +
"using (SELECT * FROM ZipCodeTerritoryTemp) as Source " + --<-- need to select data
"on " +
"Target.Id = Source.Id " +
"when matched then " +
updateCommand +
"when not matched then " +
insertCommand + ";";

但是,在你走之前提前用这种方法阅读本有关与问题的文章,从阿龙贝特朗MERGE语句。

我建议你使用IF EXISTS方法是这样的......

更新

UPDATE T
SET  T.Col1 = S.Col1,
     T.Col2 = S.Col2,
     T.Col3 = S.Col3
FROM ZipCodeTerritory T INNER JOIN ZipCodeTerritoryTemp S
ON   T.id = S.id

插入

INSERT INTO ZipCodeTerritory(Col1, Col2, Col3, ....)
SELECT S.Col1, S.Col2, S.Col3, ....
FROM   ZipCodeTerritoryTemp S LEFT JOIN ZipCodeTerritory T
ON     S.ID = T.ID
WHERE  T.ID IS NULL 


文章来源: How to identify which row/rows are causing an error during a MERGE statement and why