Medios de información y poder: el control de los flujos de ...
4_Variables, Flujos de Control y Transacciones
-
Upload
yandelsin-ivan -
Category
Documents
-
view
15 -
download
8
Transcript of 4_Variables, Flujos de Control y Transacciones
TECNOLÓGICO DE ESTUDIOS SUPERIORES DE ECATEPEC MAESTRÍA EN INGENIERÍA EN SISTEMAS COMPUTACIONALES
BASES DE DATOS
Variables en Transact-SQL Una variable es un valor identificado por un nombre (identificador) sobre el que podemos realizar modificaciones. En Transact SQL los identificadores de variables deben comenzar por el caracter @, es decir, el nombre de una variable debe comenzar por @. Para declarar variables en Transact SQL debemos utilizar la palabra clave declare, seguido del identificador y tipo de datos de la variable. -- Esto es un comentario de línea simple
/*
Este es un comentario con varias líneas.
Conjunto de Líneas.
*/
declare @nombre varchar(50)-- declare declara una variable
-- @nombre es el identificador de la
-- variable de tipo varchar
set @nombre = 'www.devjoker.com' -- El signo = es un operador
-- www.devjoker.com es un literal
print @Nombre -- Imprime por pantalla el valor de @nombre.
-- No diferencia mayúsculas ni minúsculas
En Transact SQL podemos asignar valores a una variable de varias formas:
A través de la instrucción set.
Utilizando una sentencia SELECT.
Realizando un FETCH de un cursor. El siguiente ejemplo muestra como asignar una variable utilizando la instrucción SET.
DECLARE @nombre VARCHAR(100)
-- La consulta debe devolver un único registro
SET @nombre = (SELECT nombre
FROM CLIENTES
WHERE ID = 1)
PRINT @nombre
Otro ejemplo sería:
DECLARE @nombre VARCHAR(100)
@nombre = ‘Roberto Méndez’
TECNOLÓGICO DE ESTUDIOS SUPERIORES DE ECATEPEC MAESTRÍA EN INGENIERÍA EN SISTEMAS COMPUTACIONALES
BASES DE DATOS El siguiente ejemplo muestra como asignar variables utilizando una sentencia SELECT.
DECLARE @nombre VARCHAR(100), @apellido1 VARCHAR(100), @apellido2 VARCHAR(100)
SELECT @nombre=nombre ,
@apellido1=Apellido1,
@apellido2=Apellido2
FROM CLIENTES
WHERE ID = 1
PRINT @nombre
PRINT @apellido1
PRINT @apellido2
Un punto a tener en cuenta cuando asignamos variables de este modo, es que si la consulta SELECT devuelve más de un registro, las variables quedarán asignadas con los valores de la última fila devuelta.
Por último veamos como asignar variables a través de un cursor. DECLARE @nombre VARCHAR(100),@apellido1 VARCHAR(100),@apellido2 VARCHAR(100)
DECLARE CDATOS CURSOR FOR SELECT nombre, Apellido1, Apellido2
FROM CLIENTES
OPEN CDATOS
FETCH CDATOS INTO @nombre, @apellido1, @apellido2
WHILE (@@FETCH_STATUS = 0)
BEGIN
PRINT @nombre
PRINT @apellido1
PRINT @apellido2
FETCH CDATOS INTO @nombre, @apellido1, @apellido2
END
CLOSE CDATOS
DEALLOCATE CDATOS
TECNOLÓGICO DE ESTUDIOS SUPERIORES DE ECATEPEC MAESTRÍA EN INGENIERÍA EN SISTEMAS COMPUTACIONALES
BASES DE DATOS
Operadores en Transact SQL
Tipo de operador Operadores
Operador de asignación =
Operadores aritméticos + (suma) - (resta)
* (multiplicación)
/ (división) ** (exponente)
% (modulo)
Operadores relacionales
o de comparación
= (igual a)
<> (distinto de) != (distinto de)
< (menor que) > (mayor que)
>= (mayor o igual a)
<= (menor o igual a) !> (no mayor a)
!< (no menor a)
Operadores lógicos AND (y lógico)
NOT (negacion) OR (o lógico)
& (AND a nivel de bit)
| (OR a nivel de bit) ^ (OR exclusivo a nivel de bit)
Operador de concatenación
+
Otros
ALL (Devuelve TRUE si el conjunto completo de comparaciones es TRUE) ANY(Devuelve TRUE si cualquier elemento del conjunto de comparaciones es
TRUE) BETWEEN (Devuelve TRUE si el operando está dentro del intervalo)
EXISTS (TRUE si una subconsulta contiene filas) IN (TRUE si el operando está en la lista)
LIKE (TRUE si el operando coincide con un patron)
NOT (Invierte el valor de cualquier operador booleano) SOME(Devuelve TRUE si alguna de las comparaciones de un conjunto es TRUE)
TECNOLÓGICO DE ESTUDIOS SUPERIORES DE ECATEPEC MAESTRÍA EN INGENIERÍA EN SISTEMAS COMPUTACIONALES
BASES DE DATOS
Estructuras de Control en Transact SQL Las estructuras de control dentro del lenguaje procedural de Transact SQL son:
Estructura IF
Estructura CASE
Bucle o ciclos WHILE
Estructura GOTO Estructura condicional IF La estructura condicional IF permite evaluar una expresión booleana (resultado VERDADERO - FALSO), y ejecutar las operaciones contenidas en el bloque formado por BEGIN / END. IF (<expresion>) BEGIN ... END ELSE IF (<expresion>) BEGIN ... END ELSE BEGIN ... END
Ejemplo de la estructura condicional IF. DECLARE @Web varchar(100), @diminutivo varchar(3) SET @diminutivo = 'DJK' IF @diminutivo = 'DJK' BEGIN PRINT 'www.devjoker.com' END ELSE BEGIN PRINT 'Otra Web (peor!)' END
TECNOLÓGICO DE ESTUDIOS SUPERIORES DE ECATEPEC MAESTRÍA EN INGENIERÍA EN SISTEMAS COMPUTACIONALES
BASES DE DATOS La estructura IF admite el uso de subconsultas: DECLARE @coPais int, @descripcion varchar(255)
set @coPais = 5
set @descripcion = 'España'
IF EXISTS(SELECT * FROM PAISES
WHERE CO_PAIS = @coPais)
BEGIN
UPDATE PAISES
SET DESCRIPCION = @descripcion
WHERE CO_PAIS = @coPais
END
ELSE
BEGIN
INSERT INTO PAISES
(CO_PAIS, DESCRIPCION) VALUES
(@coPais, @descripcion)
END
Estructura condicional CASE La estructura condicional CASE permite evaluar una expresión y devolver un valor u otro. La sintaxis general de case es:
CASE <expresion>
WHEN <valor_expresion> THEN <valor_devuelto>
WHEN <valor_expresion> THEN <valor_devuelto>
ELSE <valor_devuelto> -- Valor por defecto
END
Ejemplo: DECLARE @Web varchar(100), @diminutivo varchar(3)
SET @diminutivo = 'DJK'
SET @Web = (CASE @diminutivo
WHEN 'DJK' THEN 'www.devjoker.com'
WHEN 'ALM' THEN 'www.aleamedia.com'
ELSE 'www.devjoker.com'
END)
PRINT @Web
Otra sintaxis de CASE nos permite evaluar diferentes expresiones: CASE
WHEN <expresion> = <valor_expresion> THEN <valor_devuelto>
WHEN <expresion> = <valor_expresion> THEN <valor_devuelto>
ELSE <valor_devuelto> -- Valor por defecto
TECNOLÓGICO DE ESTUDIOS SUPERIORES DE ECATEPEC MAESTRÍA EN INGENIERÍA EN SISTEMAS COMPUTACIONALES
BASES DE DATOS
END
El mismo ejemplo aplicando esta sintaxis: DECLARE @Web varchar(100), @diminutivo varchar(3)
SET @diminutivo = 'DJK'
SET @Web = (CASE
WHEN @diminutivo = 'DJK' THEN 'www.devjoker.com'
WHEN @diminutivo = 'ALM' THEN 'www.aleamedia.com'
ELSE 'www.devjoker.com'
END)
PRINT @Web
En CASE también se permite el uso de subconsultas DECLARE @Web varchar(100), diminutivo varchar(3)
SET @diminutivo = 'DJK'
SET @Web = (CASE
WHEN @diminutivo = 'DJK' THEN (SELECT web
FROM WEBS
WHERE id=1)
WHEN @diminutivo = 'ALM' THEN (SELECT web
FROM WEBS
WHERE id=2)
ELSE 'www.devjoker.com'
END)
PRINT @Web
Bucle o Ciclo WHILE El bucle WHILE se repite mientras expresión se evalúe como verdadero. WHILE Es el único tipo de bucle del que dispone Transact SQL. WHILE <expresion>
BEGIN
...
END
Ejemplo:
DECLARE @contador int
SET @contador = 0
WHILE (@contador < 100)
BEGIN
SET @contador = @contador + 1
TECNOLÓGICO DE ESTUDIOS SUPERIORES DE ECATEPEC MAESTRÍA EN INGENIERÍA EN SISTEMAS COMPUTACIONALES
BASES DE DATOS PRINT 'Iteracion del bucle ' + cast(@contador AS varchar)
END
Podemos saltar a la siguiente iteración del bucle utilizando CONTINUE.
DECLARE @contador int
SET @contador = 0
WHILE (@contador < 100)
BEGIN
SET @contador = @contador + 1
IF (@contador % 2 = 0)
CONTINUE
PRINT 'Iteracion del bucle ' + cast(@contador AS varchar)
END
En este caso solo imprimirá la línea de texto cuando el valor de @contador sea impar Con Break el ciclo se interrumpirá: DECLARE @contador int
SET @contador = 0
WHILE (1 = 1)
BEGIN
SET @contador = @contador + 1
IF (@contador % 50 = 0)
BREAK
PRINT 'Iteracion del bucle ' + cast(@contador AS varchar)
END
Cuando el valor de @contador llegue a 50, el ciclo se interrumpirá y terminará También podemos utilizar el bucle WHILE conjuntamente con subconsultas. DECLARE @coRecibo int
WHILE EXISTS (SELECT *
FROM RECIBOS
WHERE PENDIENTE = 'S')
-- Ojo, la subconsulta se ejecuta
-- una vez por cada iteracion
-- del bucle!
BEGIN
SET @coRecibo = (SELECT TOP 1 CO_RECIBO
FROM RECIBOS WHERE PENDIENTE = 'S')
UPDATE RECIBOS
SET PENDIENTE = 'N'
WHERE CO_RECIBO = @coRecibo
END
TECNOLÓGICO DE ESTUDIOS SUPERIORES DE ECATEPEC MAESTRÍA EN INGENIERÍA EN SISTEMAS COMPUTACIONALES
BASES DE DATOS Estructura GOTO La sentencia goto nos permite desviar el flujo de ejecución hacia una etiqueta. Fué muy utilizada en versiones anteriores de SQL Server conjuntamente con la variable de sistema @@ERROR para el control de errores. Actualmente, se desaconseja el uso GOTO, recomendándose el uso de TRY - CATCH para la gestión de errores. DECLARE @divisor int, @dividendo int, @resultado int
SET @dividendo = 100
SET @divisor = 0
SET @resultado = @dividendo/@divisor
IF @@ERROR > 0
GOTO error
PRINT 'No hay error'
RETURN
error:
PRINT 'Se ha producido una division por cero'
TECNOLÓGICO DE ESTUDIOS SUPERIORES DE ECATEPEC MAESTRÍA EN INGENIERÍA EN SISTEMAS COMPUTACIONALES
BASES DE DATOS
Transacciones Una transacción es un conjunto de operaciones Transact SQL que se ejecutan como un único bloque, es decir, si falla una operación Transact SQL fallan todas. Si una transacción tiene éxito, todas las modificaciones de los datos realizadas durante la transacción se confirman y se convierten en una parte permanente de la base de datos. Si una transacción encuentra errores y debe cancelarse o revertirse, se borran todas las modificaciones de los datos.
El ejemplo clásico de transacción es una transferencia bancaria, en la que quitamos saldo a una cuenta y lo añadimos en otra. Si no somos capaces de abonar el dinero en la cuenta de destino, no debemos quitarlo de la cuenta de origen. SQL Server funciona por defecto con Transacciones de confirmación automática, es decir, cada instrucción individual es una transacción y se confirma automáticamente. Sobre el ejemplo anterior de la transferencia bancaria, un script debería realizar algo parecido a lo siguiente: DECLARE @importe DECIMAL(18,2),
@CuentaOrigen VARCHAR(12),
@CuentaDestino VARCHAR(12)
/* Asignamos el importe de la transferencia
* y las cuentas de origen y destino
*/
SET @importe = 50
SET @CuentaOrigen = '200700000001'
SET @CuentaDestino = '200700000002'
/* Descontamos el importe de la cuenta origen */
UPDATE CUENTAS
SET SALDO = SALDO - @importe
WHERE NUMCUENTA = @CuentaOrigen
/* Registramos el movimiento */
INSERT INTO MOVIMIENTOS
(IDCUENTA, SALDO_ANTERIOR, SALDO_POSTERIOR, IMPORTE, FXMOVIMIENTO)
SELECT
IDCUENTA, SALDO + @importe, SALDO, @importe, getdate()
FROM CUENTAS
WHERE NUMCUENTA = @CuentaOrigen
/* Incrementamos el importe de la cuenta destino */
UPDATE CUENTAS
SET SALDO = SALDO + @importe
WHERE NUMCUENTA = @CuentaDestino
/* Registramos el movimiento */
INSERT INTO MOVIMIENTOS
(IDCUENTA, SALDO_ANTERIOR, SALDO_POSTERIOR, IMPORTE, FXMOVIMIENTO)
SELECT
IDCUENTA, SALDO - @importe, SALDO, @importe, getdate()
FROM CUENTAS
TECNOLÓGICO DE ESTUDIOS SUPERIORES DE ECATEPEC MAESTRÍA EN INGENIERÍA EN SISTEMAS COMPUTACIONALES
BASES DE DATOS WHERE NUMCUENTA = @CuentaDestino
Esta forma de actuar seria errónea, ya que cada instrucción se ejecutaría y confirmaría de forma independiente, por lo que un error dejaría los datos erróneos en la base de datos ( ¡y ese es el peor error que nos podemos encontrar! ) Transacciones implícitas y explicitas Para agrupar varias sentencias Transact SQL en una única transacción, disponemos de los siguientes métodos:
Transacciones explícitas Cada transacción se inicia explícitamente con la instrucción BEGIN TRANSACTION y se termina explícitamente con una instrucción COMMIT o ROLLBACK.
Transacciones implícitas Se inicia automáticamente una nueva transacción cuando se ejecuta una instrucción que realiza modificaciones en los datos, pero cada transacción se completa explícitamente con una instrucción COMMIT o ROLLBACK.
Para activar-desactivar el modo de transacciones implícitas debemos ejecutar la siguiente instrucción.
--Activamos el modo de transacciones implícitas
SET IMPLICIT_TRANSACTIONS ON
--Desactivamos el modo de transacciones implícitas
SET IMPLICIT_TRANSACTIONS OFF
Cuando la opción ANSI_DEFAULTS está establecida en ON, IMPLICIT_TRANSACTIONS también se establece en ON.
TECNOLÓGICO DE ESTUDIOS SUPERIORES DE ECATEPEC MAESTRÍA EN INGENIERÍA EN SISTEMAS COMPUTACIONALES
BASES DE DATOS
El siguiente ejemplo muestra el script anterior haciendo uso de transacciones explicitas. DECLARE @importe DECIMAL(18,2),
@CuentaOrigen VARCHAR(12),
@CuentaDestino VARCHAR(12)
/* Asignamos el importe de la transferencia
* y las cuentas de origen y destino
*/
SET @importe = 50
SET @CuentaOrigen = '200700000002'
SET @CuentaDestino = '200700000001'
BEGIN TRANSACTION -- O solo BEGIN TRAN
BEGIN TRY
/* Descontamos el importe de la cuenta origen */
UPDATE CUENTAS
SET SALDO = SALDO - @importe
WHERE NUMCUENTA = @CuentaOrigen
/* Registramos el movimiento */
INSERT INTO MOVIMIENTOS
(IDCUENTA, SALDO_ANTERIOR, SALDO_POSTERIOR,
IMPORTE, FXMOVIMIENTO)
SELECT
IDCUENTA, SALDO + @importe, SALDO, @importe, getdate()
FROM CUENTAS
WHERE NUMCUENTA = @CuentaOrigen
/* Incrementamos el importe de la cuenta destino */
UPDATE CUENTAS
SET SALDO = SALDO + @importe
WHERE NUMCUENTA = @CuentaDestino
/* Registramos el movimiento */
INSERT INTO MOVIMIENTOS
(IDCUENTA, SALDO_ANTERIOR, SALDO_POSTERIOR,
IMPORTE, FXMOVIMIENTO)
SELECT
IDCUENTA, SALDO - @importe, SALDO, @importe, getdate()
FROM CUENTAS
WHERE NUMCUENTA = @CuentaDestino
/* Confirmamos la transaccion*/
COMMIT TRANSACTION -- O solo COMMIT
END TRY
BEGIN CATCH
/* Hay un error, deshacemos los cambios*/
ROLLBACK TRANSACTION -- O solo ROLLBACK
PRINT 'Se ha producido un error!'
END CATCH
TECNOLÓGICO DE ESTUDIOS SUPERIORES DE ECATEPEC MAESTRÍA EN INGENIERÍA EN SISTEMAS COMPUTACIONALES
BASES DE DATOS El siguiente ejemplo muestra el mismo script con transacciones implicitas. SET IMPLICIT_TRANSACTIONS ON
DECLARE @importe DECIMAL(18,2),
@CuentaOrigen VARCHAR(12),
@CuentaDestino VARCHAR(12)
/* Asignamos el importe de la transferencia
* y las cuentas de origen y destino
*/
SET @importe = 50
SET @CuentaOrigen = '200700000002'
SET @CuentaDestino = '200700000001'
BEGIN TRY
/* Descontamos el importe de la cuenta origen */
UPDATE CUENTAS
SET SALDO = SALDO - @importe
WHERE NUMCUENTA = @CuentaOrigen
/* Registramos el movimiento */
INSERT INTO MOVIMIENTOS
(IDCUENTA, SALDO_ANTERIOR, SALDO_POSTERIOR,
IMPORTE, FXMOVIMIENTO)
SELECT
IDCUENTA, SALDO + @importe, SALDO, @importe, getdate()
FROM CUENTAS
WHERE NUMCUENTA = @CuentaOrigen
/* Incrementamos el importe de la cuenta destino */
UPDATE CUENTAS
SET SALDO = SALDO + @importe
WHERE NUMCUENTA = @CuentaDestino
/* Registramos el movimiento */
INSERT INTO MOVIMIENTOS
(IDCUENTA, SALDO_ANTERIOR, SALDO_POSTERIOR,
IMPORTE, FXMOVIMIENTO)
SELECT
IDCUENTA, SALDO - @importe, SALDO, @importe, getdate()
FROM CUENTAS
WHERE NUMCUENTA = @CuentaDestino
/* Confirmamos la transaccion*/
COMMIT TRANSACTION -- O solo COMMIT
END TRY
BEGIN CATCH
/* Hay un error, deshacemos los cambios*/
ROLLBACK TRANSACTION -- O solo ROLLBACK
PRINT 'Se ha producido un error!'
END CATCH
La transacción sigue activa hasta que emita una instrucción COMMIT o ROLLBACK.
TECNOLÓGICO DE ESTUDIOS SUPERIORES DE ECATEPEC MAESTRÍA EN INGENIERÍA EN SISTEMAS COMPUTACIONALES
BASES DE DATOS Una vez que la primera transacción se ha confirmado o revertido, se inicia automáticamente una nueva transacción la siguiente vez que la conexión ejecuta una instrucción para modificar datos. La conexión continúa generando transacciones implícitas hasta que se desactiva el modo de transacciones implícitas. Podemos verificar el número de transacciones activas a través de @@TRANCOUNT. SET IMPLICIT_TRANSACTIONS ON
BEGIN TRY
UPDATE CUENTAS SET FXALTA = FXALTA - 1
PRINT @@TRANCOUNT
COMMIT
END TRY
BEGIN CATCH
ROLLBACK
PRINT 'Error'
END CATCH
Otro punto a tener en cuenta cuando trabajamos con transacciones son los bloqueos y el nivel de aislamiento. Un bloqueo diremos que un bloqueo se produce cuando un usuario modifica o lee datos en una transacción. Por ejemplo, imaginemos que estamos actualizando una "factura" de estado "pendiente" a estado "pagado", durante el tiempo que dura la transacción (antes de hacer COMMIT o ROLLBACK), si otro usuario quiere ver el estado de la "factura" ... ¿qué estado mostramos? ¡Los datos están cambiando! Si devolvemos "pendiente" no sería correcto, ya que el estado ha cambiado a "pagado", pero si devolvemos "pagado" tampoco es correcto, ya que la transacción podría hacer ROLLBACK y abríamos efectuado una lectura incorrecta. SQL Server bloquea la fila (en el mejor de los casos) e impide el acceso a los datos afectados por la transacción, como consecuencia el resto de usuarios concurrentes se quedan "bloqueados", sin posibilidad siquiera de leer los datos. Para controlar como afectan los bloqueos a las transacciones, podemos modificar el nivel de aislamiento de las transacciones a través de la instrucción SET TRANSACTION ISOLATION LEVEL. Con esta instrucción controlamos como interpreta la transacción los bloqueos existentes y como genera nuevos bloqueos con sus operaciones de lectura/escritura. La sintaxis es la siguiente: SET TRANSACTION ISOLATION LEVEL <opcion>
TECNOLÓGICO DE ESTUDIOS SUPERIORES DE ECATEPEC MAESTRÍA EN INGENIERÍA EN SISTEMAS COMPUTACIONALES
BASES DE DATOS Donde <opcion> puede tomar estos valores:
READ COMMITTED - La transacción no puede leer datos modificados por otras transacciones. Permite a otras transacciones puedan modificar los datos que se han leído. Esta opción es la predeterminada para SQL Server (incluido 2005).
READ UNCOMMITTED - La transacción es capaz de leer los datos modificados por otras transacciones pero que aún no han sido confirmadas (pendientes de COMMIT).
REPEATABLE READ - La transacción no puede leer datos modificados por otras transacciones y otras transacciones no pueden modificar los datos que se han leído.
SERIALIZABLE - Las instrucciones no pueden leer datos que hayan sido modificados, pero aún no confirmados, por otras transacciones y ninguna otra transacción puede modificar los datos leídos por la transacción actual ni insertar filas nuevas con valores de clave que pudieran estar incluidos en el intervalo de claves hasta que la transacción actual finalice.
SNAPSHOT - Activa el versionado de fila. Las instrucciones que se ejecuten en la transacción no verán las modificaciones de datos efectuadas por otras transacciones, en su lugar reciben una "copia coherente" de cómo estaban los datos al comienzo de la transacción. De este modo actúan otros gestores de bases de datos muy populares, como por ejemplo, ORACLE.
Para activar la opción ALLOW_SNAPSHOT_ISOLATION con la siguiente instrucción: ALTER DATABASE MyDatabase
SET ALLOW_SNAPSHOT_ISOLATION ON
Transacciones anidadas. Cuando anidamos varias transacciones la instrucción COMMIT afectará a la última transacción abierta, pero ROLLBACK afectará a todas las transacciones abiertas. Un hecho a tener en cuenta, es que, si hacemos ROLLBACK de la transacción superior se desharán también los cambios de todas las transacciones internas, aunque hayamos realizado COMMIT de ellas. BEGIN TRAN
UPDATE EMPLEADOS
SET NOMBRE = 'Devjoker'
WHERE ID=101
BEGIN TRAN
UPDATE EMPLEADOS
SET APELLIDO1 = 'Devjoker.COM'
WHERE ID=101
-- Este COMMIT solo afecta a la segunda transaccion.
COMMIT
-- Este ROLLBACK afecta a las dos transacciones.
ROLLBACK
TECNOLÓGICO DE ESTUDIOS SUPERIORES DE ECATEPEC MAESTRÍA EN INGENIERÍA EN SISTEMAS COMPUTACIONALES
BASES DE DATOS Una consideración a tener en cuenta cuando trabajamos con transacciones anidadas es la posibilidad de utilizar puntos de guardado o SAVEPOINTs. Puntos de recuperación (SavePoint). Los puntos de recuperación (SavePoints) permiten manejar las transacciones por pasos, pudiendo hacer rollbacks hasta un punto marcado por el savepoint y no por toda la transacción. El siguiente ejemplo muestra como trabajar con puntos de recuperación. BEGIN TRAN
UPDATE EMPLEADOS
SET NOMBRE = 'Devjoker'
WHERE ID=101
UPDATE EMPLEADOS
SET APELLIDO1 = 'Devjoker.COM'
WHERE ID=101
SAVE TRANSACTION P1 -- Guardamos la transaccion (Savepoint)
UPDATE EMPLEADOS
SET APELLIDO1 = 'Otra cosa!'
WHERE ID=101
-- Este ROLLBACK afecta solo a las instrucciones
-- posteriores al savepoint P1.
ROLLBACK TRANSACTION P1
-- Confirmamos la transaccion
COMMIT