如何在从子表中删除一条记录时在SQL中创建删除触发器更新父表中的记录数?

时间:2012-06-28 10:32:05

标签: sql sql-server sql-server-2008

我创建了两个表。首先是magzine

CREATE TABLE [dbo].[Magzine](
[ID] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](350) NULL,
[MagzineType] [int] NOT NULL,
[TotalPages] [int] NOT NULL,
[DateCreated] [datetime] NOT NULL,
[Active] [bit] NOT NULL, CONSTRAINT [PK_tblMagzine] PRIMARY KEY CLUSTERED (
[ID] 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

,其列名为TotalPages,用于存放杂志中的总页数

这是MagzinePage表。

CREATE TABLE [dbo].[MagzinePage](
[ID] [int] IDENTITY(1,1) NOT NULL,
[MagzineID] [int] NOT NULL,
[Title] [nvarchar](255) NULL,
[SubTitle] [nvarchar](255) NULL,
[Photo] [nvarchar](255) NULL,
[Description] [nvarchar](2000) NULL,
[DateCreated] [datetime] NOT NULL,
[CreatedBy] [nvarchar](25) NOT NULL,
[Active] [bit] NOT NULL,
[IsMapped] [bit] NULL, CONSTRAINT [PK_tblPage] PRIMARY KEY CLUSTERED (
[ID] 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

我在magzinePage表中插入数据时创建了一个触发器,它可以处理根据ID插入表中的记录总数  这是触发器

Alter TRIGGER trgAfterInsert ON [dbo].[MagzinePage] 
    FOR INSERT

    AS
    Declare @MagzineID int;
    Declare @TotalPages int;
    Set @MagzineID= (Select Top 1 MagzineID From MagzinePage Order By ID Desc)
    Set @TotalPages =  (Select Count(MagzineID) from MagzinePage Where MagzineID=@MagzineID Group By MagzinePage.MagzineID) 

        Update Magzine Set TotalPages=@TotalPages Where ID= @MagzineID

        PRINT 'AFTER INSERT trigger fired.'
    GO  

现在我想创建一个触发器,只要删除magzinePage表中的记录,就可以帮助减少Magzine表中的总页数。

请帮我找个出路。 谢谢。

2 个答案:

答案 0 :(得分:2)

您可以创建一个索引视图,让您始终保持最新的计数:

create view dbo.vwMagzinePageCount 
with schemabinding
as
select [MagzineID], count_big(*) as TotalPages
from dbo.[MagzinePage]
group by [MagzineID];
go

create unique clustered index cdxMagzinePageCount 
    on dbo.vwMagzinePageCount (MagzineID);
go

我保留了你的拼写'Magzine'。从许多观点来看,这比触发器要好得多(正确性,性能等)。引擎将在任何操作中保持此计数的最新状态。从Magzine表中删除TotalPages列。

答案 1 :(得分:2)

如果您需要存储计数,无论出于何种原因,您是否考虑让SQL Server为您完成工作?如果从第一个表中删除TotalPages,请定义此视图:

CREATE VIEW dbo.MagzineWithPages
WITH SCHEMABINDING
AS
    select m.ID,Name,MagzineType,COUNT_BIG(*) as TotalPages,m.DateCreated,m.Active
    from
        dbo.Magzine m
            inner join
        dbo.MagzinePage mp
            on
                m.ID = mp.MagzineID
    group by
        m.ID,Name,MagzineType,m.DateCreated,m.Active
GO
CREATE UNIQUE CLUSTERED INDEX IX_MagzineWithPages on MagzineWithPages (ID)

然后,当添加和删除页面行时,COUNT()将自动更新。这也有一个好处,您可能希望更新此定义,例如忽略Active为0的页面。


为什么你的触发器 坏了 - 运行这样的INSERT:

INSERT INTO [dbo].[MagzinePage]([MagzineID],[DateCreated],[CreatedBy],[Active]) VALUES
(1,CURRENT_TIMESTAMP,'Me',1),
(2,CURRENT_TIMESTAMP,'Me also',1)

该表中的TOP 1 MagzineID现在为12。您将为其中一个运行更新,而错过另一个。


如果你真的想把它保留在原始表中(我强烈建议反对它,如果你坚持的话):

CREATE TRIGGER T_MagzinePage on dbo.MagzinePage
AFTER INSERT,UPDATE,DELETE
AS

SET NOCOUNT ON

;with Deltas as (
    select MagzineID,COUNT(*) as Cnt,0 as Del from inserted group by MagzineID
    union all
    select MagzineID,COUNT(*),1 from deleted group by MagzineID
), Merged as (
    select MagzineID,SUM(CASE WHEN Del=0 then Cnt ELSE -Cnt END) as Net
)
update m set TotalPages = TotalPages + Net
from Magzine m
inner join
Merged mm
on
    m.MagzineID = mm.MagzineID

应该将其作为单个触发器(废弃现有的触发器),并正确处理插入中的多行,更新MagzineID以及其他一系列问题。