【问题标题】:Looping through SELECT result set in SQL循环遍历 SQL 中的 SELECT 结果集
【发布时间】:2015-02-24 17:17:51
【问题描述】:

我什至不知道如何用这个问题来表达这个问题,但是就这样吧。我需要能够在同一个 SQL 脚本中循环遍历结果集,并在更多 SQL 中使用结果。

例如

begin
SELECT (SELECT ColumnA, ColumnB from SomeTable) as x

loop through x(
    INSERT ColumnA into TableA
    INSERT ColumnB into TableB
    )
end

但我忘记了这样做的确切方法。我知道我以前在以前的职位上做过,但我在该公司的文件中找不到它的代码。

显然,这是一个非常粗略和基本的示例,我计划对结果集做更多的事情,但我只是举这个例子。


编辑:这是一个更接近的示例,说明我希望这样做以防万一这会有所帮助。

begin
    while(select columnA, columnB, columnC, columnD from myTable) as x

    begin
        INSERT columnA, columnB into TableA

        (get newly created ID of TableA - but that's a separate question involving @@IDENTITY)

        INSERT NewID, columnC, columnD into TableB
    end loop
end

【问题讨论】:

标签: sql sql-server sql-server-2008 tsql


【解决方案1】:

SQL 中称为CURSORSCURSOR的基本结构是:

 DECLARE @ColumnA INT, @ColumnB INT

 DECLARE CurName CURSOR FAST_FORWARD READ_ONLY
 FOR
    SELECT  ColumnA, ColumnB
    FROM    SomeTable

 OPEN CurName

 FETCH NEXT FROM CurName INTO @ColumnA, @ColumnB

 WHILE @@FETCH_STATUS = 0
    BEGIN

        INSERT  INTO TableA( ColumnA )
        VALUES  ( @ColumnA )
        INSERT  INTO TableB( ColumnB )
        VALUES  ( @ColumnB )

        FETCH NEXT FROM CurName INTO @ColumnA, @ColumnB

    END

 CLOSE CurName
 DEALLOCATE CurName

另一种迭代解决方法是WHILE循环。但是要使其正常工作,您应该在表中具有唯一的标识列。例如

DECLARE @id INT

SELECT TOP 1 @id  =  id FROM dbo.Orders ORDER BY ID

WHILE @id IS NOT NULL
BEGIN

  PRINT @id

  SELECT TOP 1 @id  =  id FROM dbo.Orders WHERE ID > @id ORDER BY ID
  IF @@ROWCOUNT = 0
  BREAK

END

但请注意,如果有替代而不是迭代的方式来完成相同的工作,则应避免使用 CURSORS。不过当然也有避免不了的情况CURSORs

【讨论】:

【解决方案2】:

处理以基于集合的方式获取身份的常用方法是通过OUTPUT 子句:

INSERT INTO TableA (ColumnA, ColumnB)
OUTPUT inserted.Id, inserted.ColumnA, inserted.ColumnB
SELECT  ColumnA, ColumnB
FROM    MyTable;

这里的问题是,您最理想的做法是:

INSERT INTO TableA (ColumnA, ColumnB)
OUTPUT inserted.Id, MyTable.ColumnC, inserted.ColumnD 
    INTO TableB (AID, ColumnC, ColumnD)
SELECT  ColumnA, ColumnB
FROM    MyTable;

问题是您不能在 OUTPUT 中引用源表,只能引用目标。幸运的是,有一个使用MERGE 的解决方法,因为如果您在永远不会为真的条件下使用MERGE,这允许您在输出子句中使用常驻内存插入表和源表的引用输出你需要的所有列:

WITH x AS
(   SELECT  ColumnA, ColumnB, ColumnC, ColumnD
    FROM    MyTable
)
MERGE INTO TableA AS a
USING x
    ON 1 = 0 -- USE A CLAUSE THAT WILL NEVER BE TRUE
WHEN NOT MATCHED THEN 
    INSERT (ColumnA, ColumnB)
    VALUES (x.ColumnA, x.ColumnB)
OUTPUT inserted.ID, x.ColumnC, x.ColumnD INTO TableB (NewID, ColumnC, ColumnD);

这种方法的问题是 SQL Server 不允许你插入外键关系的任何一侧,所以如果 tableB.NewID 引用 tableA.ID 那么上面的操作会失败。要解决此问题,您需要输出到临时表中,然后将临时表插入 TableB:

CREATE TABLE #Temp (AID INT, ColumnC INT, ColumnD INT);
WITH x AS
(   SELECT  ColumnA, ColumnB, ColumnC, ColumnD
    FROM    MyTable
)
MERGE INTO TableA AS a
USING x
    ON 1 = 0 -- USE A CLAUSE THAT WILL NEVER BE TRUE
WHEN NOT MATCHED THEN 
    INSERT (ColumnA, ColumnB)
    VALUES (x.ColumnA, x.ColumnB)
OUTPUT inserted.ID, x.ColumnC, x.ColumnD INTO #Temp (AID, ColumnC, ColumnD);

INSERT TableB (AID, ColumnC, ColumnD)
SELECT AID, ColumnC, ColumnD
FROM #Temp;

Example on SQL Fiddle

【讨论】:

  • 虽然对于两个表来说是一个有用的(如果是钝的)技巧,但它不会超出这个范围。显而易见的方法是将中间结果具体化为临时表。
  • 我正在添加编辑以将输出显示到临时表中,但出于不同的原因。尽管如此,还是需要使用MERGE。您希望答案的可扩展性如何?满足插入到一张表并输出新插入的id,将原表中的列输出到第三张表的标准。如果需要进一步操作,那么可以,显而易见的方法是使用临时表。
  • 它不需要非常可扩展。这不会是常态。我将导入地址列表。将来可能会再次发生,但不会那么频繁。我也不需要最好的表现,因为这真的是一次,或者至少在很长一段时间内一次,事件。
【解决方案3】:

不要使用游标,因为它在性能方面不好。

试试这个链接: http://support2.microsoft.com/kb/111401

您应该使用 ROW_NUMBER() 并将数据填充到临时表中 - 变量或全局表。

【讨论】:

  • 这确实让我记得它类似于:while((select stuff from table) as x) begin ...do stuff...end.
  • 将结果插入临时表并循环访问它们与使用游标相同,但不易管理。如果您使用正确的选项声明游标,那么它将比临时表上的 while 循环执行得更好。进一步阅读 - Bad Habits to Kick : Thinking a WHILE loop isn't a CURSOR
【解决方案4】:

如果您只是将数据从一个表插入到另一个表中,您可以使用这种方法:

insert into TableA (ColumnA, ColumnB, myTableID)
select (ColumnA, ColumnB, myTableID) from myTable

insert into TableB (ColumnC, ColumnD, myTableID)
select m.ColumnC, m.ColumnD, a.TableAid
from myTable m
join TableA a
    on m.myTableID = a.myTableID

编辑:

可以在myTable的外键TableA中添加一列。这将帮助您在TableB 中获得TableA 的ID。

【讨论】:

  • 实际上要复杂得多。我只是用它作为一个非常基本的例子。我会更详细地更新问题。
  • myTable中没有TableA的外键。 TableA 中的 ID 是在插入时生成的。
  • @ReverendDovie 我知道。我认为添加它没有任何害处
  • 但是会包含什么内容呢?我要查找的 ID 在插入之前不存在
  • @ReverendDovie 将myTable 的PK 插入TableA。然后,对于TableB,已经有一个相关的TableAmyTable。您可以添加where 以将myTable 中的那些在TableB 中不需要的记录列入候选名单。
猜你喜欢
  • 2019-02-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-17
  • 1970-01-01
  • 2013-09-14
相关资源
最近更新 更多