【问题标题】:How to write an INSTEAD OF INSERT trigger on a multi table view that works with identities?如何在使用身份的多表视图上编写 INSTEAD OF INSERT 触发器?
【发布时间】:2014-06-08 12:25:16
【问题描述】:

我有两个具有 1:1 关系的表,并使用 IDENTITY 列作为它们的主键。

视图将两个表连接在一起,给人的印象是所有列都真正存储在一个表中。

出于查看目的,这很好,但视图也需要INSTEAD OF INSERT 触发器。

您如何编写这样的触发器来拆分两个表中的列值?

注意:父表中除了标识列之外没有候选键。否则这个问题可能会有所帮助:

INSERT INTO View, INSTEAD OF Trigger, Identity, multiple tables?

(还有一种可能的解决方案是在该问题的答案中为每个字段声明一个变量。)

简化的架构(我省略了 PKEY、FKEY、COLLATIONS 等):

CREATE TABLE
  [dbo].[parent]
(
    [parent_id] INT IDENTITY(1,1)
  , [name]      NVARCHAR(100)
)

CREATE TABLE
  [dbo].[child]
(
    [parent_id] INT NOT NULL
  , [child_id]  INT IDENTITY(1,1)
  , [name]      NVARCHAR(100)
)

GO

CREATE VIEW
  [dbo].[parent_child]
AS
SELECT
    par.[parent_id]
  , par.[name]      AS "parent_name"
  , chi.[child_id]
  , chi.[name]      AS "child_name"
FROM
  [dbo].[parent] par
LEFT OUTER JOIN
  [dbo].[child] chi
ON
  chi.[parent_id] = par.[parent_id]

GO

触发器模板:

CREATE TRIGGER
  [dbo].[parent_child_instead_of_insert]
ON
  [dbo].[parent_child]
INSTEAD OF INSERT
AS
BEGIN
  -- Implementation here
END
GO

示例数据:

INSERT INTO 
  [dbo].[parent_child]
(
    [parent_name]
  , [child_name]
)
SELECT 
    'parent1'
  , 'child1' 
UNION 
SELECT 
    'parent2'
  , 'child2'

【问题讨论】:

    标签: sql sql-server view triggers identity-column


    【解决方案1】:

    Future-proofing an INSTEAD OF INSERT trigger 问题的启发,我想出了一个使用临时表#inserted 的解决方案,它是inserted 表的完整副本。

    触发器在该表上添加一个临时标识列,以便可以使用唯一值迭代插入的行。

    剩下的就是一个简单的循环,使用游标先将每一行插入父表,然后使用SCOPE_IDENTITY() 插入子行。

    与“为每一列声明一个 var”解决方案相比,此解决方案的优势在于您必须仅获取临时标识值而不是所有列。

    性能方面可能不是很好,因为必须复制插入表中的所有数据。

    SELECT * INTO [dbo].[#inserted] FROM [inserted]
    ALTER TABLE [dbo].[#inserted] ADD [temp_id] INT IDENTITY(1,1)
    
    DECLARE
      @temp_id int
    
    DECLARE
      cur CURSOR
    FOR
    SELECT
      [temp_id]
    FROM 
      [dbo].[#inserted]
    
    OPEN cur
    FETCH cur INTO @temp_id
    WHILE @@FETCH_STATUS = 0
    BEGIN
    
    INSERT INTO 
      [dbo].[parent]
    (
      [name]
    )
    SELECT
      [parent_name]
    FROM
      [#inserted]
    WHERE
      [temp_id] = @temp_id
    
    INSERT INTO
      child
    (
        [parent_id]
      , [name]
    )
    SELECT
        SCOPE_IDENTITY()
      , [child_name]
    FROM
      [dbo].[#inserted]
    WHERE
      [temp_id] = @temp_id
    
    FETCH cur INTO @temp_id
    END
    
    CLOSE cur
    DEALLOCATE cur
    

    【讨论】:

      【解决方案2】:

      您可以通过为每一列声明一个变量来遍历inserted 表。

      DECLARE
        @parent_name NVARCHAR(100)
      
      DECLARE
        @child_name NVARCHAR(100)
      
      DECLARE
        cur CURSOR
      FOR
      SELECT
          [parent_name]
        , [child_name]
      FROM 
        [inserted]
      
      OPEN cur
      FETCH cur INTO @parent_name, @child_name
      WHILE @@FETCH_STATUS = 0
      BEGIN
      
      INSERT INTO 
        [dbo].[parent]
      (
        [name]
      )
      SELECT
        @parent_name
      
      INSERT INTO
        child
      (
          [parent_id]
        , [name]
      )
      SELECT
          SCOPE_IDENTITY()
        , @child_name
      
      FETCH cur INTO @parent_name, @child_name
      END
      
      CLOSE cur
      DEALLOCATE cur
      

      我从这个答案中得到了这个想法:https://stackoverflow.com/a/15162860/426242

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-11-26
        • 2011-01-13
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多