Задать вопрос
@pawellrus

Как реализовать блокировки в базе данных MSSQL?

Добрый день. Пишу приложение на C#, которое работает с базой данных MSSQL.
Столкнулся со следующей проблемой. Допустим пользователь через SELECT загружает данные о записи в форму для редактирования. Другой пользователь в это же время удаляет эту запись. Соответственно, у первого пользователя в момент UPDATE возникнет ошибка. Или другой случай - если оба пользователя решат открыть одну и ту же запись на редактирование.

Так, как раньше, я подобными задачами не увлекался, хочется знать, в какую сторону гуглить для правильной реализации механизма блокировок базы данных в приложениях.
  • Вопрос задан
  • 663 просмотра
Подписаться 1 Оценить Комментировать
Пригласить эксперта
Ответы на вопрос 2
petermzg
@petermzg
Самый лучший программист
У вас просто неправильный подход.
Не нужно блокировать запись, нужно проверять ее версию/наличие при обновлении данных.

При вашем подходе, один заблокирует запись и уйдет обедать, работа остановилась.
Ответ написан
Комментировать
k1lex
@k1lex
Программист торг. сети. C# (WPF, WinForms), T-SQL
Может многие меня не поддержат. Но у меня стояла задача разблокировать редактирование, при этом оставив просмотр для остальных. Оставлять одну только проверку версии казалось мало, а реализовывать через транзацию побоялся: неизвестно как будет себя вести изоляция, если ею раньше не пользовался.
Реализация была через три дополнительных поля и пару процедур. Три на самом деле избыточно. Хватило бы и два. В общем этакий велосипед.
Поля в заголовке:
  • Идентификатор пользователя UserEditGUID uniqueidentifier
  • Дата когда пользователь начал редактировать BeginEdit datetime
  • Не особо нужное поле isEdit bit


И собственно процедура блокировки:
ALTER PROCEDURE [dbo].[LockUnLockOrder]
	@HeadGUID uniqueidentifier, @UserGUID  uniqueidentifier, @Lock bit
	
AS
BEGIN
	-- SET NOCOUNT ON added to prevent extra result sets from
	-- interfering with SELECT statements.
	SET NOCOUNT ON;
	
	if @HeadGUID is null or @UserGUID is null or @Lock is null 
	begin
	raiserror('Процедура LockUnLockOrder: Неинициализированный параметр',16,1)
	return -1
	end
if V_ORDER.dbo.CheckEditStatus(@HeadGUID,@UserGUID) = 1
begin
	if 	@Lock = 1			
	UPDATE [V_ORDER].[dbo].[OrderHeader]
	SET isEdit = 1
		,BeginEdit = getdate()
		,UserEdit = @UserGUID
	WHERE GUID = @HeadGUID
	else 
	if 	@Lock = 0			
	UPDATE [V_ORDER].[dbo].[OrderHeader]
	SET isEdit = 0
		,BeginEdit = null
		,UserEdit = null 
	WHERE GUID = @HeadGUID
	return 1
end
else raiserror('Невозможно изменить статус заказа',16,1)
end


И функция проверки возможности редактирования. Если пользователь открыл час назад, и не закрыл, то разрешение на редактирование выдается.
-- =============================================
-- Author:		k1lex
-- Create date: 20160818
-- Description:	функция возвращает разрешение редактирования заказа. 1 - можно редактировать. 0 - нельзя
-- =============================================
ALTER FUNCTION [dbo].[CheckEditStatus]
(
	-- Add the parameters for the function here
	@HeadGUID uniqueidentifier, @UserGUID  uniqueidentifier
)
RETURNS bit
AS
BEGIN
DECLARE @bit BIT

IF (
		SELECT COUNT(*)
		FROM [V_ORDER].[dbo].[OrderHeader] O WITH (NOLOCK)
		WHERE O.GUID = @HeadGUID
			AND (ISNULL(O.IsReadOnly, 0) = 1 OR (O.userEdit != @UserGUID AND O.IsEdit = 1 AND DATEDIFF(HOUR, BeginEdit, GETDATE()) < 1)	
			and [Status]=1
			)
		) = 0
	SET @bit = 1
ELSE
	SET @bit = 0

RETURN @bit
END


И поддержу Петр , что нужна проверка даты последнего изменения. На тот случай, если кто-то откроет, и вспомнит через три часа что открыл и отредактирует уже отредактированное.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы