Unidad 3

107
Unidad 3

description

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. cabecera. cuerpo. BLOQUE. se incluyen todos los elementos de definición del programa; como nombre, parámetros (entrada y/o salida), - PowerPoint PPT Presentation

Transcript of Unidad 3

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

• 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)

• Insertar un registro de persona.

• Puede surgir un error ?

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'

• Uso de INTO en SELECT• Into en select…

• 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'

• Modificar PA usando @@error o @rowcount, raiserror

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

• Cursores

• 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

• Ejecutar el siguiente cursor

• Que hace?

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

• Triggers-Disparadores

• 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...

• INSERT

• Aplicar reglas de negocio…???

• 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'

• El cliente se registra de todas maneras…

• Qué hacer para evitar 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)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'

• Veamos que pasa con Update.

• 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)

• @@rowcount. Variable global. Guarda el numero de filas afectadas por la ultima instrucción.

• 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

• On UPDATE en una fila especifica…

• 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

• ON DELETE.

• Al eliminarse un proveedor, se debe manejar un respaldo de los datos eliminados.

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)

• No se puede borrar mas de un proveedor a la vez!!!

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

• ALTER TRIGGER• DROP Trigger

• Deshabilitar• ALTER TABLE compra• DISABLE TRIGGER insertcomprastock

• Habilitar• ALTER TABLE compra• ENABLE TRIGGER insertcomprastock