触发根据子表的最大值更新父表

时间:2019-03-24 03:33:50

标签: tsql

这是我第一次涉足SSMS / T-SQL(来自Access)。我有一个触发器设置,根据它们之间的键,它使父表中的列的值始终等于子表中的列的MAX值。为了计算最大值,我定义了一个UDF,我认为它可以正常工作。

我似乎遇到的问题是,触发器针对表中的每个键执行,而不只是针对已更新/删除/插入的键执行(或者我可以从调试器中收集到的信息)。

这是父表:

CREATE TABLE [dbo].[factMeasures](
    [MeasureID] [int] IDENTITY(1,1) NOT NULL,
    [QARTOD] [int] NULL,
    [Param] [char](10) NOT NULL,
    [Value] [real] NOT NULL,
 CONSTRAINT [PK_factMeasures] PRIMARY KEY CLUSTERED 
(
    [MeasureID] 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

这是子表:

CREATE TABLE [dbo].[dt_QCflags](
    [QC_ID] [int] IDENTITY(1,1) NOT NULL,
    [fkMeasureID] [int] NOT NULL,
    [RuleValue] [int] NOT NULL,
 CONSTRAINT [PK_dt_QCflags] PRIMARY KEY CLUSTERED 
(
    [QC_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

ALTER TABLE [dbo].[dt_QCflags]  WITH CHECK ADD  CONSTRAINT [FK_dt_QCflags_factMeasures] FOREIGN KEY([fkMeasureID])
REFERENCES [dbo].[factMeasures] ([MeasureID])
ON DELETE CASCADE
GO

ALTER TABLE [dbo].[dt_QCflags] CHECK CONSTRAINT [FK_dt_QCflags_factMeasures]
GO

这里是UDF,用于计算输入[MeasureID]的[RuleValue]的最大值

CREATE FUNCTION [dbo].[MaxQC](@MeasureID INT)
RETURNS INT
AS
BEGIN

RETURN
    (SELECT
      Max([dt_QCflags].[RuleValue]) AS Max_RuleValue
    FROM
      dbo.dt_QCflags
    WHERE
      dt_QCflags.fkMeasureID = @MeasureID
    GROUP BY
      fkMeasureID);

END

这是子表上的触发器:

ALTER TRIGGER [dbo].[UpdateQARTOD]
    ON [dbo].[dt_QCflags]
    AFTER INSERT, UPDATE, DELETE
AS
BEGIN   
    UPDATE factMeasures
    SET QARTOD = dbo.MaxQC(MeasureID)  -- by QARTOD Definition, QARTOD flag is set to the MAX of all sub-test results

END

所以我想要的是父级(factMeasures.QARTOD)中的列始终包含子表中的列(dt_QCF​​lags.RuleValue)的最大值,给定MeasureID值 < / p>

当我调试它时,它似乎正在为父表中的每条记录运行触发器,因此我认为我需要修改触发器,但是我不确定如何获取仅该记录的MeasureID。添加/删除/修改。

我猜这与插入,删除等“魔术表”有关,但是我似乎语法不正确。

谢谢!

1 个答案:

答案 0 :(得分:1)

我认为除非您有充分的理由,否则存储可以在查询级别轻松计算的值是错误的。

这似乎是我所见过的许多情况之一,人们认为他们通过将一个值存储在一个表中而从另一个表的值计算出了一些东西,但事实恰恰相反-现在您拥有两点数据需要一直保持同步,并且由于同步它们的过程是一个触发器,因此您实际上并没有控制权-例如,禁用/启用触发器非常容易。

因此,我对您的建议是一起删除所有触发器,并在需要时简单地计算值。

请注意,由于SQL Server支持max() over(partition by),这意味着如果要计算列的最大值,甚至不需要group by

已更新。在您对答案进行评论后,您似乎有充分的理由来存储这些值。

说了这么多,这是您所提问题的直接答案。
在SQL Server触发器中,数据库使您可以查询两个名为inserted and deleted的特殊表。这些表包含在声明触发器的表中插入(或删除)的数据(如果使用instead of触发器,则这些数据将被删除)。

请注意,在SQL Server中,触发器是按语句而不是每行触发的。这意味着inserteddeleted表可能包含0、1或许多行。

如果您仍然想使用触发器来计算值,我建议您使用一个插入/更新触发器和另一个用于删除触发器。

这将使代码更简单。

在删除触发器中,您没有加入deleted表:

UPDATE T
SET QARTOD = MaxValue
FROM factMeasures As T
JOIN 
(
    SELECT d.fkMeasureID, Max(t.RuleValue) As MaxValue
    FROM Deleted AS d
    LEFT JOIN dt_QCflags As t
        ON d.QC_ID = t.QC_ID
    GROUP BY d.fkMeasureID
) as D
    ON T.MeasureID = D.fkMeasureID

在插入/更新触发器中,您编写了非常相似的代码-但在这种情况下,您无需引用物理表,只需引用inserted表:

UPDATE T
SET QARTOD = MaxValue
FROM factMeasures As T
JOIN 
(
    SELECT fkMeasureID, Max(RuleValue) As MaxValue
    FROM Inserted 
    GROUP BY fkMeasureID
) as I
    ON t.MeasureID = I.fkMeasureID