【问题标题】:Executing merge statement via C# SqlCommand not working通过 C# SqlCommand 执行合并语句不起作用
【发布时间】:2014-02-06 00:18:22
【问题描述】:

我第一次尝试使用临时表和 MERGE 语句通过 C# 中的 SqlCommand 对象更新 SQL 表。我正在开发的程序旨在首先将一组非常大的记录(超过 20k+)导出到 Excel 电子表格中。然后,用户可以对特定值进行搜索和替换,并根据需要更新尽可能多的记录中的字段。

然后我要做的是获取该电子表格,用它填充DataTable,然后使用SqlBulkCopyDataTable 填充一个临时SQL 表。

然后我使用MERGE 语句来更新数据库中仍然存在的行。

但是,我遇到的问题是我在 ZipCodeTerritory 表上的唯一约束不断被触发,给我以下错误消息:

无法在具有唯一索引“UQ_ChannelStateEndDateZipCodeISNULL”的对象“dbo.ZipCodeTerritory”中插入重复的键行。重复键值为 (9, CA , 94351 , 9999-12-31)。

这让我相信,不知何故,要么UPDATE 语句没有被执行,要么我以某种方式在使用ON 关键字的语句部分错误地加入了表。唯一约束仅在INSERT 语句或UPDATEChannelCodeStateCodeZipCodeEndDate 字段期间触发。我正在对IndDistrnId 字段进行大规模更新,并彻底检查了电子表格。

再次,这是我第一次尝试这种技术,所以任何帮助/建议将不胜感激。谢谢

C#

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 ";

//Load updates into datatable
DataTable table = LoadData(updates);

//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] IDENTITY(1,1) NOT NULL, " +
                    "[ErrorCodes] [varchar](255) NULL, " +
                    "[Status] [char](1) NULL, " +
                    "CONSTRAINT [PK_ZipCodeTerritoryTemp] PRIMARY KEY NONCLUSTERED  " +
                "( " +
                    "[Id] ASC " +
                ")WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 90) ON [PRIMARY] " +
                ") ON [PRIMARY]";

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 + ";";

            cmd.CommandText = mergeSql;
            int results = cmd.ExecuteNonQuery();

            //Drop temp table
            cmd.CommandText = "DROP TABLE [dbo].[ZipCodeTerritoryTemp]";
            cmd.ExecuteNonQuery();
        }
    }
    catch (Exception)
    {
        throw;
    }
    finally
    {
        //Drop temp table
        SqlCommand final = new SqlCommand("DROP TABLE [dbo].[ZipCodeTerritoryTemp]", connection);
        final.ExecuteNonQuery();
    }
}

SQL

为了便于阅读,这里是我在 SQL Server Management Studio 中编写的 MERGE 语句。我将它复制到 C# 中。仅供参考 - 在 Management Studio 中运行此语句并收到完全相同的错误消息。

MERGE INTO ZipCodeTerritory as Target
USING ZipCodeTerritoryTemp as Source
ON Target.Id = Source.Id

WHEN MATCHED THEN

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;

【问题讨论】:

  • 旁注:您不需要在行尾的所有, " +。您可以使用以@ 开头的verbatim string literal。然后您可以直接将 sql 复制/粘贴到 Visual Studio 中。
  • 我会尝试删除临时表的 DROP 并检查那里真正插入了什么。从 excel 工作表导入是否有可能加载一些虚假行?
  • 我在代码运行时停止了代码以检查临时表中的数据。从电子表格导入的所有内容都在那里,并更新了IndDistrnId 字段。现在我正在 SQL Server Management Studio 中使用临时表/数据进行试验,试图让 MERGE 工作。
  • 该异常涉及违反了 主表 上的唯一索引(由许多列组成)。但是,要查找两个表之间的匹配项,您需要使用不同的键。您是否检查过导入的值为(9, CA , 94351 , 9999-12-31) 的行是否与主表中唯一索引上具有相同值的不同行冲突?
  • 我刚刚意识到在临时表上设置了IDENTITY(1,1,) 属性,因此Id 字段无法匹配。检查这是否是(唯一..)问题。

标签: c# sql sql-server merge sqlcommand


【解决方案1】:

问题最终是在临时表中的Id 字段上设置了IDENTITY 属性。删除它后,我能够毫无错误地运行MERGE。现在是临时表:

//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] NOT NULL, " +               //DO NOT GIVE THE PK OF THE TEMP TABLE AN IDENTITY(1,1,) PROPRETY
                    "[ErrorCodes] [varchar](255) NULL, " +
                    "[Status] [char](1) NULL, " +
                    "CONSTRAINT [PK_ZipCodeTerritoryTemp] PRIMARY KEY NONCLUSTERED  " +
                "( " +
                    "[Id] ASC " +
                ")WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 90) ON [PRIMARY] " +
                ") ON [PRIMARY]";

【讨论】:

  • 您也不需要ID 列和PK。这仅取决于您如何设置MERGE 命令MATCH 条件。
猜你喜欢
  • 1970-01-01
  • 2020-01-30
  • 2014-09-01
  • 2023-03-12
  • 2017-09-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-06-10
相关资源
最近更新 更多