从存储过程结果集中插入/更新表上的数据

aby*_*aby 5 sql loops stored-procedures sql-server-2008

我有一个名为proc_item的存储过程,它从不同的表中获取数据(使用join).我希望在另一个名为Item的表中插入存储过程的结果集(如果数据是新的)或更新(如果数据已存在).任何人都可以告诉我如何做到这一点?我是sql,存储过程和循环的新手.

谢谢

Sol*_*zky 8

您应该创建一个临时表来保存存储过程的结果,然后将结果合并到表中.建议使用临时表而不是表变量,因为它会更好地加入现有表,因为统计数据更好.

CREATE TABLE #TempResults
(
  Field1 DATATYPE1,
  Field2 DATATYPE2,
  ...,
  PRIMARY KEY CLUSTERED (KeyField1,...)
)

INSERT INTO #TempResults (Field1, Field2, ...)
   EXEC Schema.ProcName @Param1, ...
Run Code Online (Sandbox Code Playgroud)

现在,有两种方法可以进行合并.第一个适用于所有版本的SQL Server,第二个使用SQL Server 2008中引入的命令.

-- this should work on all SQL SERVER versions
UPDATE  rt
SET     rt.Field2 = tmp.Field2,
        ...
FROM    Schema.RealTable rt
INNER JOIN   #TempResults tmp
        ON   tmp.KeyField1 = rt.KeyField1
        ...

INSERT INTO Schema.RealTable (Field1, Field2, ...)
   SELECT  tmp.Field1, tmp.Field2, ...
   FROM    #TempResults tmp
   LEFT JOIN  Schema.RealTable rt
          ON  rt.KeyField1 = tmp.KeyField1
          ...
   WHERE   rt.KeyField1 IS NULL
Run Code Online (Sandbox Code Playgroud)

要么:

-- the MERGE command was introduced in SQL SERVER 2008
MERGE Schema.RealTable AS target
USING (SELECT Field1, Field2,... FROM #TempResults) AS source (Field1, Field2,..)
ON (target.KeyField1 = source.KeyField1)
WHEN MATCHED THEN 
  UPDATE SET Field2 = source.Field2,
         ...
WHEN NOT MATCHED THEN   
  INSERT (Field1, Field2,...)
     SELECT  tmp.Field1, tmp.Field2, ...
     FROM    #TempResults tmp
Run Code Online (Sandbox Code Playgroud)

有关MERGE命令的更多信息,请访问:http://msdn.microsoft.com/en-us/library/bb510625( v = SQL.100)
.aspx

现在,如果你有一个大的结果集要合并,你要合并的表非常大,并且在这种类型的操作可能导致一些阻塞的情况下有很多活动,那么它可以循环来做1000行的集合在某个时间或类似的东西.有点像这样:

<insert CREATE TABLE / INSERT...EXEC block>

CREATE TABLE #CurrentBatch
(
  Field1 DATATYPE1,
  Field2 DATATYPE2,
  ...
)

DECLARE @BatchSize SMALLINT = ????

WHILE (1 = 1)
BEGIN

    -- grab a set to work on
    DELETE TOP (@BatchSize)
    OUTPUT deleted.Field1, deleted.Field2, ...
    INTO #CurrentBatch (Field1, Field2, ...)
    FROM #TempResults

    IF (@@ROWCOUNT = 0)
    BEGIN
        -- no more rows
        BREAK
    END

    <insert either UPDATE / INSERT...SELECT block or MERGE block from above
     AND change references to #TempResults to be #CurrentBatch>

    TRUNCATE TABLE #CurrentBatch

END
Run Code Online (Sandbox Code Playgroud)


sko*_*gar 2

看看@srutzky的解决方案,它更适合这个问题


您可以做的第一件事是将所有内容写入表中。为此,您需要定义一个表,该表具有与存储过程返回的列相同的列:

DECLARE @myTempTableName TABLE(
                                dataRow1 DATATYPE,
                                dataRow2 DATATYPE,
                                ...
                               )

INSERT INTO @myTempTableName(dataRow1, dataRow2,...)
EXEC( *mystoredprocedure* )
Run Code Online (Sandbox Code Playgroud)

现在您需要的所有数据都在表中。下一步是检查需要更新哪些内容以及要插入哪些内容。假设 datarow1 是检查它是否已经存在的变量(例如:相同的名称或相同的 id)并且假设它是唯一的(否则您还需要一些唯一的东西 - 迭代临时表所需的)

DECLARE @rows INT,
        @dataRow1 DATATYPE,
        @dataRow2 DATATYPE, ...

-- COUNT Nr. of rows (how many rows are in the table)
SELECT 
    @rows = COUNT(1) 
FROM 
    @myTempTableName 

-- Loop while there are still some rows in the temporary table
WHILE (@rows > 0)

BEGIN

  -- select the first row and use dataRow1 as indicator which row it is. If dataRow1 is not unique the index should be provided by the stored procedure as an additional column
  SELECT TOP 1
    @dataRow1 = dataRow1,
    @dataRow2 = dataRow2, ..
  FROM
    @myTempTableName

  -- check if the value you'd like to insert already exists, if yes --> update, else --> insert
  IF EXISTS (SELECT * FROM *TableNameToInsertOrUpdateValues* WHERE dataRow1=@dataRow1)
      UPDATE 
            *TableNameToInsertOrUpdateValues*
      SET
            dataRow2=@dataRow2
      WHERE
            dataRow1=@dataRow1

  ELSE
      INSERT INTO 
            *TableNameToInsertOrUpdateValues* (dataRow1, dataRow2)
      VALUES 
            (@dataRow1, @dataRow2)


  --IMPORTANT: delete the line you just worked on from the temporary table
  DELETE FROM 
     @myTempTableName 
  WHERE 
     dataRow1= @dataRow1

  SELECT 
     @rows = COUNT(1) 
  FROM 
     @myTempTableName

END -- end of while-loop
Run Code Online (Sandbox Code Playgroud)

可以在此查询的开头完成声明。我把它放在我用过的地方,这样更容易阅读。

我从哪里获得了部分代码,并且对于迭代表也很有帮助(来自 @cmsjr 的解决方案,没有光标):Cursor insidecursor

  • 没有理由使用游标来执行此操作。 (2认同)