TRANSACCIONES SQL.pdf

download TRANSACCIONES SQL.pdf

of 7

Transcript of TRANSACCIONES SQL.pdf

  • 7/22/2019 TRANSACCIONES SQL.pdf

    1/7

    Bases de datos en castellano. Transacciones en SQL Server bases de datos mysql oracle sqlite access mssql

    Transacciones en SQL Serverutor:Cesar Manivesa (manivesa)

    1 . Introduccin

    2 . Sentencias para una transaccin

    3 . Transacciones anidadas

    4 . Un par de ejemplos ms

    5 . Transacciones y procedimientos almacenados.

    Transacciones en SQL Server

    Entre las habilidades de todo Sistema Gestor de Bases de Datos Relaciones tiene que estar la de permitir al programador crear transacciones. Y

    aunque el SQL Server nos permite trabajar con transacciones de manera sencilla y eficaz siempre hay dificultades...

    ntroduccin

    Una transaccin es un conjunto de operaciones que van a ser tratadas como una nica unidad. Estas transacciones deben cumplir 4 propiedades

    undamentales comnmente conocidas como ACID (atomicidad, coherencia, asilamiento y durabilidad).

    La transaccin ms simple en SQL Server es una nica sentencia SQL. Por ejemplo una sentencia como esta:

    UPDATE Products SET UnitPrice=20 WHERE ProductName ='Chai'

    Es una transaccin. (Como siempre ejemplos de Northwind) Esta es una transaccin 'autocommit', una transaccin autocompletada.

    Cuando enviamos esta sentencia al SQL Server se escribe en el fichero de transacciones lo que va a ocurrir y a continuacin realiza los cambios

    necesarios en la base de datos. Si hay algn tipo de problema al hacer esta operacin el SQL Server puede leer en el fichero de transacciones lo que se

    estaba haciendo y si es necesario puede devolver la base de datos al estado en el que se encontraba antes de recibir la sentencia.

    Por supuesto este tipo de transacciones no requieren de nuestra intervencin puesto que el sistema se encarga de todo. Sin embargo si hay que realizar

    varias operaciones y queremos que sean tratadas como una unidad tenemos que crear esas transacciones de manera explcita.

    Sentencias para una transaccin

    Como decamos una transaccin es un conjunto de operaciones tratadas como una sola. Este conjunto de operaciones debe marcarse como

    ransaccin para que todas las operaciones que la conforman tengan xito o todas fracasen.

    La sentencia que se utiliza para indicar el comienzo de una transaccin es 'BEGIN TRAN'. Si alguna de las operaciones de una transaccin falla hay quedeshacer la transaccin en su totalidad para volver al estado inicial en el que estaba la base de datos antes de empezar. Esto se consigue con la

    sentencia 'ROLLBACK TRAN'.

    Si todas las operaciones de una transaccin se completan con xito hay que marcar el fin de una transaccin para que la base de datos vuelva a estar

    en un estado consistente con la sentencia 'COMMIT TRAN'.

    Un ejemplo

    Trabajaremos con la base de datos Northwind en nuestros ejemplos. Vamos a realizar una transaccin que modifica el precio de dos productos de la

    base de datos.

    USE NorthWind

    DECLARE @Error int

    --Declaramos una variable que utilizaremos para almacenar un posible cdigo de error

    http://www.programacion.net/bbdd/articulo/man_transacciones/ (1 de 7)17/09/2008 12:27:14

    http://www.programacion.net/bbdd/autor/190/http://www.programacion.net/bbdd/autor/190/
  • 7/22/2019 TRANSACCIONES SQL.pdf

    2/7

    Bases de datos en castellano. Transacciones en SQL Server bases de datos mysql oracle sqlite access mssql

    BEGIN TRAN

    --Iniciamos la transaccin

    UPDATE Products SET UnitPrice=20 WHERE ProductName ='Chai'

    --Ejecutamos la primera sentencia

    SET @Error=@@ERROR

    --Si ocurre un error almacenamos su cdigo en @Error

    --y saltamos al trozo de cdigo que deshara la transaccin. Si, eso de ah es un

    --GOTO, el demonio de los programadores, pero no pasa nada por usarlo

    --cuando es necesarioIF (@Error0) GOTO TratarError

    --Si la primera sentencia se ejecuta con xito, pasamos a la segunda

    UPDATE Products SET UnitPrice=20 WHERE ProductName='Chang'

    SET @Error=@@ERROR

    --Y si hay un error hacemos como antes

    IF (@Error0) GOTO TratarError

    --Si llegamos hasta aqu es que los dos UPDATE se han completado con

    --xito y podemos "guardar" la transaccin en la base de datos

    COMMIT TRAN

    TratarError:

    --Si ha ocurrido algn error llegamos hasta aqu

    If @@Error0 THEN

    BEGIN

    PRINT 'Ha ecorrido un error. Abortamos la transaccin'

    --Se lo comunicamos al usuario y deshacemos la transaccin

    --todo volver a estar como si nada hubiera ocurrido

    ROLLBACK TRAN

    END

    Como se puede ver para cada sentencia que se ejecuta miramos si se ha producido o no un error, y si detectamos un error ejecutamos el bloque de

    cdigo que deshace la transaccin.

    Hay una interpretacin incorrecta en cuanto al funcionamiento de las transacciones que esta bastante extendida. Mucha gente cree que si tenemos

    varias sentencias dentro de una transaccin y una de ellas falla, la transaccin se aborta en su totalidad.

    Nada ms lejos de la realidad! Si tenemos dos sentencias dentro de una transaccin.

    USE NorthWind

    BEGIN TRANUPDATE Products SET UnitPrice=20 WHERE ProductName='Chang'

    UPDATE Products SET UnitPrice=20 WHERE ProductName='Chang'

    COMMIT TRAN

    Estas dos sentencias se ejecutarn como una sola. Si por ejemplo en medio de la transaccin (despus del primer update y antes del segundo) hay un

    corte de electricidad, cuando el SQL Server se recupere se encontrar en medio de una transaccin y, o bien la termina o bien la deshace, pero no se

    quedar a medias.

    El error est en pensar que si la ejecucin de la primera sentencia da un error se cancelar la transaccin. El SQL Server slo se preocupa de ejecutar

    as sentencias, no de averiguar si lo hacen correctamente o si la lgica de la transaccin es correcta. Eso es cosa nuestra.

    Por eso en el ejemplo que tenemos ms arriba para cada sentencia de nuestro conjunto averiguamos si se ha producido un error y si es as actuamos

    en consecuencia cancelando toda la operacin.

    http://www.programacion.net/bbdd/articulo/man_transacciones/ (2 de 7)17/09/2008 12:27:14

  • 7/22/2019 TRANSACCIONES SQL.pdf

    3/7

    Bases de datos en castellano. Transacciones en SQL Server bases de datos mysql oracle sqlite access mssql

    Transacciones anidadas

    Otra de las posibilidades que nos ofrece el SQL Server es utilizar transacciones anidadas. Esto quiere decir que podemos tener transacciones dentro de

    ransacciones, es decir, podemos empezar una nueva transaccin sin haber terminado la anterior.

    Asociada a esta idea de anidamiento existe una variable global @@TRANCOUNT que tiene valor 0 si no existe ningn nivel de anidamiento, 1 si hay

    una transaccin anidada, 2 si estamos en el segundo nivel de anidamiento. y as sucesivamente.

    La dificultad de trabajar con transacciones anidadas est en el comportamiento que tienen ahora las sentencias 'COMMIT TRAN' y 'ROLLBACK TRAN'

    ROLLBACK TRAN:Dentro de una transaccin anidada esta sentencia deshace todas las transacciones internas hasta la instruccin

    BEGIN TRANSACTION ms externa.

    COMMIT TRAN:Dentro de una transaccin anidada esta sentencia nicamente reduce en 1 el valor de @@TRANCOUNT, pero no

    "finaliza" ninguna transaccin ni "guarda" los cambios. En el caso en el que @@TRANCOUNT=1 (cuando estamos en la ltima transaccin)

    COMMIT TRAN hace que todas las modificaciones efectuadas sobre los datos desde el inicio de la transaccin sean parte permanente de la

    base de datos, libera los recursos mantenidos por la conexin y reduce @@TRANCOUNT a 0.

    Como siempre un ejemplo es lo mejor para entender como funciona.

    CREATE TABLE Test (Columna int)

    GO

    BEGIN TRAN TranExterna -- @@TRANCOUNT ahora es 1

    SELECT 'El nivel de anidamiento es', @@TRANCOUNT

    INSERT INTO Test VALUES (1)

    BEGIN TRAN TranInterna1 -- @@TRANCOUNT ahora es 2.

    SELECT 'El nivel de anidamiento es', @@TRANCOUNT

    INSERT INTO Test VALUES (2)

    BEGIN TRAN TranInterna2 -- @@TRANCOUNT ahora es 3.

    SELECT 'El nivel de anidamiento es', @@TRANCOUNT

    INSERT INTO Test VALUES (3)

    COMMIT TRAN TranInterna2 -- Reduce @@TRANCOUNT a 2.-- Pero no se guarda nada en la base de datos.

    SELECT 'El nivel de anidamiento es', @@TRANCOUNT

    COMMIT TRAN TranInterna1 -- Reduce @@TRANCOUNT a 1.

    -- Pero no se guarda nada en la base de datos.

    SELECT 'El nivel de anidamiento es', @@TRANCOUNT

    COMMIT TRAN TranExterna -- Reduce @@TRANCOUNT a 0.

    -- Se lleva a cabo la transaccin externa y todo lo que conlleva.

    SELECT 'El nivel de anidamiento es', @@TRANCOUNT

    SELECT * FROM Test

    Por cierto que lo de usar nombre para las transacciones es por claridad, puesto que COMMIT TRAN como ya hemos dicho solamente reduce en 1 elvalor de @@TRANCOUNT.

    Veamos ahora un ejemplo de transaccin anidada con ROLLBACK TRAN

    BEGIN TRAN TranExterna -- @@TRANCOUNT ahora es 1

    SELECT 'El nivel de anidamiento es', @@TRANCOUNT

    INSERT INTO Test VALUES (1)

    BEGIN TRAN TranInterna1 -- @@TRANCOUNT ahora es 2.

    SELECT 'El nivel de anidamiento es', @@TRANCOUNT

    INSERT INTO Test VALUES (2)

    BEGIN TRAN TranInterna2 -- @@TRANCOUNT ahora es 3.

    SELECT 'El nivel de anidamiento es', @@TRANCOUNT

    INSERT INTO Test VALUES (3)

    ROLLBACK TRAN --@@TRANCOUNT es 0 y se deshace

    --la transaccin externa y todas las internas

    http://www.programacion.net/bbdd/articulo/man_transacciones/ (3 de 7)17/09/2008 12:27:14

  • 7/22/2019 TRANSACCIONES SQL.pdf

    4/7

    Bases de datos en castellano. Transacciones en SQL Server bases de datos mysql oracle sqlite access mssql

    SELECT 'El nivel de anidamiento es', @@TRANCOUNT

    SELECT * FROM Test

    En este caso no se inserta nada puesto que el ROLLBACK TRAN deshace todas las transacciones dentro de nuestro anidamiento hasta la transaccin

    ms externa y adems hace @@TRANCOUNT=0

    Supone este funcionamiento asimtrico del COMMIT y del ROLLBACK un problema? Pues la verdad es que no. La manera de tratar las transacciones

    por el SQL Server es la que nos permite programar de manera natural los anidamientos. De todos modos, si queremos ir un poco ms lejos hay una

    cuarta sentencia para trabajar con transacciones: SAVE TRAN

    Save Tran

    Esta sentencia crea un punto de almacenamiento dentro de una transaccin. Esta marca sirve para deshacer una transaccin en curso slo hasta ese

    punto. Por supuesto nuestra transaccin debe continuar y terminar con un COMMIN TRAN (o los que hagan falta) para que todo se guarde o con un

    ROLLBACK TRAN para volver al estado previo al primer BEGIN TRAN.

    BEGIN TRAN TranExterna -- @@TRANCOUNT ahora es 1

    SELECT 'El nivel de anidamiento es', @@TRANCOUNT

    INSERT INTO Test VALUES (1)

    BEGIN TRAN TranInterna1 -- @@TRANCOUNT ahora es 2.

    SELECT 'El nivel de anidamiento es', @@TRANCOUNT

    INSERT INTO Test VALUES (2)

    SAVE TRAN Guadada

    BEGIN TRAN TranInterna2 -- @@TRANCOUNT ahora es 3.

    SELECT 'El nivel de anidamiento es', @@TRANCOUNT

    INSERT INTO Test VALUES (3)

    ROLLBACK TRAN Guadada -- se deshace lo hecho el punto guardado.

    SELECT 'El nivel de anidamiento es', @@TRANCOUNT

    --Ahora podemos decidir si la transaccin se lleva a cabo

    --o se deshace completamente

    --Para deshacerla un ROLLBACK bastar como hemos visto--Pero para guardar la transaccin hace falta reducir @@TRANCOUNT a 0

    COMMIT TRAN TranInterna1 -- Reduce @@TRANCOUNT a 2.

    SELECT 'El nivel de anidamiento es', @@TRANCOUNT

    COMMIT TRAN TranInterna1 -- Reduce @@TRANCOUNT a 1.

    -- Pero no se guarda nada en la base de datos.

    SELECT 'El nivel de anidamiento es', @@TRANCOUNT

    COMMIT TRAN TranExterna -- Reduce @@TRANCOUNT a 0.

    -- Se lleva a cabo la transaccin externa y todo lo que conlleva.

    SELECT 'El nivel de anidamiento es', @@TRANCOUNT

    SELECT * FROM Test

    Si no ponemos el nombre del punto salvado con SAVE TRAN al hacer un ROLLBACK TRAN se deshace la transaccin ms externa y

    @@TRANCOUNT se pone a 0.

    Como podemos ver el uso de transacciones no es complicado, e incluso las transacciones anidadas si se tratan con cuidado son fciles de manejar.

    Un par de ejemplos ms

    Veamos otro par de ejemplos para dejar claro como funcionan las sentencias BEGIN, COMMIT y SAVE

    BEGIN TRAN

    -- Primer BEGIN TRAN y ahora @@TRANCOUNT = 1

    BEGIN TRAN

    -- Ahora @@TRANCOUNT = 2

    http://www.programacion.net/bbdd/articulo/man_transacciones/ (4 de 7)17/09/2008 12:27:14

  • 7/22/2019 TRANSACCIONES SQL.pdf

    5/7

    Bases de datos en castellano. Transacciones en SQL Server bases de datos mysql oracle sqlite access mssql

    COMMIT TRAN

    -- Volvemos a @@TRANCOUNT = 1

    -- Pero no se guarda nada ni se hacen efectivos los posibles cambios

    COMMIT TRAN

    -- Por fin @@TRANCOUNT = 0

    -- Si hubiera cambios pendientes se llevan a la base de datos

    -- Y volvemos a un estado normal con la transaccin acabada

    Uso del COMMIT

    BEGIN TRAN

    -- Primer BEGIN TRAN y @@TRANCOUNT = 1

    BEGIN TRAN

    -- Ahora @@TRANCOUNT = 2

    COMMIT TRAN

    -- Como antes @@TRANCOUNT = 1

    --Y como antes nada se guarda

    ROLLBACK TRAN

    -- Se cancela TODA la transaccin. Recordemos que el COMMIT

    -- de antes no guardo nada, solo redujo @@TRANCOUNT

    -- Ahora @@TRANCOUNT = 0

    COMMIT TRAN

    -- No vale para nada porque @@TRANCOUNT es 0 por el efecto del ROLLBACK

    Uso del ROLLBACK

    En cuanto al SAVE TRAN podemos recordarlo con el siguiente ejemplo:

    CREATE TABLE Tabla1 (Columna1 varchar(50))

    GO

    BEGIN TRAN

    INSERT INTO Tabla1 VALUES ('Primer valor')

    SAVE TRAN Punto1

    INSERT INTO Tabla1 VALUES ('Segundo valor')

    ROLLBACK TRAN Punto1

    INSERT INTO Tabla1 VALUES ('Tercer valor')

    COMMIT TRAN

    SELECT * FROM Tabla1

    Columna1

    --------------------------------------------------

    Primer valor

    Tercer valor

    (2 filas afectadas)

    Un ROLLBACK a un SAVE TRAN no deshace la transaccin en curso ni modifica @@TRANCOUNT, simplemente cancela lo ocurrido desde el 'SAVE

    TRAN nombre' hasta su 'ROLLBACK TRAN nombre'

    Transacciones y procedimientos almacenados.

    Cuando trabajamos con procedimientos almacenados debemos recordar que cada procedimiento almacenado es una unidad. Cuando se ejecuta lo

    hace de manera independiente de quien lo llama. Sin embargo si tenemos un ROLLBACK TRAN dentro de un procedimiento almacenado cancelaremosa transaccin en curso, pero si hay una transaccin externa al procedimiento en el que estamos trabajando se cancelar esa transaccin externa.

    Con esto no quiero decir que no se pueda usar, simplemente que hay que tener muy claras las 4 normas sobre las sentencias BEGIN, ROLLBACK y

    COMMIT que comentamos al principio de este artculo. Veamos como se comporta una transaccin en un procedimiento almacenado llamado desde

    http://www.programacion.net/bbdd/articulo/man_transacciones/ (5 de 7)17/09/2008 12:27:14

  • 7/22/2019 TRANSACCIONES SQL.pdf

    6/7

    Bases de datos en castellano. Transacciones en SQL Server bases de datos mysql oracle sqlite access mssql

    una transaccin.

    CREATE PROCEDURE Inserta2

    AS

    BEGIN TRAN --Uno

    INSERT INTO Tabla1 VALUES ('Valor2')

    ROLLBACK TRAN --Uno

    GO

    CREATE PROCEDURE Inserta1

    AS

    BEGIN TRAN --Dos

    INSERT INTO Tabla1 VALUES ('Valor 1')

    EXEC Inserta2

    INSERT INTO Tabla1 VALUES ('Valor3')

    COMMIT TRAN --Dos

    GO

    En principio parece que si ejecutamos el procedimiento 'Inserta1' el resultado sera:

    EXECUTE inserta1

    SELECT * FROM tabla1

    txt

    --------------------------------------------------

    Valor1

    Valor3

    (2 filas afectadas)

    Pero lo que obtenemos es:

    EXECUTE inserta1

    SELECT * FROM tabla1

    (1 filas afectadas)

    (1 filas afectadas)

    Servidor: mensaje 266, nivel 16, estado 2, procedimiento Inserta2, lnea 8

    El recuento de transacciones despus de EXECUTE

    indica que falta una instruccin COMMIT o ROLLBACK TRANSACTION.

    Recuento anterior = 1, recuento actual = 0.

    (1 filas afectadas)

    Servidor: mensaje 3902, nivel 16, estado 1, procedimiento Inserta1, lnea 8

    La peticin COMMIT TRANSACTION no tiene la correspondiente BEGIN TRANSACTION.

    txt

    --------------------------------------------------

    Valor3

    (1 filas afectadas)

    Si analizamos estos mensajes vemos que se inserta la primera fila, se salta al segundo procedimiento almacenado y se inserta la segunda fila. Se

    ejecuta el 'ROLLBACK TRAN -Dos' del segundo procedimiento almacenado y se deshacen las dos inserciones porque este ROLLBACK cancela la

    ransaccin exterior, la del primer procedimiento almacenado. A continuacin se termina el segundo procedimiento almacenado y como este

    procedimiento tiene un 'BEGIN TRAN -Dos' y no tiene un COMMIT o un ROLLBACK (recordemos que el 'ROLLBACK TRAN -Dos' termina el 'BEGIN

    TRAN -Uno') se produce un error y nos avisa que el procedimiento almacenado termina con una transaccin pendiente. Al volver al procedimiento

    almacenado externo se ejecuta el INSERT que inserta la tercera fila y a continuacin el 'COMMIT TRAN --Uno'. Aqu aparece otro error puesto que esteCOMMIT TRAN -Uno' estaba ah para finalizar una transaccin que ya ha sido cancelada anteriormente.

    El modo correcto

    http://www.programacion.net/bbdd/articulo/man_transacciones/ (6 de 7)17/09/2008 12:27:14

  • 7/22/2019 TRANSACCIONES SQL.pdf

    7/7

    Bases de datos en castellano. Transacciones en SQL Server bases de datos mysql oracle sqlite access mssql

    Para que nuestras transacciones se comporten como se espera dentro de un procedimiento almacenado podemos recurrir al SAVE TRAN.

    Veamos como:

    CREATE PROCEDURE Inserta2

    AS

    SAVE TRAN Guardado

    INSERT INTO Tabla1 VALUES ('Valor2')

    ROLLBACK TRAN Guardado

    GO

    CREATE PROCEDURE Inserta1

    AS

    BEGIN TRAN

    INSERT INTO Tabla1 VALUES ('Valor 1')

    EXEC Inserta2

    INSERT INTO Tabla1 VALUES ('Valor 3')

    COMMIT TRAN

    GO

    Ahora el 'ROLLBACK TRAN guardado' del segundo procedimiento almacenado deshace la transaccin slo hasta el punto guardado. Adems este

    ROLLBACK no decrementa el @@TRANCOUNT ni afecta a las transacciones en curso.

    El resultado obtenido al ejecutar este procedimiento almacenado es:

    EXECUTE inserta1

    SELECT * FROM tabla1

    (1 filas afectadas)

    (1 filas afectadas)

    (1 filas afectadas)

    txt

    --------------------------------------------------

    Valor 1

    Valor 3

    (2 filas afectadas)

    Una vez vistos estos ejemplos queda claro que no hay problema en utilizar transacciones dentro de procedimientos almacenados siempre que

    engamos en cuenta el comportamiento del ROLLBACK TRAN y que utilicemos el SAVE TRAN de manera adecuada.

    Para mas informacin sobre transacciones:

    Ms informacin en MICROSOFT http://msdn.microsoft.com/library/default.asp?url=/library/en-us/acdata/ac_8_md_06_2it2.asp

    En SQLTEAM podemos encontrar http://www.sqlteam.com/item.asp?ItemID=15583

    En CODEPROJECT tenemos http://www.codeproject.com/database/sqlservertransactions.asp

    Y en DOTNETJUNKIES http://dotnetjunkies.com/WebLog/darrell.norton/archive/2003/12/24/4921.aspx