Почему я получаю периодически повторяющиеся ошибки обновления в операторе MERGE, где это не должно быть возможно?

1

У меня есть следующий код:

// 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, он равен количеству строк в таблице.

Почему я иногда получаю эту ошибку?

  • 0
    Я попытался бы сделать это с некоторыми фиктивными данными, посмотреть, если вы не можете сузить, где и как это происходит. т. е. иметь 'Bob' в таблице resource_access, а затем иметь оба 'Bob' и 'BOB' в наборе accessedKeys, чтобы подтвердить, что это или не является причиной ошибки. Надеюсь, это имеет смысл.
Теги:
sql-server
tsql

1 ответ

2
Лучший ответ

Вы проверили, что имена отличаются от поиска без учета регистра? С#, который вы указали для поиска различных значений, зависит от регистра. В зависимости от настроек, которые вы установили в своей базе данных, MERGE будет делать свой регистр поиска нечувствительным. Таким образом, если ваша таблица имеет имя "Боб", и ваши входящие данные имеют как "Боб", так и "BOB", вы получите эту ошибку.

Попробуйте это вместо этого:

accessedKeys.Distinct(StringComparer.CurrentCultureIgnoreCase).Count()
  • 0
    Спасибо, я не уверен, почему я не подумал об этом.
  • 0
    Это будет потому, что вы человек (по крайней мере, я так полагаю).

Ещё вопросы

Сообщество Overcoder
Наверх
Меню