Допустим, что UserA и UserB имеют приложение открыто и работают с данными того же типа. UserA вставляет запись в таблицу со значением 10 (PrimaryKey = 'A'), UserB в настоящее время не видит введенное значение UserA и пытается вставить новое значение 20 (PrimaryKey = 'A'). То, что я хотел в этой ситуации, было исключение DBConcurrencyException, но вместо этого у меня есть нарушение первичного ключа. Я понимаю, почему, но я понятия не имею, как это разрешить. Какова хорошая практика для решения таких обстоятельств? Я не хочу сливаться перед обновлением базы данных, потому что хочу, чтобы ошибка информировала пользователя о том, что несколько пользователей обновили эти данные.Нарушение ограничения первичного ключа, несколько пользователей
ответ
То, что я хотел в этой ситуации был DBConcurrencyException, но вместо того, что у меня есть первичный ключ нарушение. Я понимаю, почему
Это правильное исключение для этой ситуации. Вы говорите, что хотите сообщить пользователю, что это значение уже вставлено, поэтому просто перехватите исключение первичного ключа и затем верните дружественное сообщение.
+1 это самый простой способ справиться с проблемой. –
Одно решение может включать в себя INSTEAD OF INSERT
Триггер на столе.
Здесь вы будете переопределять инструкцию INSERT в своем триггере. У вас будет шанс на RAISEERROR, когда вы обнаружите, что строка или значение уже существуют для Первичного ключа «A».
Зачем добавлять сложность и замедлять работу? Как следует из следующего правильного предложения, исключение гарантировано, что оно существует и является правильным: в худшем случае он может иметь код приложения, который поймает его и реконструирует тот, который ему больше всего нравится (даже если я действительно не понимаю, почему он хотел бы сделать это так). –
@ p.marino: действительно - это только один подход. YMMV. –
Это дизайнерское решение, которое вы должны сделать - хотите ли вы использовать Пессимистический или Оптимистический замок?
Я слишком ленив - quoted from this thread:
Эти методики, используемые для решения вопросов многопользовательских. Как можно справиться с тем, что 2 человека хотят обновить одну и ту же запись одновременно?
Do Nothing
- Пользователь 1 читает запись
- Пользователь 2 читает ту же запись
- пользователя 1 обновления, что запись
- Пользователь 2 обновляет ту же запись
Пользователь 2 теперь переписал изменения, сделанные пользователем 1 , Они полностью исчезли, как будто их никогда не было. Это называется «потерянным обновлением».
Заблокировать запись, когда прочитано. Пессимистическая замок
- пользователя 1 считывает запись и блокирует его, поставив эксклюзивную блокировку на запись (ДЛЯ пункта UPDATE)
- User 2 попытки чтения и заблокировать ту же запись, но Теперь нужно ждать за пользователем 1
- Пользователь 1 обновляет запись (и, конечно, обязывает)
- Пользователь 2 теперь может прочитать запись с изменениями, которые пользователь 1 из
- Пользователь 2 обновляет запись полностью с изменениями от пользователя 1
Потерянное проблема обновления решена. Проблема с этим подходом - параллелизм. Пользователь 1 блокирует запись, которую они могут никогда не обновлять. Пользователь 2 не может даже прочитать запись, потому что для чтения также нужен эксклюзивный замок. Такой подход требует слишком много эксклюзивной блокировки, и блокировки живут слишком долго (часто через пользовательское управление - абсолютное no-no).Этот подход почти никогда не реализован.
Оптимистичный замок. Оптимистическая блокировка не использует эксклюзивные замки при считывании. Вместо этого во время обновления выполняется проверка, чтобы убедиться, что запись не была изменена с момента ее чтения. Это можно сделать, проверив каждое поле в таблице. т.е. UPDATE Table1 SET Col2 = x WHERE COL1 =: OldCol1 AND COl2 =: OldCol AND Col3 =: OldCol3 AND ... Есть, конечно, несколько недостатков. Во-первых, вы должны иметь SELECTed каждый столбец из таблицы. Во-вторых, вы должны создать и выполнить это массовое заявление. Большинство людей реализуют это, вместо этого, через один столбец, обычно называемый временной меткой. Этот столбец используется для других целей, чем реализация оптимистического параллелизма. Это может быть число или дата. Идея заключается в том, что ему присваивается значение, когда строка вставлена. Всякий раз, когда запись считывается, также считывается столбец временной метки. Когда выполняется обновление, проверяется столбец временной метки. Если он имеет то же значение в UPDATE, как и при чтении, тогда все хорошо, UPDATE выполняется и изменена метка времени!. Если значение времени метки отличается во время UPDATE, тогда пользователю возвращается ошибка: они должны перечитать запись, повторно внести изменения и попытаться обновить запись снова.
- пользователя 1 считывает запись, в том числе временной метки 21
- пользователем 2 считывает запись, в том числе временной метки 21
- пользователь 1 пытается обновить запись. Временная метка в файле (21) совпадала с меткой времени в базе данных (21), поэтому обновление выполняется, а метка времени обновляется (22).
- Пользователь 2 пытается обновить запись. Временная метка в руке (21) не соответствует отметке времени в базе данных (22), поэтому возвращается ошибка. Пользователь 2 должен теперь перечитать запись, включая новые временные метки (22) и изменения пользователя 1, повторно применить их изменения и повторить попытку обновления.
За исключением того, что эти два метода применяются к обновлениям существующих строк. Речь идет о вставках с одним и тем же первичным ключом, в данном случае это БД, которое вызывает исключение, а оптимистичная/пессимистическая блокировка на самом деле не применяется. –
@ p.marino: Пессимистичная/Оптимистическая блокировка предназначена для борьбы с параллелизмом, что больше, чем обновлений. –
Как вы могли заметить, пример, который вы (лениво) скопировали, подробно расскажет о * updates *. И на самом деле оптимистическая блокировка часто реализуется путем добавления дополнительного поля с числовым значением, которое будет увеличено первым, кто обратится к записи, чтобы обновить ее. Например, это обычная стратегия с веб-приложениями с хранилищем на базе БД. И это имеет мало общего с заявленной проблемой. –
Если вы получаете нарушения PK, когда одновременно работающих пользователей вставить НОВЫЕ записи, то один из двух происходит:
нарушение происходит на естественном ключе, ключ, который имеет ценность для бизнеса, как имя пользователя или аналогичный. Нарушение ПК происходит из-за недостатка бизнес-процесса, т.е. два разных оператора пытаются вставить один и тот же элемент бизнеса. Логика реагирования полностью зависит от конкретных бизнес-правил, и мы не можем давать никаких советов.
Нарушение происходит на суррогатном ключе, т.е. идентификатор, такой как CustomerID или аналогичный. В этом случае недостаток полностью зависит от кода приложения, так как он использует недостающий алгоритм для генерации новых идентификаторов. Опять же, никакие действительные рекомендации не могут быть даны без понимания того, как генерируются новые идентификаторы.
Где первичный ключ сгенерирован? DB, SP .NET Code? –