如何将存储过程的结果返回到用于插入记录的选择语句

Posted

技术标签:

【中文标题】如何将存储过程的结果返回到用于插入记录的选择语句【英文标题】: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 语句?

【问题讨论】:

【参考方案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;
    

    至少在示例代码的存储过程中列出的第一个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 :)

以上是关于如何将存储过程的结果返回到用于插入记录的选择语句的主要内容,如果未能解决你的问题,请参考以下文章

Oracle存储过程创建临时表,插入数据后,怎么返回临时表数据,并在asp.net中接收到datatable中

存储过程返回2个结果数据集 - 有没有办法只将第一个数据集插入变量但仍然显示第二个?

MySQL 过程将空结果返回给 PHP

如何写sqlserver2000存储过程?用于批量录入数据,求解

sql server返回插入记录的ID(多条记录)

MYSQL的存储过程如何返回查询到的行数据?