SQL Server 2005更新触发器不适用于依赖列

时间:2014-05-17 12:27:29

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

我遇到一种奇怪的情况,即表上的更新触发器不会更新依赖于在更新期间也会更新的其他列的列。以下是复制此问题的背景和代码。

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

我在一个平面文件中找到每日水果价格和交易量,我将其导入一个名为PriceData的简单表格。然后,我使用简单的INSERT语句将Price和Volume信息从此表移动到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]

- 第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);

- 第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。

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

1 个答案:

答案 0 :(得分:0)

在解决这个问题5天后,以及一些谷歌搜索,我终于找到了解决方案。

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

  

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

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

VolTrendPrevD保持为null,因为Update触发器中的Select语句始终与FruitTrades表中的第一行匹配(因为Inserted表有多行),对于此行,VolTrend为null。

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

现在修复:

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

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

  3. 在FruitTrades表中引入一个额外的列PricePrevD,并以与VolTrendPrevD列相同的方式更新触发器中的值。

  4. 最重要的是,确保按日期按升序插入来自Access的插入行(通过在Access中创建适当的日期索引)。否则,将缺少预期的结果。

  5. 希望这有助于......: - )