【问题标题】:SQL Server 2005 Update Triggers not working for dependent columnsSQL Server 2005 更新触发器不适用于依赖列
【发布时间】:2014-07-05 20:29:51
【问题描述】:

我遇到了一个奇怪的情况,表上的更新触发器没有更新依赖于其他列的列,这些列也在更新期间得到更新。这是复制此问题的背景和代码。

我有一个商品管理应用程序,可以每天跟踪水果价格。我需要每天计算水果的价格和数量趋势。每日水果价格和价格量计算存储在 FruitTrades 表中。我在这个表上定义了一个更新触发器,每当在这个表中插入或更新一行时,它将计算价格和交易量趋势。

每日水果价格和数量以一个平面文件的形式提供给我,我将其导入一个名为 PriceData 的简单表中。然后我使用一个简单的 INSERT 语句将价格和交易量信息从该表移至 FruitTrades 表。这会触发 FruitTrades 中的更新触发器,但触发器不会更新其中的两列。知道为什么吗?

复制此问题的步骤如下:

-- 步骤 1(创建 FruitTrades 表)

CREATE TABLE [dbo].[FruitTrades](
    [FID] [nchar](3) NOT NULL,
    [TradeDate] [smalldatetime] NOT NULL,
    [TAID] [tinyint] NULL,
    [Price] [real] NOT NULL,
    [Vol] [int] NULL,
    [3DAvgPrice] [real] NULL,
    [5DAvgPrice] [real] NULL,
    [VolTrend] [real] NULL,
    [VolTrendPrevD] [real] NULL,
    CONSTRAINT [PK_FruitTrades] PRIMARY KEY CLUSTERED
(
[FID] ASC,
[TradeDate] DESC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]

) ON [PRIMARY];

-- 步骤 2(创建更新触发器)

CREATE TRIGGER [dbo].[TRG_FruitTrades_Analysis]
ON [dbo].[FruitTrades]
AFTER INSERT, UPDATE
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;

UPDATE FruitTrades SET
-- Calculate the 3 day average price
[FruitTrades].[3DAvgPrice] =
(
SELECT AVG(Price) FROM
(
SELECT TOP 3 Price FROM FruitTrades
WHERE FID = [Inserted].[FID] AND TradeDate <= [Inserted].[TradeDate]
) AS Last3Trades
),
-- Calculate the 5 day average price
[FruitTrades].[5DAvgPrice] =
(
SELECT AVG(Price) FROM
(
SELECT TOP 5 Price FROM FruitTrades
WHERE FID = [Inserted].[FID] AND TradeDate <= [Inserted].[TradeDate]
) AS Last5Trades
),
-- Fetch the previous days VolTrend and update VolTrendPrev column
[FruitTrades].[VolTrendPrevD] =
(
SELECT TOP 1 VolTrend FROM FruitTrades
WHERE FID = [Inserted].[FID] AND TradeDate < [Inserted].[TradeDate]
),
-- Calculate Volume Trend and update VolTrend column
[FruitTrades].[VolTrend] =
(
ISNULL([FruitTrades].[VolTrendPrevD], 0) +
([Inserted].[Vol] * (([Inserted].[Price] /
(SELECT TOP 1 Price FROM FruitTrades WHERE FID = [Inserted].[FID] AND TradeDate < [Inserted].[TradeDate])) - 1.0 ))
),
-- Now Update the Action ID column
[FruitTrades].[TAID] =
(
CASE
WHEN [FruitTrades].[3DAvgPrice] >= [FruitTrades].[5DAvgPrice] AND [FruitTrades].[VolTrend] >= [FruitTrades].[VolTrendPrevD] THEN 1
WHEN [FruitTrades].[3DAvgPrice] >= [FruitTrades].[5DAvgPrice] AND [FruitTrades].[VolTrend] <= [FruitTrades].[VolTrendPrevD] THEN 2
WHEN [FruitTrades].[3DAvgPrice] <= [FruitTrades].[5DAvgPrice] AND [FruitTrades].[VolTrend] >= [FruitTrades].[VolTrendPrevD] THEN 3
WHEN [FruitTrades].[3DAvgPrice] <= [FruitTrades].[5DAvgPrice] AND [FruitTrades].[VolTrend] <= [FruitTrades].[VolTrendPrevD] THEN 4
ELSE NULL
END
)
FROM FruitTrades
INNER JOIN Inserted ON Inserted.FID = FruitTrades.FID AND Inserted.TradeDate = FruitTrades.TradeDate
END

-- 步骤 3(创建 PriceData 表)

CREATE TABLE [dbo].[PriceData](
[FID] [nchar](3) NOT NULL,
[TradeDate] [smalldatetime] NOT NULL,
[Price] [real] NULL,
[Vol] [real] NULL,
CONSTRAINT [PK_PriceData] PRIMARY KEY CLUSTERED
(
[FID] ASC,
[TradeDate] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

-- STEP 4(模拟数据导入 PriceData 表)

INSERT INTO PriceData (FID, TradeDate, Price, Vol) VALUES ('APL', '4/30/2012', 200, 1000);
INSERT INTO PriceData (FID, TradeDate, Price, Vol) VALUES ('APL', '4/29/2012', 190, 1200);
INSERT INTO PriceData (FID, TradeDate, Price, Vol) VALUES ('APL', '4/28/2012', 195, 1250);
INSERT INTO PriceData (FID, TradeDate, Price, Vol) VALUES ('APL', '4/27/2012', 205, 1950);
INSERT INTO PriceData (FID, TradeDate, Price, Vol) VALUES ('APL', '4/26/2012', 200, 2000);
INSERT INTO PriceData (FID, TradeDate, Price, Vol) VALUES ('APL', '4/25/2012', 180, 1300);
INSERT INTO PriceData (FID, TradeDate, Price, Vol) VALUES ('APL', '4/24/2012', 185, 1250);

-- STEP 5(将价格波动日期从 PriceDate 表移动到 Fruit Tables)

INSERT INTO FruitTrades (FID, TradeDate, Price, Vol) SELECT FID, TradeDate, Price, Vol FROM PriceData;

-- 第 6 步(检查 FruitTrades 表的正确性)

SELECT * FROM FruitTrades ORDER BY TradeDate

--- 结果

在第 6 步之后,您会发现 FruitTrades 表列中的 TAID 和 VolTrendPrevD 仍然为 NULL。

非常感谢任何有关如何解决此问题的帮助。

【问题讨论】:

  • 为什么要使用触发器?更糟糕的仍然是按记录计算聚合的触发器。鉴于我每天都有一份文件,我刚刚写了一个处理它的过程。至少那时我可以分解它并测试每个部分,看看它是否符合我的预期。
  • 可能。我确实想到了一个开始的过程。但是触发器方法似乎更简单,并且需要的代码也更少。我只是想了解为什么它不起作用?例如,是否涉及种族问题?

标签: sql-server-2005 triggers calculated-columns


【解决方案1】:

在这个问题上辛苦了 5 天,并在谷歌上搜索了一番,我终于自己找到了解决方案。

第一个问题是由于我对触发器及其触发方式缺乏了解。根据 SQL 文档,

SQL Server 触发器每个语句只触发一次,而不是每个语句触发一次 受影响的行

由于这个设计原则,当在第 5 步中,我从 PriceData 表批量插入到 FruitTrades 表中时,触发器只被触发一次,而不是每行一次。因此更新的值不正确。

VolTrendPrevD 保持为空,因为在 Update 触发器中对其的 Select 语句始终与 FruitTrades 表中的第一行匹配(因为 Inserted 表有多个行)并且对于该行,VolTrend 为空。

TAID 保持为空,因为 VolTrendPrevD 为空。

现在修复:

  1. 将包含价格数据的文本文件导入到 MSaccess 表中。从那里批量插入到 SQL Server 表中(使用链接表)。这种方法使用 ODBC 将批量插入转换为多个单次插入,从而绕过了第一个问题。

  2. 将 VolTrend 转换为计算列。因此无需在触发器中更新它。

  3. 在 FruitTrades 表中引入一个附加列 PricePrevD 并在触发器中更新其值,方式与 VolTrendPrevD 列相同。

  4. 最重要的是,确保从 Access 中插入的行按日期升序插入(通过在 Access 中创建适当的日期索引)。否则将丢失所需的结果。

希望这会有所帮助... :-)

【讨论】:

  • 总是编写触发器来处理多行
猜你喜欢
  • 2014-10-09
  • 1970-01-01
  • 1970-01-01
  • 2023-04-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多