Duda sobre nivel de aislamiento

08/09/2007 - 12:14 por Vicente Flich | Informe spam
Hola a todos:

Estamos migrando una aplicacion para que trabaje contra un servidor SLQ
Server 2005. La duda es sobre el nivel de aislamiento de las transacciones.
Lo explico con un ejemplo. En la aplicacion gestiona clientes, cada uno
logicamente tiene su codigo de cliente (por ejemplo un campo numerico). Lo
que queremos es que al crear un nuevo cliente se busque el ultimo codigo de
cliente, le sumamos 1 y añadimos el nuevo cliente.

El problema viene cuando se trabaja en multiusuario. Tenemos que evitar que
se creen 2 codigos de cliente iguales.

Para ejemplificarlo, en lugar de poner nuestro codigo en C++, os pongo el
siguiente 'pseudocodigo':

use BASEDEDATOS
set transaction isolation level serializable
begin transaction

// Punto 1: Obtenemos el ultimo registro
select top(1) codigo from clientes order by codigo desc
nuevocodigo = clientes.codigo+1

// Punto 2: Grabamos
insert into clientes (codigo) values(Nuevocodigo)

commit

En el pseudocodigo anterior, si 2 procesos se ejecutan simultaneamente,
ambos ejecutan el Punto1 a la vez... los 2 procesos dan con el mismo codigo,
duplicando el codigo de articulo.

He probado con todos los niveles de aislamiento: READ COMMITTED, UNCOMMITED,
SERIALIZABLE, REPEATABLE READS, SNAPSHOT, pero en todos falla.

Supongo que esto debe hacerse mejor de otra forma (hacer el codigo como
campo identity por ejemplo), pero no me sirve, ya que como os he comentado
estamos migrando una aplicacion (programada al estilo XBase) y es una
aplicacion bastante grande, lo tengo todo migrado a excepcion de este
problema y no es solo al crear un cliente, sino en muhos otros puntos del
programa.

Gracias por adelantado.

Saludos,
Vicente Flich
Flifeca Aplicaciones Informáticas
 

Leer las respuestas

#1 Alejandro Mesa
09/09/2007 - 19:32 | Informe spam
Hola Vicente,

Hay varias maneras de lograr esto.

1 - usar el el hint de tabla (updlock) o (tablelock) en la sentencia que
selecciona
2 - usar una tabla especifica para mantener esta numeración.

Te dejo de tarea leer sobe el primer metodo, y te paso un ejemplo sobre el
segundo.

use mi_db
go

create table dbo.codigo (
nombre_tabla sysname not null primary key,
codigo int not null default(0),
incremento smallint not null default(1)
)
go

insert into dbo.codigo (nombre_tabla, codigo, incremento) values('clientes',
0, 1)
go

create procedure dbo.usp_proximo_codigo
@nombre_table sysname,
@codigo int output
as

set nocount on

update dbo.codigo
set @codigo = codigo = codigo + incremento
where nombre_tabla = @nombre_tabla

return @@error
go

declare @codigo int
declare @rv int
declare @error int
declare @nombre varchar(50)

set @nombre = 'Microsoft'

begin transaction

exec @rv = dbo.usp_proximo_codigo 'clientes', @codigo output

set @error = coalesce(nullif(@rv, 0), @@error)

if @error = 0
begin
insert into dbo.clientes(codigo, nombre) values(@codigo, @nombre)

if @@error != 0
begin
raiserror('error seleccionando proximo codigo.', 16, 1)
goto ErrorHandler
end
end
else
begin
raiserror('error insertando cliente.', 16, 1)
goto ErrorHandler
end

commit transaction

return 0

ErrorHandler:
if @@trancount > 0
rollback transaction

return -1
go

Este codigo lo escribo mientras te respondo, asi que si algo no funciona
entonces dejanos saber.


AMB

"Vicente Flich" wrote:

Hola a todos:

Estamos migrando una aplicacion para que trabaje contra un servidor SLQ
Server 2005. La duda es sobre el nivel de aislamiento de las transacciones.
Lo explico con un ejemplo. En la aplicacion gestiona clientes, cada uno
logicamente tiene su codigo de cliente (por ejemplo un campo numerico). Lo
que queremos es que al crear un nuevo cliente se busque el ultimo codigo de
cliente, le sumamos 1 y añadimos el nuevo cliente.

El problema viene cuando se trabaja en multiusuario. Tenemos que evitar que
se creen 2 codigos de cliente iguales.

Para ejemplificarlo, en lugar de poner nuestro codigo en C++, os pongo el
siguiente 'pseudocodigo':

use BASEDEDATOS
set transaction isolation level serializable
begin transaction

// Punto 1: Obtenemos el ultimo registro
select top(1) codigo from clientes order by codigo desc
nuevocodigo = clientes.codigo+1

// Punto 2: Grabamos
insert into clientes (codigo) values(Nuevocodigo)

commit

En el pseudocodigo anterior, si 2 procesos se ejecutan simultaneamente,
ambos ejecutan el Punto1 a la vez... los 2 procesos dan con el mismo codigo,
duplicando el codigo de articulo.

He probado con todos los niveles de aislamiento: READ COMMITTED, UNCOMMITED,
SERIALIZABLE, REPEATABLE READS, SNAPSHOT, pero en todos falla.

Supongo que esto debe hacerse mejor de otra forma (hacer el codigo como
campo identity por ejemplo), pero no me sirve, ya que como os he comentado
estamos migrando una aplicacion (programada al estilo XBase) y es una
aplicacion bastante grande, lo tengo todo migrado a excepcion de este
problema y no es solo al crear un cliente, sino en muhos otros puntos del
programa.

Gracias por adelantado.

Saludos,
Vicente Flich
Flifeca Aplicaciones Informáticas

Preguntas similares