为什么我在 MERGE 语句上收到间歇性重复更新错误,而这是不可能的

本文关键字:错误 更新 不可能 语句 MERGE 为什么 | 更新日期: 2023-09-27 18:34:35

我有以下代码:

// accessedKeys is a HashSet<string>
if (accessedKeys.Count > 0)
{
    string queryFormat = @"SET NOCOUNT ON;
        DECLARE @updated_resources TABLE (name varchar(512))
        INSERT INTO @updated_resources (name) VALUES ('{0}')
        MERGE resource_access WITH (HOLDLOCK) AS target
        USING @updated_resources AS source
        ON target.name = source.name
        WHEN MATCHED THEN
        UPDATE SET last_access = GETDATE()
        WHEN NOT MATCHED THEN
        INSERT (name, last_access) VALUES (source.name, GETDATE());";
    string entries = String.Join("'),('", accessedKeys.Select(s => s.Replace("'", "''")));
    string query = String.Format(queryFormat, entries);
    using (var connection = new SqlConnection(...))
    {
        connection.Open();
        using (var cmd = connection.CreateCommand())
        {
            cmd.CommandType = System.Data.CommandType.Text;
            cmd.CommandText = query;
            cmd.ExecuteNonQuery();
        }
    }
    accessedKeys.Clear();
}

resource_access是这样创建的:

CREATE TABLE resource_access (
    name varchar(512) primary key, 
    last_access smalldatetime not null
)

在执行语句期间,我间歇性地收到以下异常:

The MERGE statement attempted to UPDATE or DELETE the same row more than 
once. This happens when a target row matches more than one source row. A 
MERGE statement cannot UPDATE/DELETE the same row of the target table 
multiple times. Refine the ON clause to ensure a target row matches at most
one source row, or use the GROUP BY clause to group the source rows.

@updated_resources.name是从不同字符串列表中填充的,resource_access.name是主键,这意味着它也是唯一的。我正在合并两个表的name列,两者都不能有重复的条目。

如果我中断异常,accessedKeys.Distinct().Count等于accessedKeys.Count.如果我执行SELECT COUNT(DISTINCT(name)) FROM resource_access,它等于表中的行数。

为什么我有时会收到此错误?

为什么我在 MERGE 语句上收到间歇性重复更新错误,而这是不可能的

您是否使用不区分大小写的搜索检查过名称是否不同?您提供的用于查找非重复值的 C# 区分大小写。根据您在数据库中设置的排序规则,MERGE 将不区分大小写进行查找。因此,如果您的表具有名称"Bob",并且传入数据同时具有"Bob"和"BOB",则会收到此错误。

试试这个:

accessedKeys.Distinct(StringComparer.CurrentCultureIgnoreCase).Count()