Manipular Registros no BD

Delphi

23/05/2013

Pessoal, tenho um problema, e gostaria de saber se alguém ja tratou isso e de que forma.. o problema é o Seguinte

tenho uma tabela.. exemplo produtos, com 3 registros.. produto 1 2 e 3

meu banco esta em um servidor e tenho 3 pessoas acessando ao mesmo tempo

supondo que duas delas selecionem o produto 2 ao mesmo tempo para alteração..

a pessoa que salvar primeiro vai perder os dados alterados quando a segunda pessoa alterar!

fiz o seguinte, criei um flag na tabela produtos, caso uma pessoa acesse atribuo verdadeiro ao flag, e não deixo outra pessoa acessar o mesmo registro... essa é a melhor maneira de resolver o problema?? existe outra??
André Macedo

André Macedo

Curtidas 0

Respostas

Thiago Santana

Thiago Santana

23/05/2013

Nesse caso costumo ter um campo na tabela onde tenho a qtd total e a qtd reservada!
Reservada são os pedidos do cliente. Daí meu estoque sempre vai ser qtd total - reservada.
Existem diversas formas! Costumo fazer dessa forma e me atende perfeitamente
GOSTEI 0
Guilherme Wiethaus

Guilherme Wiethaus

23/05/2013

Ola André,

Não é a melhor maneira não. Pois se falhar na sua escrita ficará com um flag na tabela impedindo até mesmoq ue o proprio que escreveu acesse já que irá validar pelo flag que você impôs na sua regra de negócio.

O ideal é fazer através de transações, numa rede boa, a escrita é feita de forma rápida sem grandes atrasos e o bloqueio ao registro é muito rápido. Lembre que existem várias modalidades de bloqueios o que recomendo é o tipo Batch Optimistic que o bloqueio acontece no momento do post apenas e não no insert/edit. Com isto vale quem salvou por último o registro. Em rede ou sistema multi usuário isto é uma boa regra. Outra maneira é enviar um sinal de fumaça avisando o outro lado que você esta alterando o registro e ele não pode mexer nele...rsrs.. Brincadeirinha.

Uma outra forma em algumas regras de bancos é utilizar StoredProc, que também ao se entrar com um usuário fica esperando terminar para que libere aos outros executarem o mesmo procedimento armazenado.

Pode fazer um teste legal.

Faça uma trigger de inclusão, dentro dela um loop que realiza um loop de 10 segundos e um flag de entradas (grava em uma tabela) da trigger, fazendo uma condicional se ela estiver setada retorna exceção. Em uma janela T-SQL inclua um registro...vai ficar em loop por um tempo..tudo bem..deixa assim...
na outra janela, rapidamente inclua registro também....não deve retornar exceção ainda, e ficará esperando uma transação terminar...quando terminar a segunda janela que mandou o comando vai gerar a exceção no Trigger (gatilho). Isto é um troque para você entender o que esta acontecendo. Eu fiz um script ai embaixo, que eu proprio testei e explana exatamente o que te expliquei.

Abaixo código para criar as tabelas necessárias para o teste:

SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE TABLE [dbo].[A](
	[Valor] [int] NULL
) ON [PRIMARY]
GO

SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE TABLE [dbo].[B](
	[Flag] [int] NULL
) ON [PRIMARY]

GO

INSERT [dbo].[B] ([Flag]) VALUES (0)

GO

SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE TRIGGER [dbo].[TESTE_A] 
   ON  [dbo].[A]
   AFTER INSERT
AS 
BEGIN
BEGIN TRY
	-- SET NOCOUNT ON added to prevent extra result sets from
	-- interfering with SELECT statements.
	SET NOCOUNT ON;

	DECLARE @X	INT;

    -- Insert statements for trigger here

	IF EXISTS (SELECT * FROM B WHERE Flag = 1) 
	BEGIN
		RAISERROR ('Erro 001: Esta setado o Flag.', 16, 1);
		RETURN;
	END;

	UPDATE B SET Flag = 1 FROM B;

	--SET @x = 0;

	SELECT GETDATE();
	WAITFOR DELAY '00:00:10.000'; -- Espera por 10 segundos
	SELECT GETDATE();

	--SET @x = @x + 1;


END TRY
BEGIN CATCH
	DECLARE @ErrorMessage NVARCHAR(4000);
	DECLARE @ErrorSeverity INT;
	DECLARE @ErrorState INT;

	SELECT @ErrorMessage = ERROR_MESSAGE();
	SELECT @ErrorSeverity = ERROR_SEVERITY();
	SELECT @ErrorState = ERROR_STATE();

	ROLLBACK TRAN;

	RAISERROR (	@ErrorMessage, -- Message text.
				@ErrorSeverity, -- Severity.
				@ErrorState -- State. 
				);
	PRINT 'ERROR_MESSAGE: ' + @ErrorMessage + ' ERROR_SEVERITY ' + CAST(@ErrorSeverity AS VARCHAR(50)) + ' ERROR_STATE: ' + @ErrorState;
	RETURN;
END CATCH
END

GO


Numa janela coloque o seguinte:

UPDATE B SET Flag = 0 FROM B;
INSERT INTO A (Valor) VALUES (1);


Na outra janela coloque:

INSERT INTO A (Valor) VALUES (2);


Troque rapidamente entre uma janela e outra e para executar o código no SQL Server 2008 pressione F5.

Verificará que um fica executando 10 segundos enquanto o outro ainda não entrou no trigger (gatilho) até que o primeiro termine. Este é um exemplo típico de bloqueio para evitar que um execute enquanto outro está terminando um processo todo.

Abraços
GOSTEI 0
Guilherme Wiethaus

Guilherme Wiethaus

23/05/2013

Esqueci de mencionar. execute os códigos na ordem como foi colocado.

Abraços
GOSTEI 0
POSTAR