Duda sobre triggers

27/06/2007 - 08:24 por Juan Diego Bueno | Informe spam
Buenas:

Quería preguntaros como resolveis una situación concreta.
Tengo una tabla de proyectos cuya clave es un código. En ella tenemos un
campo TOTAL que recoge la suma de los importes de unas facturas que están en
otra tabla.Yo lo estoy resolviendo usando triggers tanto para eliminación
como para inserción y actualización, pero dichos desencadenadores acaban
usando cursores que recorren las tablas deleted o inserted. Dado que por
cuestiones de rendimiento no se aconseja el uso de cursores, ¿cómo podría
resolver esta situación de otra forma?

Se me ocurre que podría no usar ese campo y obtener el TOTAL en base a una
consulta, pero tal cual lo tengo ahora, me obligaría a cambiar bastantes
partes de mi aplicación, con lo cual quiero evitarlo.

Uso SQL Server 2005 Express

Gracias

Preguntas similare

Leer las respuestas

#1 Jesús López
27/06/2007 - 09:51 | Informe spam
Suponiendo que las tablas sean estas:

CREATE TABLE Proyectos
(
CodProyecto int PRIMARY KEY,
NombreProyecto varchar(50) NOT NULL,
Total money
)

CREATE TABLE Facturas
(
NumFactura int PRIMARY KEY,
CodProyecto int NOT NULL REFERENCES Proyectos(CodProyecto),
FechaFactura smalldatetime NOT NULL DEFAULT GETDATE(),
Importe money NOT NULL
)

GO

El siguiente trigger actualizaría automáticamente el campo Total de la tabla
Proyectos:

CREATE TRIGGER ActualizarTotalFacturas
ON Facturas
FOR INSERT, UPDATE, DELETE
AS
BEGIN
SET NOCOUNT ON;
WITH IncrementosFacturas(CodProyecto, Incremento)
AS
(
SELECT CodProyecto, SUM(Importe) AS Incremento
FROM
(
SELECT CodProyecto, Importe
FROM inserted
UNION ALL
SELECT CodProyecto, -Importe
FROM deleted
) AS F
GROUP BY CodProyecto
)
UPDATE P
SET Total = COALESCE(P.Total, 0) + [IF].Incremento
FROM IncrementosFacturas [IF] INNER JOIN Proyectos P
ON [IF].CodProyecto = P.CodProyecto

END
GO


Pero para la próxima vez, ten en cuenta que una vista indexada es más
eficiente y más sencillo que esto de los triggers.


Saludos:

Jesús López.
Solid Quality Mentors
www.solidq.com

"Juan Diego Bueno" escribió en el mensaje
news:
Buenas:

Quería preguntaros como resolveis una situación concreta.
Tengo una tabla de proyectos cuya clave es un código. En ella tenemos un
campo TOTAL que recoge la suma de los importes de unas facturas que están
en otra tabla.Yo lo estoy resolviendo usando triggers tanto para
eliminación como para inserción y actualización, pero dichos
desencadenadores acaban usando cursores que recorren las tablas deleted o
inserted. Dado que por cuestiones de rendimiento no se aconseja el uso de
cursores, ¿cómo podría resolver esta situación de otra forma?

Se me ocurre que podría no usar ese campo y obtener el TOTAL en base a una
consulta, pero tal cual lo tengo ahora, me obligaría a cambiar bastantes
partes de mi aplicación, con lo cual quiero evitarlo.

Uso SQL Server 2005 Express

Gracias



Respuesta Responder a este mensaje
#2 Juan Diego Bueno
27/06/2007 - 10:01 | Informe spam
Muchísimas gracias Jesús

Ya he oído hablar de las vistas indexadas, así que me lo miraré para la
próxima vez que necesite algo similar a esto. No había pensado en las
consultas recursivas para esto. Había visto otra opción muy similar, pero
estableciendo un trigger para cada operación y con updates clásicos

Lo dicho, gracias

Saludos

"Jesús López" escribió en el mensaje
news:O%23v23$
Suponiendo que las tablas sean estas:

CREATE TABLE Proyectos
(
CodProyecto int PRIMARY KEY,
NombreProyecto varchar(50) NOT NULL,
Total money
)

CREATE TABLE Facturas
(
NumFactura int PRIMARY KEY,
CodProyecto int NOT NULL REFERENCES Proyectos(CodProyecto),
FechaFactura smalldatetime NOT NULL DEFAULT GETDATE(),
Importe money NOT NULL
)

GO

El siguiente trigger actualizaría automáticamente el campo Total de la
tabla Proyectos:

CREATE TRIGGER ActualizarTotalFacturas
ON Facturas
FOR INSERT, UPDATE, DELETE
AS
BEGIN
SET NOCOUNT ON;
WITH IncrementosFacturas(CodProyecto, Incremento)
AS
(
SELECT CodProyecto, SUM(Importe) AS Incremento
FROM
(
SELECT CodProyecto, Importe
FROM inserted
UNION ALL
SELECT CodProyecto, -Importe
FROM deleted
) AS F
GROUP BY CodProyecto
)
UPDATE P
SET Total = COALESCE(P.Total, 0) + [IF].Incremento
FROM IncrementosFacturas [IF] INNER JOIN Proyectos P
ON [IF].CodProyecto = P.CodProyecto

END
GO


Pero para la próxima vez, ten en cuenta que una vista indexada es más
eficiente y más sencillo que esto de los triggers.


Saludos:

Jesús López.
Solid Quality Mentors
www.solidq.com

"Juan Diego Bueno" escribió en el mensaje
news:
Buenas:

Quería preguntaros como resolveis una situación concreta.
Tengo una tabla de proyectos cuya clave es un código. En ella tenemos un
campo TOTAL que recoge la suma de los importes de unas facturas que están
en otra tabla.Yo lo estoy resolviendo usando triggers tanto para
eliminación como para inserción y actualización, pero dichos
desencadenadores acaban usando cursores que recorren las tablas deleted o
inserted. Dado que por cuestiones de rendimiento no se aconseja el uso de
cursores, ¿cómo podría resolver esta situación de otra forma?

Se me ocurre que podría no usar ese campo y obtener el TOTAL en base a
una consulta, pero tal cual lo tengo ahora, me obligaría a cambiar
bastantes partes de mi aplicación, con lo cual quiero evitarlo.

Uso SQL Server 2005 Express

Gracias







Respuesta Responder a este mensaje
#3 Jesús López
27/06/2007 - 10:17 | Informe spam
Uso una CTE, pero no es recursiva. En realidad la CTE no es necesaria aquí,
sin embargo hace que sea más fácil de leer (al menos eso creo).

Saludos:

Jesús López.
Solid Quality Mentors
www.solidq.com

"Juan Diego Bueno" escribió en el mensaje
news:
Muchísimas gracias Jesús

Ya he oído hablar de las vistas indexadas, así que me lo miraré para la
próxima vez que necesite algo similar a esto. No había pensado en las
consultas recursivas para esto. Había visto otra opción muy similar, pero
estableciendo un trigger para cada operación y con updates clásicos

Lo dicho, gracias

Saludos

"Jesús López" escribió en el mensaje
news:O%23v23$
Suponiendo que las tablas sean estas:

CREATE TABLE Proyectos
(
CodProyecto int PRIMARY KEY,
NombreProyecto varchar(50) NOT NULL,
Total money
)

CREATE TABLE Facturas
(
NumFactura int PRIMARY KEY,
CodProyecto int NOT NULL REFERENCES Proyectos(CodProyecto),
FechaFactura smalldatetime NOT NULL DEFAULT GETDATE(),
Importe money NOT NULL
)

GO

El siguiente trigger actualizaría automáticamente el campo Total de la
tabla Proyectos:

CREATE TRIGGER ActualizarTotalFacturas
ON Facturas
FOR INSERT, UPDATE, DELETE
AS
BEGIN
SET NOCOUNT ON;
WITH IncrementosFacturas(CodProyecto, Incremento)
AS
(
SELECT CodProyecto, SUM(Importe) AS Incremento
FROM
(
SELECT CodProyecto, Importe
FROM inserted
UNION ALL
SELECT CodProyecto, -Importe
FROM deleted
) AS F
GROUP BY CodProyecto
)
UPDATE P
SET Total = COALESCE(P.Total, 0) + [IF].Incremento
FROM IncrementosFacturas [IF] INNER JOIN Proyectos P
ON [IF].CodProyecto = P.CodProyecto

END
GO


Pero para la próxima vez, ten en cuenta que una vista indexada es más
eficiente y más sencillo que esto de los triggers.


Saludos:

Jesús López.
Solid Quality Mentors
www.solidq.com

"Juan Diego Bueno" escribió en el
mensaje news:
Buenas:

Quería preguntaros como resolveis una situación concreta.
Tengo una tabla de proyectos cuya clave es un código. En ella tenemos un
campo TOTAL que recoge la suma de los importes de unas facturas que
están en otra tabla.Yo lo estoy resolviendo usando triggers tanto para
eliminación como para inserción y actualización, pero dichos
desencadenadores acaban usando cursores que recorren las tablas deleted
o inserted. Dado que por cuestiones de rendimiento no se aconseja el uso
de cursores, ¿cómo podría resolver esta situación de otra forma?

Se me ocurre que podría no usar ese campo y obtener el TOTAL en base a
una consulta, pero tal cual lo tengo ahora, me obligaría a cambiar
bastantes partes de mi aplicación, con lo cual quiero evitarlo.

Uso SQL Server 2005 Express

Gracias











Respuesta Responder a este mensaje
#4 Carlos M. Calvelo
27/06/2007 - 16:38 | Informe spam
Hola Juan,

On 27 jun, 08:24, "Juan Diego Bueno"
wrote:
Buenas:

Quería preguntaros como resolveis una situación concreta.
Tengo una tabla de proyectos cuya clave es un código. En ella tenemos un
campo TOTAL que recoge la suma de los importes de unas facturas que están en
otra tabla.Yo lo estoy resolviendo usando triggers tanto para eliminación
como para inserción y actualización, pero dichos desencadenadores acaban
usando cursores que recorren las tablas deleted o inserted. Dado que por
cuestiones de rendimiento no se aconseja el uso de cursores, ¿cómo podría
resolver esta situación de otra forma?

Se me ocurre que podría no usar ese campo y obtener el TOTAL en base a una
consulta, pero tal cual lo tengo ahora, me obligaría a cambiar bastantes
partes de mi aplicación, con lo cual quiero evitarlo.

Uso SQL Server 2005 Express

Gracias



Veo que Jesús ya te ha ayudado y solo quiero añadir un par de
consideraciones en general, al hilo de otros "encuentos" :)

Se podría haber hecho en un view que tiene todas las columnas de la
table base y el campo calculado. Se renombra la tabla base y se le
da al view el nombre de la tabla base. Entonces las aplicaciones no
se hubieran enterado de nada. Eso es independencia lógica de
datos, recuerdas? Pero para que las aplicaciones de verdad no se
enteren el view tiene que ser updateble, naturalmente, lo cual se
podría conseguir p.e. con instead of triggers.

Otra consideración es tratar de estimar si la vista se va a utilizar
mucho o poco para consultar en relacion a los insert/update/delete.
Si se utiliza poco es mejor hacer el calculo en el view y no cargar
las updates con triggers. Y si es al revés, hacerlo como ya lo tienes,
controlando la redundancia que introduces en los triggers.
Los dos métodos tendrían que ser intercambiables (p.e. para
favorecer la eficiencia de unas aplicaciones sobre otras) sin que
las aplicaciones se enteren (aparte del cambio en eficiencia, claro).

En cuanto al trigger que te ha dado Jesús, quizás para entender
mejor lo que pasa, si lo necesitas, puedes verlo asi:

1. En el trigger del insert:

update facturas
set total = total + sum(i.importe)
from facturas f inner join inserted i on f.CodProyecto i.CodProyecto

2. En el trigger del delete:

update facturas
set total = total - sum(d.importe)
from facturas f inner join deleted d on f.CodProyecto d.CodProyecto

3. Y en el trigger del update: 1. y 2.

Este es un buen ejemplo también de como pensar en términos de
'set at a time' (te acuerdas?) en vez de meterse en cursores.

Espero sea de alguna ayuda este comentario, porque tu problema
ya estaba solucionado.

Saludos,
Carlos
Respuesta Responder a este mensaje
#5 principiante
27/06/2007 - 18:25 | Informe spam
Lo mejor es una vista indexada para esas cosas.
Para eso las inventaron.


Jose TH


"Juan Diego Bueno" wrote in message
news:
Buenas:

Quería preguntaros como resolveis una situación concreta.
Tengo una tabla de proyectos cuya clave es un código. En ella tenemos un
campo TOTAL que recoge la suma de los importes de unas facturas que están
en otra tabla.Yo lo estoy resolviendo usando triggers tanto para
eliminación como para inserción y actualización, pero dichos
desencadenadores acaban usando cursores que recorren las tablas deleted o
inserted. Dado que por cuestiones de rendimiento no se aconseja el uso de
cursores, ¿cómo podría resolver esta situación de otra forma?

Se me ocurre que podría no usar ese campo y obtener el TOTAL en base a una
consulta, pero tal cual lo tengo ahora, me obligaría a cambiar bastantes
partes de mi aplicación, con lo cual quiero evitarlo.

Uso SQL Server 2005 Express

Gracias



Respuesta Responder a este mensaje
Ads by Google
Help Hacer una preguntaSiguiente Respuesta Tengo una respuesta
Search Busqueda sugerida