【问题标题】:Update DateUsed by trigger更新触发器使用的日期
【发布时间】:2015-11-27 15:34:08
【问题描述】:

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

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

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

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

【问题讨论】:

  • 您使用的是哪种 dbms 产品? (看起来不像 ANSI SQL……而且大多数产品的触发器都不符合 ANSI。)
  • 根据使用的语法重新标记为sql-server

标签: sql-server tsql sql-update database-trigger


【解决方案1】:

为什么第一个不起作用

因为微软对UPDATE 的扩展有一个很棒的方面,它使用了FROM 子句:

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

(我的强调)。

也就是说,如果inserted 中的多行与Vehicle 中的同一行匹配,则它是未定义的哪个 行将用于应用更新 - 以及@987654330 中的所有计算@ 子句的计算“好像”它们都是并行计算的 - 因此,第二次更新同一行的尝试不会在第一次尝试时观察到结果 - 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 扩展很有用——我只是希望它能针对这种情况触发警告。

【讨论】:

  • 因此,在我与当前值进行比较的更新语句中,它与原始 (!) 值进行比较,而不是自更新开始以来可能已经更新的值。我说的对吗?
  • @TieleDeclercq - 是的,没错。这意味着活动可以并行发生,并且意味着您可以做一些可爱的事情 - UPDATE tab SET colA = colB, colB = colA 交换两列的值,因为 = 右侧的值始终是原始值。跨度>
  • 好的.. 知道这一点实际上是有道理的。谢谢!
猜你喜欢
  • 2021-09-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-02-19
  • 1970-01-01
  • 2011-04-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多