0

我正在尝试使用来自另一个表的触发器更新一个表。我认为这将是一个非常简单的查询,但我第一次提出的查询不起作用,我不明白为什么。

CREATE TABLE [dbo].[Vehicle](
    [id] [int] IDENTITY(1,1) NOT NULL,
    [plate] [nvarchar](50) NOT NULL,
    [name] [nvarchar](50) NOT NULL,
    [dateUsed] [datetime] NULL
)

CREATE TABLE [dbo].[Transaction](
    [id] [int] IDENTITY(1,1) NOT NULL,
    [vehicleId] [int] NOT NULL,
    [quantity] [float] NOT NULL,
    [dateTransaction] [datetime] NOT NULL,
)

添加事务时,我希望更新 Vehicle 表。如果添加dateTransaction的时间较晚dateUsed,则应对其进行更新,以便该dateUsed字段始终包含该特定车辆的最新日期。

我认为这个触发器应该可以解决问题..但它没有:

UPDATE [Vehicle] 
SET [dateUsed] = 
    CASE 
        WHEN [dateUsed] < [Transaction].[dateTransaction] 
            OR [dateUsed] IS NULL
            THEN [Transaction].[dateTransaction] 
        ELSE [dateUsed] 
    END
FROM [Transaction]
WHERE [Vehicle].[id]=[Transaction].[vehicleId]

对我来说看起来不错......它应该检查所有新插入的记录并更新该dateUsed字段。如果dateTransaction是较新的,请使用那个..如果不是..使用当前的。但我似乎遗漏了一些东西,因为它没有更新到最新日期。它确实与该特定车辆的一项交易相匹配,但与最新的交易不匹配。

一个有效的查询:

UPDATE [Vehicle] 
SET [dateUsed] = InsertedPartitioned.[dateTransaction]
FROM [Vehicle]
LEFT JOIN (
    SELECT 
        [vehicleId],
        [dateTransaction],
        ROW_NUMBER() OVER(PARTITION BY [VehicleId] ORDER BY [dateTransaction] DESC) AS RC
    FROM [Inserted]) AS InsertedPartitioned 
    ON InsertedPartitioned.RC=1 
    AND InsertedPartitioned.[vehicleId]=[Vehicle].[id]
WHERE InsertedPartitioned.[vehicleId] IS NOT NULL
    AND ([Vehicle].[dateUsed] IS NULL 
        OR InsertedPartitioned.[dateTransaction] > [Vehicle].[dateUsed]);

所以我有一个可行的解决方案,它甚至可能会更好(没有用大插入来计时)但是它让我不知道为什么第一个它不起作用!

任何人都可以“启发我”吗?

4

1 回答 1

1

为什么第一个它不起作用

由于 Microsoft 扩展的一个奇妙方面,UPDATE它使用了一个FROM子句:

FROM指定子句以提供更新操作的条件时要小心。如果语句包含的子句未以这样的方式指定,即更新的每个列出现只有一个值可用,则语句的结果UPDATE未定义的,也就是说,如果语句不是确定性的。FROMUPDATE

(我的重点)。

也就是说,如果多于一行 frominserted匹配同一行,Vehicle则未定义哪一行将用于应用更新 - 并且SET子句中的所有计算都“好像”它们都被并行计算 - 所以它不是好像第二次尝试更新同一行将观察第一次尝试的结果 -DateUsed可以观察到的列的当前值始终是原始值。


在 ANSI 标准 SQL 中,您必须在UPDATE不使用FROM扩展名的情况下编写,因此必须编写相关的子查询,例如:

UPDATE [Vehicle] 
SET [dateUsed] = COALESCE((SELECT dateUsed FROM inserted i
         WHERE i.VehicleId = Vehicle.Id and
         (i.dateUsed > Vehicle.DateUsed or
          Vehicle.DateUsed IS NULL),
     dateUsed)
WHERE [id] IN (select [vehicleId] FROM inserted)

在相同的情况下,它会很好地给您一个关于返回多个值的子查询的错误(对于 中的那个COALESCE,而不是 中的那个IN),从而为您提供为什么它不起作用的线索。

但是,不可否认的是,FROM扩展是有用的——我只是希望它对这种情况触发警告。

于 2015-09-02T08:21:43.037 回答