29 de mayo de 2001

I.R. con Triggers (SQL)

Probablemente en las futuras versiones de SQL Server Microsoft incluya la propagación en cascada de las restricciones de claves externas, de momento hasta la versión 7, o lo gestionamos en la aplicación cliente o con procedimientos almacenados.

Estos procedimientos los podemos aplicar a cualquier gestor de base de datos, (SQL Server, Informix, Oracle, Sybase, etc), siempre verificando la sintaxis, ya que de un gestor a otro varía. Los procedimientos expuestos son aplicables a SQL Server.

Tablas Inserted y Deleted

Estas tablas contienen las filas modificadas almacenadas en el registro de transacciones, en realidad son vistas, su estructura coincide con la tabla que acaba de ser modificada. Se utilizan para conocer que cambios se han realizado, hago referencia a ellas dentro de los Triggers.

Borrado en cascada

Este tipo de disparador es fácil de implementar. Con un ejemplo lo ilustraremos mejor, vamos a crear un Trigger para borrar la cabecera de una factura y sus líneas.
CREATE TRIGGER CabFac_Del_LinFac ON CabFac                
FOR DELETE AS
IF @@ROWCOUNT = 0  
  RETURN  
DELETE LinFac  
FROM LinFac L, deleted d  
WHERE L.Id_Fac = d.Id_Fac  
IF @@ERROR  0  
  BEGIN  
    RAISEERROR (‘Error al borrar líneas de factura….’, 16, 1)  
    ROLLBACK TRAN
    RETURN
  END
Hay que tener presente que los Triggers se activan posteriormente a la acción, por tanto hay que revisar las restricciones de clave externa de la tabla de CabFac para que permita borrar.

Actualizaciones en cascada

Esto si que se complica un poco. Pensemos en lo que significa modificar una clave primaria, seria como eliminar una fila e insertar una nueva, esto hace que perdamos la relación entre las filas a las que deben afectar los cambios. Personalmente os recomiendo que limitéis la actualización a una fila, de este modo las tablas inserted y deleted solo tendrán una fila. Utilicemos el ejemplo de la cabecera y líneas de factura para ilustrar este procedimiento.
CREATE TRIGGER
CabFac_Upd_LinFac ON CabFac  
FOR UPDATE AS  
   DECLARE @nRows INT, @nId_Old INT, @nId_new INT  
  SELECT @nRows = @@ROWCOUNT  
  IF @nRows = 0  
    RETURN  
  IF UPDATED(Id_Fac)  
    BEGIN  
    IF @nRows = 1  
      BEGIN  
        SELECT @nId_Old = Id_Fac FROM deleted  
        SELECT @nId_New = Id_Fac FROM inserted  
        UPDATE LinFac SET id_Fac = @nId_New  
        WHERE Id_Fac = @nId_Old  
      END  
  ELSE  
    BEGIN  
      RAISERROR (‘No se puede actualizar mas de una Factura a la vez’, 16, 1)  
      ROLLBACK TRAN  
      RETURN  
    END
  END

Algunas recomendaciones

Revisar las restricciones de claves externas ya que pueden impedir que se dispare el Trigger.
Una buena técnica para las actualizaciones en cascada es insertar una nueva fila con el valor de la clave nueva, modificar las filas de la tabla que hacen referencia a este valor y por ultimo eliminar la fila antigua.

Rapidito

Como podéis observar integrar todo este código de Triggers es una tarea ardua, sobre todo en bases de datos con muchas tablas, para agilizar este proceso podemos utilizar el Asistente para Upsizing del VFP (solo SQL Server y Oracle :-( ) , el Access (con mucho cuidado ;-) ) u otra herramienta especifica para estos menesteres.

Luis Rey García
Advanced & Frontdata Systems, S.L.

23 de mayo de 2001

OBTENER UN CAMPO AUTOINCREMENTAL

Supongo que muchos programadores utilizaran en sus bases de datos campos Autoincrementales, y más de uno habrá tenido problemas a la hora de saber que es lo que acaba de insertar.

Buenas noticias:
En la mayoría de las DBS existen unas variables globales que guardan el ultimo valor añadido a una columna Autoincremental.

Malas noticias:
Las variables de los autoincrementales no son las mismas para diferentes DBS.
El proceso en el que intervenga un campo autoincremental es un poco manual

Tres pasos:
Establecer las transacciones a MANUALES
= SQLSetProp(THIS.nConn, "Transactions", 2)
Insertamos la fila.
VL_C_SQL = “INSERT NIVEL ( DESCR ) VALUES ( “ + THISFORM.DESCR.VALUE + “ )”
IF SQLExec(THIS.nConn, VL_C_SQL) 
   THIS.GetLastError
   = SQLRollBack(THIS.nConn)
   Return -1
ENDIF
Obtenemos el autoincremental
VL_C_SQL = “SELECT @@IDENTITY””
IF SQLExec(THIS.nConn, VL_C_SQL, “CX_SERIAL”)
   THIS.GetLastError
   =SQLRollBack(THIS.nConn)
   Return –1
ENDIF

THIS.Serial = CX_SERIAL.exp

Este código esta sacado de su contexto habitual, espero que os aclare las dudas.

Pablo Roca

15 de mayo de 2001

Calcula el factorial de un número

Función recursiva para calcular el factorial de un número
*************************************
* Funcion recursiva para calcular el factorial de un numero
* para todo N en los naturales > 0
* Forma de Uso: ? Factorial(cb) 
*
* Autor: Ramón Rodriguez Martinez
* e-mail cesa@ases.net.mx
* URL http://www.empresavirtual.com.mx
************************************
Function Factorial(Numero)
if Numero
    return 1
Else
    Numero=Numero*Factorial(Numero-1)
Endif
Return Numero
EndFunc

Ordena con QuickSort

Funcion recursiva para ordenar arrays por el método de QuickSort

**************************************
* Funcion recursiva para ordenar arrays por el método de QuickSort
* L es el límite inferior del arreglo y R el limite superior
* o lo que es lo mismo L es la parte izquirda de la particion y R la derecha
*
* Se utiliza A como una variable privada previamente definida 
* Ejemlo de uso
*
*Dimension a[3]
*A[1]=5
*A[2]=4
*A[3]=3
*qsort(1,3)
*?A[1],A[2],a[3]
*
* Autor: Ramón Rodriguez Martinez
**************************************
procedure qSort(l, r)
Local   i, j, x, y
 i = l
 j = r
 x = a[Int((l+r)/2)]
 Do While i < j
     Do while a[i] < x
      i = i + 1
     EndDo
     Do while x < a[j]
      j = j - 1
     EndDo
     if i <= j then
        y = a[i]
        a[i] = a[j]
        a[j] = y
        i = i + 1
        j = j - 1
     endIf
   EndDo
   if l < j then
    qSort(l, j)
   endif
   if i < r then
    qSort(i, r)
   endif
endProc
Ramón Rodriguez Martinez