Unidad 3
-
Upload
lacota-santiago -
Category
Documents
-
view
40 -
download
0
description
Transcript of Unidad 3
• De acuerdo a lo visto, cómo es la organización o estructura de un programa en el servidor de BD?
• …
• Organización de programa en bloques.
• CREATE…
• AS• DECLARE…• …• Sentencias SQL• …
cabecera
cuerpo
se incluyen todos los elementos de definición del programa; como nombre, parámetros (entrada y/o salida),tipo de trigger, etc.
se definen todos los elementos de desarrollo comodeclaración de variables y bloque de sentencias
BLOQUE
• CREATE…
• AS• DECLARE…• …• Sentencias SQL• …
cabecera
cuerpo
declaración
ejecución
BLOQUE
excepciones
• Manejo de excepciones: control de errores.• PL/SQL usa bloque de exceptions.
DECLARE....excep EXCEPTION; /* (2) */BEGIN...IF ... THENRAISE excep; /* (2) */END IF;...EXCEPTIONWHEN TOO_MANY_ROWS THEN /* (1) */sentencias_manejo_excepcion_sistema;WHEN excep THEN /* (2) */sentencias_manejo_excepcion_usuario;WHEN OTHERS THEN ...;;END;
• SQL Server 2005 posee un mecanismo de manejo de excepciones en un formato de bloque TRY/CATCH:
BEGIN TRY
INSERT INTO Sales.Currency (CurrencyCode, Name, ModifiedDate )
VALUES('LAE','Pol','01/06/2005')
PRINT 'Inserción completada con exito.'
END TRY
BEGIN CATCH
PRINT 'Inserción fallida.'
END CATCH
BEGIN TRYDECLARE @divisor int , @dividendo int, @resultado int
SET @dividendo = 100SET @divisor = 0
-- Esta linea provoca un error de division por 0SET @resultado = @dividendo/@divisorPRINT 'No hay error‘END TRY
BEGIN CATCHPRINT 'Se ha producido un error‘END CATCH
• SQL Server 2000 no maneja el TRY/CATCH.
• Qué se ha usado para controlar errores?
• IF ELSE…PRINT…RETURN
• @@ERROR. Variable global de sistema. Almacena el número de error producido por la última sentencia Transact SQL ejecutada.
• Devuelve 0 si la última instrucción Transact-SQL se ejecutó con éxito.
• Si la instrucción causó un error, devuelve el número de error. • El valor de @@ERROR cambia al finalizar cada instrucción
Transact-SQL.
• Qué se aconseja?• Guardar @@ERROR en una variable de tipo entero
inmediatamente después de que se complete la instrucción Transact-SQL. El valor de la variable se puede usar posteriormente.
DECLARE @divisor int , @dividendo int , @resultado int
SET @dividendo = 100
SET @divisor = 0
/*se genera una division por 0->error*/
SET @resultado = @dividendo/@divisor
IF @@ERROR = 0
PRINT 'No hay error'
ELSE
PRINT 'Error de division'
DECLARE @divisor int , @dividendo int , @resultado int
SET @dividendo = 100
SET @divisor = 0
/*se genera una division por 0->error*/
SET @resultado = @dividendo/@divisor
/*esta linea vuelve a establecer @@error a 0*/
PRINT 'Controlando el error ...'
IF @@ERROR = 0
PRINT 'No hay error'
ELSE
PRINT 'Error de division'
DECLARE @divisor int,@dividendo int , @resultado int
declare @nu int
SET @dividendo = 100
SET @divisor = 0
SET @resultado = @dividendo/@divisor
set @nu=@@error
print 'Controlando el error...'
IF @nu = 0
PRINT 'No hay error'
ELSE
PRINT 'Hay error: ' +cast(@nu as varchar)
• En ocasiones es necesario provocar voluntariamente un error; nos puede interesar que se genere un error cuando los datos incumplen una regla de negocio.
• Se puede provocar un error en tiempo de ejecución a través de la función RAISERROR.
• Raiserror. Devuelve un mensaje de error definido por el usuario y establece un indicador del sistema para registrar que se ha producido un error; más eficaz que PRINT para devolver mensajes a las aplicaciones.
• Sintaxis• RAISERROR ( { msg_id | msg_str } { , severity ,
state } [ , argument [ ,...n ] ] ) [ WITH option [ ,...n ] ]
• msg_id. Número de mensaje de error definido por el usuario que está almacenado en la tabla sysmessages. Deben ser mayores de 50.000.
• msg_str. Mensaje ad hoc con un formato similar al estilo de formato PRINT... Puede contener un máximo de 400 caracteres. Si el mensaje contiene más de 400 caracteres, solamente aparecerán los 397 primeros y se agregarán puntos suspensivos…
• Severity. Nivel de gravedad definido por el usuario que se asocia con este mensaje. Todos los usuarios pueden utilizar los niveles de gravedad de 0 a 18.
• State. Entero arbitrario entre 1 y 127 que representa información acerca del estado de llamada del error. Un valor negativo de state pasa a tener un valor predeterminado de 1.
• VER RESTO DE CONFIGURACION
Create procedure chequearpersona1 (@dni varchar(9))asif exists (select * from persona where dni=@dni)beginselect nombre, apellido from persona where dni=@dniendelseraiserror ('Valor pedido no existe', 16, 1)
exec chequearpersona1 '343434343'
• Es posible definir errores de usuario con el fin de poder reutilizarlos, y así ofrecer un comportamiento homogéneo.
• Esto puede realizarse a través del procedimiento almacenado del sistema sp_addmessage (especificando código de error, severidad, texto del error, e idioma).
• Pueden consultarse los errores existentes en sysmessages.
• EXEC sp_addmessage @msgnum = 50001, @severity = 16, @msgtext = 'No existe elemento buscado', @lang = 'us_english‘
• SELECT * FROM master.dbo.sysmessages
Create procedure chequearpersona1 (@dni varchar(9))asif exists (select * from persona where dni=@dni)beginselect nombre, apellido from persona where dni=@dniendelseraiserror (50001, 16, 1)
• Apliquemos @@error y raiserror a algun procedimiento realizado anteriormente.
• Que tipo de problema podria ser?
• Select?
• Qué error podría surgir con un select?
• Uso de @@rowcount
Create procedure chequearpersona (@dni varchar(9))
asif exists (select * from persona where dni=@dni)beginselect nombre, apellido from persona where
dni=@dniendelseprint 'Persona no esta registrada'
Create procedure chequearpersona6 (@dni varchar(9))
as
select nombre, apellido from persona where dni=@dni
if @@rowcount=0
print 'Persona no esta registrada'
alter procedure chequearpersona6 (@dni varchar(9))
as
select nombre, apellido from persona where dni=@dni
if @@rowcount=0
raiserror ('Persona no esta registrada', 16,1)
Create procedure insertarpersona5 (@DNI varchar(9), @nombre varchar(25), @apellido varchar(50), @ciudad varchar(25), @direccioncalle varchar(50), @direccionnum varchar(3), @telefono varchar(9), @fechanacimiento datetime, @varon char(1))
asinsert into persona (DNI, Nombre, Apellido, Ciudad,
DireccionCalle, DireccionNum, Telefono, FechaNacimiento, Varon)
values (@DNI, @Nombre, @APellido, @Ciudad, @DireccionCalle, @DireccionNum, @Telefono, @FechaNacimiento, @Varon)
if @@error<>0print 'Hubo un error de insercion'
alter procedure insertarpersona5 (@DNI varchar(9), @nombre varchar(25), @apellido varchar(50), @ciudad varchar(25), @direccioncalle varchar(50), @direccionnum varchar(3), @telefono varchar(9), @fechanacimiento datetime, @varon char(1))
asdeclare @nu intinsert into persona (DNI, Nombre, Apellido, Ciudad, DireccionCalle, DireccionNum,
Telefono, FechaNacimiento, Varon)values (@DNI, @Nombre, @APellido, @Ciudad, @DireccionCalle, @DireccionNum,
@Telefono, @FechaNacimiento, @Varon)set @nu=@@errorif @nu=2627 print 'No se aceptan claves duplicadas. No se pudo insertar persona.'elseif @nu=547print 'Conflicto con restricciones tipo check en insercion. No se pudo insertar persona.'if @nu=0print 'Insercion realizada correctamente'
alter procedure insertarpersona5 (@DNI varchar(9), @nombre varchar(25), @apellido varchar(50), @ciudad varchar(25), @direccioncalle varchar(50), @direccionnum varchar(3), @telefono varchar(9), @fechanacimiento datetime, @varon char(1))
asdeclare @nu intinsert into persona (DNI, Nombre, Apellido, Ciudad, DireccionCalle,
DireccionNum, Telefono, FechaNacimiento, Varon)values (@DNI, @Nombre, @APellido, @Ciudad, @DireccionCalle,
@DireccionNum, @Telefono, @FechaNacimiento, @Varon)set @nu=@@errorif @nu=2627 raiserror ('No se aceptan claves duplicadas',16,1)elseif @nu=547raiserror ('Conflicto con restricciones tipo check en insercion',16,1)if @nu=0print 'Insercion realizada correctamente'
• Hacer lo mismo con el siguiente PA (actualiza id de una titulacion)
create PROCEDURE updatetitulacion1 (@idtitantiguo numeric(6), @idtitnuevo numeric(6)) AS if (select count(*) from titulacion where idtitulacion=@idtitantiguo)=1 begin if (select count(*) from titulacion where idtitulacion=@idtitnuevo)=0 begin update titulacion set idtitulacion=@idtitnuevo where idtitulacion=@idtitantiguo return 0 end else return 2 end else return 1
create PROCEDURE updatetitulacion5 (@idtitantiguo numeric(6), @idtitnuevo numeric(6))
AS update titulacion set idtitulacion=@idtitnuevo where idtitulacion=@idtitantiguoif @@error<>0print 'Hubo un error...'elseif @@error=0print 'todo bien'
alter PROCEDURE updatetitulacion5 (@idtitantiguo numeric(6), @idtitnuevo numeric(6))
AS update titulacion set idtitulacion=@idtitnuevo where idtitulacion=@idtitantiguoif @@error<>0print 'Hubo un error...'elseif @@error=0print 'todo bien'if @@rowcount=0print 'no se encontro titulacion'
• INTO (en SELECT). La cláusula INTO habilita para especificar que el conjunto de resultados se utilizará para crear una tabla nueva con el nombre definido en la cláusula: Crea una nueva tabla e inserta en ella las filas resultantes…
• Se suele utilizar para crear tablas de trabajo, o tablas intermedias; se crean para una determinada tarea y luego se borran.
• Se puede utilizar para crear una copia de seguridad de la tabla.
• Sintaxis. • INTO new_table
SELECT * INTO new_table_name FROM old_tablename
SELECT column_name(s) INTO new_table_name FROM old_tablename
En BD Universidad:select * into copia from persona
select Nombre, Apellidointo copia1 from persona
select Nombre, Apellidointo copia1 from personaWhere ciudad=‘Concepcion’
select Asignatura.Nombre as asig, persona.nombre, persona.apellidointo copia2from asignatura, profesor, personaWhere asignatura.idprofesor=profesor.idprofesorand profesor.dni=persona.dni
• No confundir con…• INSERT INTO…SELECT:
• Inserción multiple de filas.• La sentencia INSERT permite tambien insertar
varios registros en una tabla. • Pare ello se utiliza una combinación de la
sentencia INSERT junto a una sentencia SELECT.
• El resultado es que se insertan todos los registros devueltos por la consulta.
• Sintaxis.• INSERT INTO <nombre_tabla>
[(<campo1>[,<campo2>,...])]SELECT [(<campo1>[,<campo2>,...])]FROM<nombre_tabla_origen>;
• Se deben cumplir las siguientes normas:• La lista de campos de las sentencias insert y select
deben coincidir en número y tipo de datos.• Ninguna de las filas devueltas por la consulta debe
infringir las reglas de integridad de la tabla en la que vayamos a realizar la inserción.
create table ciudades
(id int identity (1,1) primary key,
ciudad varchar (30))
insert into ciudades (ciudad)
select distinct ciudad
from persona
• Hacer un PA para la tabla persona, con parametro de entrada (ciudad) que realice un select de todas las personas de esas ciudad, y genere una copia de seguridad con esos datos.
create procedure chequearpersonaycopiar (@ciudad varchar(25))
asif exists (select * from persona where ciudad=@ciudad)Beginselect * from persona where ciudad=@ciudadselect * into copiapersonaciudad from persona where
ciudad=@ciudadendelseprint 'Persona no esta registrada'
alter procedure chequearpersonaycopiar (@ciudad varchar(25))
asselect * from persona where ciudad=@ciudadselect * into copiapersonaciudad from persona
where ciudad=@ciudadif @@rowcount=0raiserror ('No hay registros de personas en esa
ciudad, no se pudo generar copia',16,1)elseprint ('Copia generada')
alter procedure chequearpersonaycopiar (@ciudad varchar(25))asIF EXISTS (SELECT name FROM sysobjects WHERE name = 'copiapersonaciudad' AND xtype = 'U')drop table copiapersonaciudadelseBeginselect * from persona where ciudad=@ciudadselect * into copiapersonaciudad from persona where ciudad=@ciudadif @@rowcount=0raiserror ('No hay registros de personas en esa ciudad, no se pudo
generar copia',16,1)elseprint ('Copia generada')end
• Cursor. Variable que permite recorrer un conjunto de resultados obtenidos a través de un SELECT fila a fila: permiten situarse en filas específicas del conjunto de resultados.
• Recuperan una fila o bloque de filas. • Aceptan modificaciones de los datos de las filas
en la posición actual del conjunto de resultados
• En qué se podrían relacionar los SELECT INTO e INSERT INTO SELECT con cursores?
• Para trabajar con cursores se deben seguir los siguientes pasos:
• Declarar el cursor, utilizando DECLARE• Abrir el cursor, utilizando OPEN• Leer los datos del cursor, utilizando FETCH ...
INTO• Cerrar el cursor, utilizando CLOSE• Liberar el cursor, utilizando DEALLOCATE
• Sintaxis General
• -- Declaración del cursor• DECLARE <nombre_cursor> CURSOR • FOR• <sentencia_sql>
• -- apertura del cursor• OPEN <nombre_cursor>
• -- Lectura de la primera fila del cursor• FETCH <nombre_cursor> INTO <lista_variables>
• WHILE (@@FETCH_STATUS = 0)• BEGIN • -- Lectura de la siguiente fila de un cursor• FETCH <nombre_cursor> INTO <lista_variables>• ... • END -- Fin del bucle WHILE
• -- Cierra el cursor• CLOSE <nombre_cursor>• -- Libera los recursos del cursor• DEALLOCATE <nombre_cursor>
• @@FETCH_STATUS. Variable global. Devuelve el estado de la última instrucción FETCH de cursor ejecutada.
• Ejemplo. Abrir un cursor y recorrerlo:
DECLARE persona_Cursor CURSOR FOR SELECT Nombre, Apellido, CiudadFROM persona WHERE varon=1
OPEN persona_Cursor
FETCH NEXT FROM persona_Cursor
WHILE @@FETCH_STATUS = 0 BEGIN
FETCH NEXT FROM persona_Cursor
END CLOSE persona_Cursor DEALLOCATE persona_Cursor
• Ejemplo. Abrir un cursor, recorrerlo e imprimir:
DECLARE @nombre varchar(20) DECLARE @apellido varchar(20) DECLARE @ciudad varchar(20)
DECLARE persona_Cursor CURSOR FOR SELECT Nombre, Apellido, CiudadFROM persona WHERE varon=1ORDER BY Apellido, Nombre
OPEN persona_Cursor FETCH NEXT FROM persona_Cursor INTO @nombre, @apellido, @ciudad WHILE @@FETCH_STATUS = 0 BEGIN
PRINT +@nombre +' '+@apellido +' vive en '+@ciudad
FETCH NEXT FROM persona_Cursor INTO @nombre, @apellido, @ciudad
END CLOSE persona_CursorDEALLOCATE persona_Cursor
• Ejemplo. Abrir un cursor, recorrerlo y actualizar:
DECLARE @nombre varchar(20)
DECLARE @creditos float
DECLARE @coste float
DECLARE asig_Cursor CURSOR FOR
SELECT Nombre, creditos, costebasico
FROM asignatura
OPEN asig_Cursor
FETCH NEXT FROM asig_Cursor into @nombre, @creditos, @coste
WHILE @@FETCH_STATUS = 0
BEGIN
update asignatura
set costebasico=@coste+@creditos*4
where nombre=@nombre
FETCH NEXT FROM asig_Cursor into @nombre, @creditos, @coste
END
CLOSE asig_Cursor
DEALLOCATE asig_Cursor
SET NOCOUNT ONDECLARE @nom varchar(20), @cont varchar(30), @message varchar (80),@nombp varchar (20)PRINT '-------- proveedores almacen kollao--------'DECLARE proov_cursor CURSOR FOR SELECT nombre, nombre_contactoFROM proveedorOPEN proov_cursor FETCH NEXT FROM proov_cursorINTO @nom, @cont WHILE @@FETCH_STATUS = 0 BEGIN PRINT ' 'SELECT @message = '----- productos por proveedor: ' + @nom + ' ,' + @cont PRINT @message DECLARE prod_cursor CURSOR FOR SELECT nombre_fantasiaFROM proveedor, productoWHERE producto.proveedor=proveedor.codigo_proveedor and proveedor.nombre=@nomOPEN prod_cursor FETCH NEXT FROM prod_cursor INTO @nombp IF @@FETCH_STATUS <> 0 PRINT ' <<No productos>>'WHILE @@FETCH_STATUS = 0 BEGIN SELECT @message = ' ' + @nombpPRINT @message FETCH NEXT FROM prod_cursor INTO @nombp END CLOSE prod_cursor DEALLOCATE prod_cursor FETCH NEXT FROM proov_cursor INTO @nom, @cont END CLOSE proov_cursor DEALLOCATE proov_cursor
• /* Este cursor deja las contraseñas iguales al nombre de usuario. La tabla Cliente tiene estos tres campos: CliCod, CliUser, CliPass */
-- declaramos las variables declare @cod as int declare @user as varchar(50) declare @pass as varchar(50)
• -- declaramos un cursor llamado "CURSORITO". declare CURSORITO cursor for select CliCod, CliUser, CliPass from Cliente
• open CURSORITO -- Avanzamos un registro y cargamos en las variables los valores encontrados en el primer registro
• fetch next from CURSORITO into @cod, @user, @pass while @@fetch_status = 0 begin update Cliente set CliPass= @user where CliCod=@cod -- Avanzamos otro registro fetch next from CURSORITO into @cod, @user, @pass end -- cerramos el cursor close CURSORITO deallocate CURSORITO
• Generar un PA que a traves de un cursor, imprima el nombre, creditos y cuatrimestre de las asignaturas registradas.
create procedure asignaturacusorasDECLARE @nombre varchar(20) DECLARE @creditos floatDECLARE @cuatri int
DECLARE asignatura_Cursor CURSOR FOR SELECT Nombre, creditos, cuatrimestreFROM asignatura
OPEN asignatura_Cursor FETCH NEXT FROM asignatura_Cursor INTO @nombre, @creditos, @cuatri WHILE @@FETCH_STATUS = 0 BEGIN
PRINT 'Asignatura ' +@nombre +' corresponde a cuatrimestre ' +cast(@cuatri as varchar)+' y tiene '+cast(@creditos as varchar) +' creditos'
FETCH NEXT FROM asignatura_Cursor INTO @nombre, @creditos, @cuatri
END CLOSE asignatura_CursorDEALLOCATE asignatura_Cursor
exec asignaturacusor
• Generar un PA que a traves de un cursor, imprima el nombre, creditos y cuatrimestre de ciertas asignaturas registradas, de acuerdo a parametro de entrada (cuatrimestre).
create procedure asignaturacusorotro (@cuatr int)asDECLARE @nombre varchar(20) DECLARE @creditos floatDECLARE @cuatri int
DECLARE asignatura_Cursor CURSOR FOR SELECT Nombre, creditos, cuatrimestreFROM asignaturawhere cuatrimestre=@cuatr
OPEN asignatura_Cursor FETCH NEXT FROM asignatura_Cursor INTO @nombre, @creditos, @cuatri WHILE @@FETCH_STATUS = 0 BEGIN
PRINT 'Asignatura ' +@nombre +' corresponde a cuatrimestre ' +cast(@cuatri as varchar)+' y tiene '+cast(@creditos as varchar) +' creditos'
FETCH NEXT FROM asignatura_Cursor INTO @nombre, @creditos, @cuatri
END CLOSE asignatura_CursorDEALLOCATE asignatura_Cursor
alter procedure asignaturacusorotro (@cuatr int)asDECLARE @nombre varchar(20) DECLARE @creditos floatDECLARE @cuatri int
DECLARE asignatura_Cursor CURSOR FOR SELECT Nombre, creditos, cuatrimestreFROM asignaturawhere cuatrimestre=@cuatr
OPEN asignatura_Cursor FETCH NEXT FROM asignatura_Cursor INTO @nombre, @creditos, @cuatri if @@fetch_status<>0print 'No hay registros'WHILE @@FETCH_STATUS = 0 BEGIN
PRINT 'Asignatura ' +@nombre +' corresponde a cuatrimestre ' +cast(@cuatri as varchar)+' y tiene '+cast(@creditos as varchar) +' creditos'
FETCH NEXT FROM asignatura_Cursor INTO @nombre, @creditos, @cuatri
END CLOSE asignatura_CursorDEALLOCATE asignatura_Cursor
• Cursores con parametros.
• Al declarar un cursor podemos definir:
• DECLARE <nombre_cursor> CURSOR [ LOCAL | GLOBAL ] • [ FORWARD_ONLY | SCROLL ] [ STATIC | KEYSET | DYNAMIC
| FAST_FORWARD ] [ READ_ONLY | SCROLL_LOCKS | OPTIMISTIC ] [ TYPE_WARNING ] FOR <sentencia_sql>
INVESTIGAR• [ LOCAL | GLOBAL ]• [ FORWARD_ONLY | SCROLL ]• [ STATIC | DYNAMIC | FAST_FORWARD ]• [ READ_ONLY]
• [ LOCAL | GLOBAL ].
• LOCAL. Especifica que el ámbito del cursor es local para el proceso por lotes, procedimiento almacenado o trigger en que se creó el cursor.
•DECLARE cClientes CURSOR LOCAL FOR
• SELECT …
• GLOBAL. Especifica que el ámbito del cursor es global para la conexión. Puede hacerse referencia al nombre del cursor en cualquier procedimiento almacenado o proceso por lotes que se ejecute en la conexión.
•DECLARE cClientes CURSOR GLOBAL FOR
• SELECT…
• Si no se especifica GLOBAL ni LOCAL, el valor predeterminado es local.
• [ FORWARD_ONLY | SCROLL ]
• FORWARD_ONLY. Especifica que el cursor sólo se puede desplazar de la primera a la última fila. FETCH NEXT es la única opción de recuperación admitida.
• DECLARE cClientes CURSOR FORWARD_ONLY FOR• SELECT …
• SCROLL. Especifica que están disponibles todas las opciones de recuperación (FIRST, LAST, PRIOR, NEXT, RELATIVE, ABSOLUTE).
• Si no se especifica SCROLL en una instrucción DECLARE CURSOR la única opción de recuperación que se admite es NEXT.
• Si se incluye la opción SCROLL, la forma en la realizamos la lectura del cursor varia, debiendo utilizar la siguiente sintaxis: FETCH [ NEXT | PRIOR | FIRST | LAST | RELATIVE | ABSOLUTE ] FROM < INTO
-- Declaracion de variables para el cursorDECLARE @Id int,@Nombre varchar(255),@Apellido1 varchar(255),@Apellido2 varchar(255),@NifCif varchar(20),@FxNacimiento datetime -- Declaración del cursorDECLARE cClientes CURSOR SCROLL FORSELECT Id, Nombre, Apellido1, Apellido2, NifCif, FxNacimientoFROM CLIENTES-- Apertura del cursorOPEN cClientes-- Lectura de la primera fila del cursorFETCH NEXT FROM cClientes INTO @id, @Nombre, @Apellido1, @Apellido2, @NifCif, @FxNacimiento WHILE (@@FETCH_STATUS = 0 )BEGINPRINT @Nombre + ' ' + @Apellido1 + ' ' + @Apellido2-- Lectura de la siguiente fila del cursorFETCH NEXT FROM cClientes INTO @id,@Nombre,@Apellido1,@Apellido2,@NifCif,@FxNacimientoEND-- Lectura de la fila anteriorFETCH PRIOR FROM cClientes
INTO @id, @Nombre, @Apellido1, @Apellido2, @NifCif, @FxNacimientoPRINT @Nombre + ' ' + @Apellido1 + ' ' + @Apellido2-- Cierre del cursorCLOSE cClientes-- Liberar los recursosDEALLOCATE cClientes
• [ STATIC | DYNAMIC | FAST_FORWARD ].
• STATIC. Define un cursor que hace una copia temporal de los datos que va a utilizar. Todas las solicitudes que se realizan al cursor se responden desde esta tabla temporal de tempdb; las modificaciones realizadas en las tablas base no se reflejan en los datos devueltos por las operaciones de recuperación realizadas en el cursor. No admite modificaciones.
• DECLARE cClientes CURSOR STATIC FOR
• DYNAMIC. Define un cursor que, al desplazarse por él, refleja en su conjunto de resultados todos los cambios realizados en los datos de las filas.
• DECLARE cClientes CURSOR DYNAMIC FOR
• FAST_FORWARD. Especifica un cursor FORWARD_ONLY, READ_ONLY. No se puede especificar FAST_FORWARD si se especifica también SCROLL o FOR_UPDATE.
• DECLARE cClientes CURSOR FAST_FORWARD FOR
• [ READ_ONLY ].
• READ_ONLY. Evita que se efectúen actualizaciones a través de este cursor.
• DECLARE cClientes CURSOR READ_ONLY FOR
• Clase especial de procedimiento almacenado que se ejecuta automáticamente (se “dispara”) cuando se produce un evento en el servidor de bases de datos.
• Se ejecuta siempre que se intenta modificar los datos de una tabla que el trigger protege: realizar un INSERT, UPDATE o DELETE…dependiendo de la accion especificada.
• No es posible evitar su ejecución.• Los triggers se definen para una tabla específica,
denominada tabla del trigger.
• No es posible invocar directamente los triggers, que tampoco pasan ni aceptan parámetros.
• Gran herramienta para controlar reglas de negocio más complejas que una simple integridad referencial.
• Ojo: el usuario no espera que el trigger le devuelva registros luego de agregar o modificar información.
• Sintaxis.
CREATE trigger <Nombre del trigger>ON <Nombre de la Tabla>FOR <INSERT l UPDATE l DELETE>ASSentencias….
• Un trigger para inserción de registros genera automáticamente una tabla en el cache con la información que intenta añadir, esta tabla se denomina INSERTED y es a través de esta tabla que se pueden hacer comparaciones en otras tablas.
• Un trigger para eliminación de registros genera automáticamente una tabla en el cache con la información que intenta eliminar, esta tabla se denomina DELETED y es a través de esta tabla que se pueden hacer comparaciones en otras tablas.
• Si se trata de un trigger para actualización se generan ambas tablas INSERTED con los nuevos datos y DELETED con la información que será reemplazada.
• Un trigger se "dispara" sólo cuando la instrucción de modificación de datos finaliza.
• SQL Server verifica la posible violación de tipos de datos, reglas o restricciones de integridad.
• El trigger y la instrucción que lo "dispara" se consideran una sola transacción que puede revertirse desde dentro del disparador.
• Si se detecta un error grave, se revierte toda la transacción.
create TRIGGER reminder ON persona
FOR INSERT, UPDATE AS
PRINT 'Se está intentando modificar datos‘
/*RAISERROR...*/
Generar trigger e intentar insertar o actualizar un registro...
• Las personas que se registren en almacenes Kollao como clientes deben ser mayores de 18 años.
• Como seria un trigger que verifique esto?
create trigger insertclienteedad on clientefor insert as
declare @fecha datetimeselect @fecha= fecha_nacimiento from insertedif (datediff(yy,@fecha, getdate()))<18beginraiserror (‘No se aceptan clientes menores de 18
años',16,1)endelseprint ‘Se inserto cliente exitosamente'
create trigger insertclienteedad on clientefor insert as
declare @fecha datetimeselect @fecha= fecha_nacimiento from insertedif (datediff(yy,@fecha, getdate()))<18beginraiserror (‘No se aceptan clientes menores de 18 años',16,1)rollback transaction /*deshace la transaccion*/endelseprint ' Se inserto cliente exitosamente '
• Se puede entonces:
• Generar un PA simple para insercion en cliente…
• Generar un PA para insercion que tome en cuenta reglas de integridad …
• Generar un trigger para la insercion en cliente que tome en cuenta la regla de negocio.
create PROCEDURE insert_cliente (@rut_cliente_1 [varchar](10), @primer_nombre_2 [varchar](15), @segundo_nombre_3 [varchar](15), @primer_apellido_4 [varchar](15), @segundo_apellido_5 [varchar](15), @calle_6 [varchar](20), @numero_7 [varchar](3), @ciudad_8 [varchar](15), @mail_9 [varchar](30), @fecha_nacimiento_10 [smalldatetime], @Sexo_11 [char](1))
AS if not exists(select * from cliente where rut_cliente=@rut_cliente_1)INSERT INTO [empresa].[dbo].[cliente]
( [rut_cliente], [primer_nombre], [segundo_nombre], [primer_apellido], [segundo_apellido], [calle], [numero], [ciudad], [mail], [fecha_nacimiento], [Sexo])
VALUES ( @rut_cliente_1, @primer_nombre_2, @segundo_nombre_3, @primer_apellido_4, @segundo_apellido_5, @calle_6, @numero_7, @ciudad_8, @mail_9, @fecha_nacimiento_10, @Sexo_11)
else raiserror('No se puede ingresar cliente, rut esta actualmente en uso',16,1)
create PROCEDURE insert_cliente1 (@rut_cliente_1 [varchar](10), @primer_nombre_2 [varchar](15), @segundo_nombre_3 [varchar](15),
@primer_apellido_4 [varchar](15), @segundo_apellido_5 [varchar](15), @calle_6 [varchar](20), @numero_7 [varchar](3),
@ciudad_8 [varchar](15), @mail_9 [varchar](30), @fecha_nacimiento_10 [smalldatetime], @Sexo_11 [char](1))
AS INSERT INTO [empresa].[dbo].[cliente]
( [rut_cliente], [primer_nombre], [segundo_nombre], [primer_apellido], [segundo_apellido], [calle], [numero], [ciudad], [mail], [fecha_nacimiento], [Sexo])
VALUES ( @rut_cliente_1, @primer_nombre_2, @segundo_nombre_3, @primer_apellido_4, @segundo_apellido_5, @calle_6, @numero_7, @ciudad_8, @mail_9, @fecha_nacimiento_10, @Sexo_11)
if @@rowcount=0raiserror('No se pudo realizar el registro de cliente',16,1)
• Hacer pruebas...
• exec insert_cliente '03772222-7’,'Loreto','Loreto','Lagos','Jeria','Heras','345','Concepcion','[email protected]','23-5-1980','f‘
• …
• TAREA 29-6 (con nota)-hecho.
• Se necesita insertar un producto cuya fecha de vencimiento sea minimo en 15 dias despues del registro. Si no es asi, no se debe realizar la insercion.
• Generar PA de insercion en producto que tome en cuenta integridad referencial (pk y fk).
• Regla de negocio la manejara trigger.• Hacer pruebas.
create PROCEDURE [insert_producto_1](@codigo_producto_1 [varchar](8), @nombre_fantasia_2 [varchar](20), @marca_3 [varchar](15), @detalle_4 [varchar](100), @precio_5 [money], @fecha_vencimiento_6 [smalldatetime], @proveedor_7 [varchar](6), @stock_8 [int])
AS if not exists(select * from producto where codigo_producto=@codigo_producto_1) begin if exists (select * from proveedor where codigo_proveedor=@proveedor_7) begin INSERT INTO producto VALUES ( @codigo_producto_1, @nombre_fantasia_2, @marca_3, @detalle_4,
@precio_5, @fecha_vencimiento_6, @proveedor_7, @stock_8)
end else raiserror ('Proveedor no esta registrado, no se puede registrar producto',16,1) endelseraiserror ('Codigo de producto ya existe, no se puede registrar producto',16,1)
create trigger insertproducto on productofor insert as
declare @fecha datetimeselect @fecha= fecha_vencimiento from insertedif (datediff(dd,getdate(),@fecha))<15beginraiserror ('No se aceptan productos con esa fecha de vencimiento',16,1)rollback transaction endelseprint ‘La fecha de vencimiento es válida, se realizo registro'
insert into productovalues ('ere45434','pañuelos','elite','dfsfdfd',640,‘12-7-2011','JG6543',5)
• Ejercicio-hecho.
• Se necesita insertar un producto nuevo. Si su stock es menor que 10, no se debe realizar la insercion.
• PA de insercion ya esta creado.***• Generar trigger.• Hacer pruebas.
create trigger insertproductostock on productofor insert as
if (select stock from inserted)<10beginraiserror ('No se aceptan productos con tan poco
stock',16,1)rollback transaction endelseprint ‘Stock valido'
• Ejercicio con nota-hecho.
• El almacen solo maneja 5 productos por proveedor registrado en la BD. No se debe poder insertar un nuevo producto si no se cumple esta regla.
• PA de insercion creado.***• Generar trigger.• Hacer pruebas.
create trigger insertproductoproveedor on productofor insert as
if (select count(*) from producto, inserted where producto.proveedor=inserted.proveedor)=6
beginraiserror ('Solo se aceptan 5 productos por
proveedor',16,1)rollback transaction endelseprint ‘Proveedor con cantidad de productos ok'
• Al modificar algun dato de sucursal, se debe manejar un respaldo de estos datos (los antiguos), incluida fecha y usuario que genero la modificacion.
• Cómo sería?• Generar PA para modificacion de sucursal
(direccion, telefono) tomando en cuenta integridad (pk, fk)
create table auditsuc
(nr int identity (1,1) primary key,
cod varchar(6),
dir varchar (50),
tel varchar (12),
fecha datetime,
usuario varchar(10))
create trigger modsucursal on sucursalfor update as
declare @cod varchar(6), @dir varchar (50), @tel varchar(12)
select @cod=(select codigo_sucursal from deleted) select @dir=(select direccion from deleted) select @tel=(select telefono_sucursal from deleted) insert into auditsuc (cod, dir, tel,fecha,usuario)values(@cod, @dir, @tel, getdate(), user)
• De acuerdo a las reglas de negocio, no se pueden actualizar varios registros de sucursales a la vez.
• Usando @@rowcount…como seria un trigger que verifique esto?
alter trigger modsucursal on sucursalfor update as
if @@rowcount=1begindeclare @cod varchar(6), @dir varchar (50), @tel varchar(12)select @cod=(select codigo_sucursal from deleted) select @dir=(select direccion from deleted) select @tel=(select telefono_sucursal from deleted)
insert into auditsuc (cod, dir, tel,fecha,usuario)values(@cod, @dir, @tel, getdate(), user)endelseraiserror(‘No se pueden realizar actualizaciones de varias filas',16,1)rollback transaction
• Se debe generar copia del dato actualizado, fecha y usuario que lo realizo.
• Se actualiza telefono de sucursal.• Generar PA de actualizacion de telefono
sucursal (a traves de codigo).• Generar trigger.
create table auditsuc1(nr int identity (1,1) primary key,cod varchar(6),tel varchar (12),fecha datetime,usuario varchar(10))
create trigger modsucursaltel on sucursalfor update asif update(telefono_sucursal)begindeclare @cod varchar(6), @tel varchar(12)select @cod=(select codigo_sucursal from deleted) select @tel=(select telefono_sucursal from deleted) insert into auditsuc1 (cod, tel,fecha,usuario)values(@cod, @tel, getdate(), user)end
create table auditproveedor(nr int identity (1,1) primary key,cod varchar(6),nom varchar (20),contacto varchar (30),tel varchar (12),fecha datetime,usuario varchar(10))
create trigger delproveedor on proveedorfor delete as
declare @cod varchar(6), @nom varchar (20), @contacto varchar (30),@tel varchar(12)
select @cod=(select codigo_proveedor from deleted) select @nom=(select nombre from deleted) select @contacto=(select nombre_contacto from deleted) select @tel=(select telefono_contacto from deleted)
insert into auditproveedor (cod, nom, contacto, tel ,fecha,usuario)
values(@cod, @nom, @contacto, @tel, getdate(), user)
create trigger delproveedor on proveedorfor delete as
declare @cod varchar(6), @nom varchar (20), @contacto varchar (30),@tel varchar(12)
If @@rowcount=1beginselect @cod=(select codigo_proveedor from deleted) select @nom=(select nombre from deleted) select @contacto=(select nombre_contacto from deleted) select @tel=(select telefono_contacto from deleted) insert into auditproveedor (cod, nom, contacto, tel ,fecha,usuario)values(@cod, @nom, @contacto, @tel, getdate(), user)endElse Raiserror (‘No se puede eliminar mas de un proveedor a la vez’,16,1)rollback transaction
create trigger delproveedor1 on proveedorfor delete as
IF (SELECT COUNT(*) FROM Deleted) > 1 BEGIN RAISERROR(‘No puede borrar más de un
proveedor al mismo tiempo.’,16, 1) ROLLBACK TRANSACTION END
create trigger delproveedor1 on proveedorfor delete as
IF @@rowcount > 1 BEGIN RAISERROR(‘No puede borrar más de un
proveedor al mismo tiempo.’,16, 1) ROLLBACK TRANSACTION END
• Conocer triggers creados:• select *
from sysobjects where type = ‘TR’
• Obtener el texto del trigger• sp_helptext nombretrigger