【问题标题】:How do I return the result of a stored procedure to a select statement that is being used to insert records如何将存储过程的结果返回到用于插入记录的选择语句
【发布时间】:2016-09-27 13:05:14
【问题描述】:

我正在编写一个脚本来在软件更新后转换一些数据。我正在引入一个新表,需要将现有表中的数据提取到这个新表中。我需要从一个表中获取一个现有的 varchar(max) 字段,在另一个表中基于它创建一个父子记录,然后在另一个表中插入新父记录的标识。

所以它有点想看起来像这样

INSERT INTO NEWTABLE (val1, val2, val3) 
SELECT 
    val1,
    val2,
    EXEC AStoredProc(val3) -- this obviosuly doesnt work
FROM EXISTINGTABLE

CREATE PROCEDURE AStoredProc
  @Value nvarchar(max),
  @NewID int OUTPUT

  DECLARE @ParentID int

  INSERT INTO AnotherTable ([Text], ParentNodeID)
  VALUES ('',NULL)

  SELECT @ParentID = SCOPE_IDENTITY()

  INSERT INTO AnotherTable  ([Text], ParentNodeID)
  VALUES (@Value,@ParentID)

  SELECT @ParentID

END

在上面的示例中,val1 和 val2 字段只是直接迁移到新表中。新表中的 val3 是一个 int 并且是从 AStoredProc 的结果插入的,它采用现有表的 varchar(max) val3 值在另一个表中创建几个记录并从它们返回新的父 ID,然后在新表中作为 val3 插入。

不用担心设计的原因,我怎样才能在存储过程中运行一些插入内容并将结果直接返回到外部 Select 语句?

【问题讨论】:

    标签: sql sql-server-2008 tsql


    【解决方案1】:

    我能想到的两个选项是:

    1. 使用CURSOR

      DECLARE @val1 datatype1,
              @val2 datatype2,
              @val3 NVARCHAR(MAX),
              @ParentID INT;
      
      DECLARE migrate CURSOR LOCAL STATIC
      FOR  SELECT et.val1, et.val2, et.val3
           FROM   EXISTINGTABLE et
           WHERE  NOT EXISTS (
                         SELECT *
                         FROM   NEWTABLE nt
                         WHERE  nt.val1 = et.val1
                         AND    nt.val2 = et.val2
                             );
      
      BEGIN TRY
      
      OPEN migrate
      
      FETCH NEXT
      FROM  migrate
      INTO  @val1, @val2, @val3;
      
      WHILE (@@FETCH_STATUS <> 0)
      BEGIN
        BEGIN TRAN;
      
        EXEC dbo.AStoredProc
                 @Value = @val3,
                 @NewID = @ParentID OUTPUT;
      
        INSERT INTO NEWTABLE (val1, val2, val3)
        VALUES (@val1, @val2, @ParentID);
      
        COMMIT;
      END;
      
      CLOSE migrate;
      DEALLOCATE migrate;
      
      END TRY
      BEGIN CATCH
        ROLLBACK TRAN;
        -- catch block stuff goes here
      END CATCH;
      
    2. 至少在示例代码的存储过程中列出的第一个AnotherTable(即生成@ParentID 值的表)中创建一个“临时”列,以保存新创建记录的关联。这将允许进行基于集合的操作,因为查询可以作为单独的步骤完成。如果EXISTINGTABLE 中已经有一个 IDENTITY 值/易于使用的 PK,则只需使用它,否则如果临时列是 UNIQUEIDENTIFIER 并使用 NEWID(),则可以轻松创建一个新值。但假设目前是幸福的道路(即EXISTINGTABLEINT PK),那么它会是这样的:

      -- set up temporary association column
      ALTER TABLE dbo.AnotherTable
        ADD [TempAssociationID] INT NOT NULL;
      
      BEGIN TRY
      
      BEGIN TRAN;
      
      -- populate initial "parent" records
      INSERT INTO dbo.AnotherTable ([Text], [TempAssociationID])
        SELECT '', et.PK_column
        FROM   dbo.EXISTINGTABLE et;
      
      -- populate "child" records
      INSERT INTO dbo.AnotherTable ([Text], [TempAssociationID], ParentNodeID)
        SELECT et.val3, et.PK_column, anthr.ID_column AS [ParentNodeID]
        FROM   dbo.EXISTINGTABLE et
        INNER JOIN  dbo.AnotherTable anthr
                ON  anthr.[TempAssociationID] = et.PK_column
               AND  anthr.[ParentNodeID] IS NULL;
      
      -- combine existing and secondary tables for "new" table
      INSERT INTO dbo.NEWTABLE (val1, val2, val3)
        SELECT  et.val1, et.val2, anthr.ID_column AS [val3]
        FROM    dbo.EXISTINGTABLE et
        INNER JOIN  dbo.AnotherTable anthr
                ON  anthr.[TempAssociationID] = et.PK_column
               AND  anthr.[ParentNodeID] IS NULL;
      
      COMMIT TRAN;
      
      END TRY
      BEGIN CATCH
        ROLLBACK TRAN;
        -- other catch block stuffs
      END CATCH;
      
      ALTER TABLE dbo.AnotherTable
        DROP COLUMN [TempAssociationID];
      

    【讨论】:

    • 是的,我有点想他们会是我的选择。认为我将采用光标方法。有点希望它可以在一个容易回滚的事务中完成
    • @StewartAlan 可以...让我完成选项 2 ;-)。
    • @StewartAlan 请查看竞争选项#2 :)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-05
    • 1970-01-01
    • 1970-01-01
    • 2014-05-23
    • 2017-04-23
    相关资源
    最近更新 更多