【问题标题】:SQL Server--Setting column value during insert using triggerSQL Server - 在使用触发器插入期间设置列值
【发布时间】:2016-04-15 15:12:31
【问题描述】:

我有一个包含以下列的表格:

id、xml、receivedDate、handledDateTime、orderNumber

id、receivedDate 和 processed 都有默认值。

存储在 xml 列中的 XML 如下所示:

<v1:OrderStatus xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:v1="http://xmlns.dell.com/Services/DMT/OrderStatus/V1">
  <v1:BusinessUnitId>11</v1:BusinessUnitId>
  <v1:OrderNumber>964806604</v1:OrderNumber>
  <v1:OrderStatusCode>RDD</v1:OrderStatusCode>
  <v1:StatusDateTime>2016-04-12T01:16:28</v1:StatusDateTime>
  <v1:RDD_Date>2016-04-19T00:00:00</v1:RDD_Date>
</v1:OrderStatus>

当 XML 被插入到表中时,我需要获取 OrderNumber 的值并将其设置在 orderNumber 列中。我开发了一个触发器来尝试完成此操作,但不是仅在新插入的行中设置值,而是为表中的所有行设置值。

我的触发器看起来像:

  declare @ordernumber varchar(50)  
  select @ordernumber=(select xml.value('(//*:OrderNumber)[1]', 'varchar(max)') as orderNumber from inserted)

    Update [dbo].[dell.des.OCIMessages] 
    set orderNumber=@ordernumber
      END

我在另一个表上有另一个触发器,它连接这两个表之间的 orderNumber 值,我在插入/更新后使用这个另一个触发器来查找具有匹配 orderNumber 和处理 = 0 的行,然后设置处理 = 1并设置 processesDateTime 字段。

不幸的是,由于我的第一个触发器更新所有行以使用相同的订单号值,我的第二个触发器无意中总是更新所有行,因为订单号值对于我的表中的每一行都是相同的。

有人可以解释一下我的触发器出了什么问题,以便它只更新插入的行吗?

顺便说一句,我一次只插入一行,所以我不必担心我的触发器必须处理多条记录。

谢谢!

【问题讨论】:

  • 我强烈反对您只能指望插入单行。从应用程序来看这可能是正确的,但是什么数据维护和诸如此类的事情呢?你应该让你的触发器集基于。至于手头的实际问题,您有两个问题。首先,您的更新语句没有 where 子句,因此它将更新整个表。其次,如果您尝试“拦截”并更改插入的值,则应使用触发器而不是触发器并在触发器中创建插入。
  • 这是一个很好的起点。 spaghettidba.com/2015/04/24/…
  • 最终将我的触发器更改为:声明@ordernumber varchar(50) select @ordernumber=(select xml.value('(//*:OrderNumber)[1]', 'varchar(max )') as orderNumber from inserted) Update [dbo].[dell.des.OCIMessages] set orderNumber=@ordernumber from [dell.des.OCIMessages] 内部连接插入 [dell.des.OCIMessages].id=inserted.id END 这似乎有效。我喜欢介绍的其他一些方法。所以我明白了,我目前的实现有什么问题?
  • 好吧,如果它对你有用,那就没有错。但是:每当您尝试一次插入多个时,这都会中断。迟早会发生这种情况...如果您的 XML 更改,计算列(可以定义为 persistent 以避免重复计算)甚至会做出反应。 Instead Of Trigger 让您完全控制插入过程。这两种方法都更好、更安全。
  • 还有一个提示,更多的是我自己关心的 :-) Please read this: someone-answers.

标签: sql sql-server xml triggers xquery


【解决方案1】:

您可以在更新中使用 where 条件:

declare @ordernumber varchar(50), @id int 
select @ordernumber= xml.value('(//*:OrderNumber)[1]', 'varchar(max)') as orderNumber, @id = id from inserted

Update [dbo].[dell.des.OCIMessages] 
set orderNumber=@ordernumber 
where id = @id
  END

或者你可以只使用基于集合的选项:

Update oci Set oci.orderNumber = inserted.xml.value('(//*:OrderNumber)[1]', 'varchar(max)')
from [dbo].[dell.des.OCIMessages] oci join inserted on oci.id = inserted.id

【讨论】:

  • 这不起作用,因为 OP 声明这是一个插入触发器。因此,该行在基表中尚不存在。
  • 在插入之后,所以该行确实存在。这不是中断。
【解决方案2】:

我提出第二个答案,因为这是一种完全不同的方法:

创建普通表

CREATE TABLE dbo.Test(id INT, xml XML, OrderNumber VARCHAR(100));

创建一个“INSTEAD OF”触发器

CREATE TRIGGER dbo.InsertToTest ON  dbo.Test
   INSTEAD OF INSERT
AS 
BEGIN
    INSERT INTO dbo.Test(id,xml,OrderNumber)
    SELECT id,xml,xml.value('(//*:OrderNumber)[1]', 'varchar(max)') 
    FROM inserted
END
GO

这就是它的工作原理

declare @xml1 xml=
'<v1:OrderStatus xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:v1="http://xmlns.dell.com/Services/DMT/OrderStatus/V1">
  <v1:OrderNumber>11111</v1:OrderNumber>
</v1:OrderStatus>';

declare @xml2 xml=
'<v1:OrderStatus xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:v1="http://xmlns.dell.com/Services/DMT/OrderStatus/V1">
  <v1:OrderNumber>22222</v1:OrderNumber>
</v1:OrderStatus>';

declare @xml3 xml=
'<v1:OrderStatus xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:v1="http://xmlns.dell.com/Services/DMT/OrderStatus/V1">
  <v1:OrderNumber>33333</v1:OrderNumber>
</v1:OrderStatus>';    

insert into dbo.Test VALUES(1,@xml1,'SomeValue'),(2,@xml2,'AnotherValue');

select * from dbo.Test;

insert into dbo.Test select 3,@xml3,null;

select * from dbo.Test;

【讨论】:

    【解决方案3】:

    这个怎么样?

    您需要一个未定义的函数来检索您的值

    CREATE FUNCTION dbo.GetOrderNumber(@xml xml)
    RETURNS VARCHAR(MAX)
    AS
    BEGIN
        RETURN @xml.value('(//*:OrderNumber)[1]', 'varchar(max)');
    END
    

    使用计算列定义您的表

    CREATE TABLE dbo.Test(id INT, xml XML, OrderNumber AS dbo.GetOrderNumber(xml));
    

    这就是它的工作原理

    declare @xml1 xml=
    '<v1:OrderStatus xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:v1="http://xmlns.dell.com/Services/DMT/OrderStatus/V1">
      <v1:OrderNumber>11111</v1:OrderNumber>
    </v1:OrderStatus>';
    
    declare @xml2 xml=
    '<v1:OrderStatus xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:v1="http://xmlns.dell.com/Services/DMT/OrderStatus/V1">
      <v1:OrderNumber>22222</v1:OrderNumber>
    </v1:OrderStatus>';
    
    declare @xml3 xml=
    '<v1:OrderStatus xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:v1="http://xmlns.dell.com/Services/DMT/OrderStatus/V1">
      <v1:OrderNumber>33333</v1:OrderNumber>
    </v1:OrderStatus>';    
    
    insert into dbo.Test VALUES(1,@xml1),(2,@xml2);
    
    select * from dbo.Test;
    
    insert into dbo.Test select 3,@xml3;
    
    select * from dbo.Test;
    

    【讨论】:

      猜你喜欢
      • 2014-10-09
      • 1970-01-01
      • 2013-11-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多