我在存储过程中发现了一个奇怪的事情,选择了表变量。它始终返回在游标的第一次迭代中获取的值(在后续迭代中)。以下是一些证明这一点的示例代码。
DECLARE @id AS INT;
DECLARE @outid AS INT;
DECLARE sub_cursor CURSOR FAST_FORWARD
FOR SELECT [TestColumn]
FROM testtable1;
OPEN sub_cursor;
FETCH NEXT FROM sub_cursor INTO @id;
WHILE @@FETCH_STATUS = 0
BEGIN
DECLARE @Log TABLE (LogId BIGINT NOT NULL);
PRINT 'id: ' + CONVERT (VARCHAR (10), @id);
INSERT INTO Testtable2 (TestColumn)
OUTPUT inserted.[TestColumn] INTO @Log
VALUES (@id);
IF @@ERROR = 0
BEGIN
SELECT TOP 1 @outid = LogId
FROM @Log;
PRINT 'Outid: ' + CONVERT (VARCHAR (10), @outid);
INSERT INTO [dbo].[TestTable3] ([TestColumn])
VALUES (@outid);
END
FETCH NEXT FROM sub_cursor INTO @id;
END
CLOSE sub_cursor;
DEALLOCATE sub_cursor;
然而,当我在SO上发布代码并尝试了各种组合时,我观察到从下面的行中删除顶部,从光标内的表变量中给出正确的值。
SELECT TOP 1 @outid = LogId FROM @Log;
这将使它像这样
SELECT @outid = LogId FROM @Log;
我不确定这里发生了什么。我认为表变量上的TOP 1应该有效,认为在循环的每次迭代中都会创建一个新表。有人可以对表变量范围和生命周期进行说明。
更新:我有解决方案来规避这里的奇怪行为。 作为一个解决方案,我在循环之前将表声明为顶部,并在循环开始时删除所有行。
答案 0 :(得分:4)
这段代码有很多东西。
首先,您在错误时回滚嵌入式事务,但我从未看到您在成功时提交它。如上所述,这将泄漏事务,这可能会在以下代码中导致重大问题。
对于@Log
表情况,您可能会感到困惑的是,SQL Server不使用与C ++或其他标准编程语言相同的变量作用域和生存期规则。即使在游标块中声明了表变量,您也只能得到一个@Log
表,然后该表用于批处理的其余部分,并且会将多行插入其中。
因此,使用TOP 1
并没有多大意义,因为没有ORDER BY
子句可以在表上强加任何类型的确定性排序。如果没有它,您将获得SQL Server认为适合的任何顺序,在这种情况下看起来是插入顺序,每次运行SELECT
时都会为您提供该日志表的第一个插入元素。
如果您真的只想要最后一个ID值,则需要为@Log
表提供一些实际的排序标准 - 数据列旁边的某种形式的自动编号或日期字段可用于提供正确排序你想做什么。