none
Triggers de borrado RRS feed

  • Pregunta

  • Hola a todos,

       a raíz de que es imposible definir constraints cíclicas me vi obligado a remplazar algunas de ellas por triggers. Una de las soluciones propuestas, la cual implementé, fue usar triggers INSTEAD OF pero me surgió una duda. Supongamos que una tabla tiene varias constraints pero solo remplazando una de ellas el problema cíclico queda resuelto. Si uso un trigger INSTEAD OF me obliga a remplazar todas las constraints (algunas pueden tener un borrado en cascada). Cómo manejan este tipo de situaciones? Existe el trigger AFTER DELETE pero no un BEFORE DELETE que creo que es lo que necesitaría en este caso (es decir, chequear si puedo borrar un registro asegurándome que no tiene hijos en la tabla relacionada).

        Cualquier ayuda es bienvenida.

        Saludos.


    Mauricio Copenhague, Dinamarca
    jueves, 14 de julio de 2011 10:07

Respuestas

  • Disculpa Mauricio, te dejé un poco colgado en este hilo.

    Ahora entiendo tu problema, tenía yo otra idea de lo que me estabas intentando decir. La solución pasa por lo que te temías: tienes que forzar la integridad mediante triggers, eliminando las claves foráneas entre las tablas que forman parte de esta relación.

    Este artículo de Itzik (http://www.sqlmag.com/article/tsql3/cascading-alternatives), aunque un tanto viejo, muestra soluciones para afrontar una situación como la que tienes. Pero vamos, que lo que tendrás que hacer son triggers que realicen las acciones.

    martes, 19 de julio de 2011 8:13

Todas las respuestas

  • No termino de entender la situación que comentas, para el caso concreto del que hablas, usa la opción INSTEAD OF. Ahí podrías chequear si el registro tiene datos relacionados para proceder al borrado o no
    jueves, 14 de julio de 2011 10:25
  • Hola Carlos,

       me imaginé que mi pregunta resultó algo enrevesada. Intentaré dar un ejemplo: Supongamos que la tabla A está relacionada con B y con C. Pero no puedo definir la constraint entre A y C porque entro en un ciclo, así que remplazo por triggers. Pero las constraints son, para A-B, borrado en cascada y para A-C, borrado restringido. Si uso un triggers INSTEAD of, ¿no me veo obligado a eliminar ambas constraints y remplazarlas por triggers?

     


    Mauricio Copenhague, Dinamarca
    jueves, 14 de julio de 2011 10:50
  • No, tú podrías tener un trigger INSTEAD OF que haga sus validaciones entre la tabla A y B y a su vez una restricción de clave foránea entre A y C. Ambas cosas son complementarias y que exista una no significa que no pueda existir la otra
    jueves, 14 de julio de 2011 11:43
  • Podrías darme un ejemplo, Carlos? Porque no logro encontrarle la vuelta.
    Mauricio Copenhague, Dinamarca
    jueves, 14 de julio de 2011 12:00
  • Postea el script de creación de las tablas involucradas en lo que quieres hacer (no todo, sólo los campos necesarios) con unos datos de ejemplo y lo que quieres conseguir e intento montártelo

    De todos modos, si estás intentando garanizar la integridad referencial por medio de triggers (código al fin y al cabo), creo que te sería más cómodo implementarla a través de procedimientos almacenados, ya que lo suyo además es que las aplicaciones no accedan a las tablas directamente sino a través de los procedimientos que tú les expongas. Además, no son tan engorrosos y, sobre todo, no están tan ocultos como puedan estarlo los triggers.

    • Editado Carlos Sacristan jueves, 14 de julio de 2011 12:09 comentario acerca procs
    jueves, 14 de julio de 2011 12:06
  • -- Tabla DBBRUGER

    CREATE TABLE [dbo].[DBBRUGER](
        [PREC] [int] IDENTITY(1,1) NOT NULL,
        [NR] [int] NULL,
        [INIT] [char](4) NULL,
        [KODE] [char](6) NULL,
        [NAVN] [char](20) NULL,
        [MCSTEMP] [varchar](79) NULL,
        [NIV] [smallint] NULL,
        [AFD_KUNDER] [char](100) NULL,
        [PRINTER] [varchar](79) NULL,
        [DATE] [int] NULL,
        [BRUGER] [char](4) NULL,
        [INDSTED] [smallint] NULL,
        [KOKKEN] [char](20) NULL,
        [AFD] [char](20) NULL,
        [VARE] [char](20) NULL,
        [KUNDE] [char](20) NULL,
        [RAPPORT] [char](20) NULL,
        [SETUP] [char](20) NULL,
        [AFD_KOSTTYPER] [char](100) NULL,
        [AFD_RAPPORTER] [char](100) NULL,
        [POS] [char](100) NULL,
        [FNAVN] [char](50) NULL,
        [WLOGIN] [varchar](40) NULL,
        [RC] [smallint] NULL,
        [RC_FILTER] [char](255) NULL,
        [ORD_KORR] [smallint] NULL,
        [SYNK_NR] [char](100) NULL,
        [KOK_NR] [int] NULL,
     CONSTRAINT [BRU:KEY] PRIMARY KEY CLUSTERED
    (
        [PREC] ASC
    )WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
    ) ON [PRIMARY]

    GO

    SET ANSI_PADDING OFF
    GO

    ALTER TABLE [dbo].[DBBRUGER]  WITH CHECK ADD  CONSTRAINT [FK_DBBRUGER_DBISTED] FOREIGN KEY([INDSTED])
    REFERENCES [dbo].[DBISTED] ([NR])
    ON UPDATE CASCADE
    GO

    ALTER TABLE [dbo].[DBBRUGER] CHECK CONSTRAINT [FK_DBBRUGER_DBISTED]
    GO

    ALTER TABLE [dbo].[DBBRUGER]  WITH CHECK ADD  CONSTRAINT [FK_DBBRUGER_DBKOKKEN] FOREIGN KEY([KOK_NR])
    REFERENCES [dbo].[DBKOKKEN] ([NR])
    ON UPDATE CASCADE
    ON DELETE CASCADE
    GO

    ALTER TABLE [dbo].[DBBRUGER] CHECK CONSTRAINT [FK_DBBRUGER_DBKOKKEN]
    GO


    -- Tabla MES


    CREATE TABLE [dbo].[MES](
        [PREC] [int] IDENTITY(1,1) NOT NULL,
        [NR] [int] NULL,
        [OVERSKRIFT] [char](255) NULL,
        [AFSENDER] [char](255) NULL,
        [TEXT] [text] NULL,
        [BNR] [int] NULL,
        [DATE] [int] NULL,
        [BRUGER] [char](5) NULL,
     CONSTRAINT [MES:KEY] PRIMARY KEY CLUSTERED
    (
        [PREC] ASC
    )WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
    ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]

    GO

    SET ANSI_PADDING OFF
    GO

    ALTER TABLE [dbo].[MES]  WITH CHECK ADD  CONSTRAINT [FK_MES_DBBRUGER] FOREIGN KEY([BNR])
    REFERENCES [dbo].[DBBRUGER] ([NR])
    ON UPDATE CASCADE
    ON DELETE CASCADE
    GO

    ALTER TABLE [dbo].[MES] CHECK CONSTRAINT [FK_MES_DBBRUGER]
    GO


    -- Tabla MESBRU


    CREATE TABLE [dbo].[MESBRU](
        [PREC] [int] IDENTITY(1,1) NOT NULL,
        [BNR] [int] NULL,
        [NR] [int] NULL,
        [STATUS] [smallint] NULL,
        [STATUSDATO] [int] NULL,
        [STATUSKL] [int] NULL,
        [DATE] [int] NULL,
        [BRUGER] [char](5) NULL,
     CONSTRAINT [MBR:KEY] PRIMARY KEY CLUSTERED
    (
        [PREC] ASC
    )WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
    ) ON [PRIMARY]

    GO

    SET ANSI_PADDING OFF
    GO

    ALTER TABLE [dbo].[MESBRU]  WITH CHECK ADD  CONSTRAINT [FK_MESBRU_MES] FOREIGN KEY([NR])
    REFERENCES [dbo].[MES] ([NR])
    ON UPDATE CASCADE
    ON DELETE CASCADE
    GO

    ALTER TABLE [dbo].[MESBRU] CHECK CONSTRAINT [FK_MESBRU_MES]
    GO


    Esta última tabla, MESBRU, también está relacionada con DBBRUGER (DBBRUGER(NR) -->> MESBRU(BNR)) pero que no puedo definir por el problema cíclico. Defino entonces un trigger INSTEAD OF pero como verás DBBRUGER también es padre de MES y DBSESSIONS y en el caso de DELETE lo debe hacer en cascada sobre estas tablas. Si defino un trigger INSTEAD OF me veo obligado también a definir, dentro de ese trigger, el borrado en cascada con estas otras tablas, ¿no es así?

    Nota: las relaciones están hechas usando el campo NR, el cual no es el IDENTITY. Esto se debe a que mi jefe hizo esto como 15 años atrás (esa es su excusa) y no sabía nada de SQL en ese momento (sigue igual). Para colmo el sistema está desarrollado en Clarion el cual genera código para borrar en cascada, lo cual hace el sistema terriblemente ineficiente. Mi objetivo es mover las relaciones desde el programa al motor a fin de optimizar el sistema. En general lo estoy logrando y a modo de ejemplo, el simple cambio de código de un registro, que en el sistema anterior demoraba 2'30'' ahora solo le lleva 8''. Igual sé que ni siquiera debería demorar nada si las relaciones estuviesen hechas usando el campo PREC pero desgraciadamente no puedo cambiar eso por ahora, el sistema es demasiado grande e implicaría re-escribir casi todo.

    Gracias por tu tiempo, Carlos.

     


    Mauricio Copenhague, Dinamarca
    jueves, 14 de julio de 2011 12:28
  • Hola TDCSoftware,  

     

    ¿Has podido solucionar tu problema?

     

    Saludos


    Eduardo Portescheller - LATAM Forum Support Engineer
    Microsoft Corporation
    lunes, 18 de julio de 2011 14:36
    Moderador
  • No, aún no, estaba esperando a ver si alguien me sugería cómo hacer el trigger.
    Mauricio - Copenhague - Dinamarca
    lunes, 18 de julio de 2011 17:11
  • Disculpa Mauricio, te dejé un poco colgado en este hilo.

    Ahora entiendo tu problema, tenía yo otra idea de lo que me estabas intentando decir. La solución pasa por lo que te temías: tienes que forzar la integridad mediante triggers, eliminando las claves foráneas entre las tablas que forman parte de esta relación.

    Este artículo de Itzik (http://www.sqlmag.com/article/tsql3/cascading-alternatives), aunque un tanto viejo, muestra soluciones para afrontar una situación como la que tienes. Pero vamos, que lo que tendrás que hacer son triggers que realicen las acciones.

    martes, 19 de julio de 2011 8:13