2012-06-19 4 views
1

Я заметил странную вещь внутри хранимой процедуры с помощью select on table variables. Он всегда возвращает значение (на последующих итерациях), которое было выбрано на первой итерации курсора. Вот пример кода, подтверждающий это.Таблица Переменная внутри курсора, странное поведение - SQL Server

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

Если я правильно прочитал этот код, вы создадите новую таблицу @Log с каждым циклом. Если это так, то @Log всегда имеет только 1 запись !? Какую цель вы хотите достичь, что вы ожидаете и что действительно происходит. До сих пор мне нужно больше разъяснений, чтобы помочь. – YvesR

+0

@YvesR - Вы не читаете его правильно. Это оператор объявления, а не исполняемая строка. Он создается неявно один раз. Код никогда не должен доходить до этой линии. например 'IF (1 = 0) BEGIN DECLARE @T TABLE (X INT) END; SELECT * FROM @T; '(хотя анализатор будет жаловаться, если он встретит ссылку на него до того, как увидит объявление) –

+0

@MartinSmith, вы правы. Спасибо за объяснение. –

ответ

4

С этим кодом есть множество вещей.

Во-первых, вы откатываете свою встроенную транзакцию по ошибке, но я никогда не вижу, чтобы вы совершали ее с успехом. Как написано, это приведет к утечке транзакции, которая может вызвать серьезные проблемы для вас в следующем коде.

Что может вас смутить в ситуации с таблицей @Log, так это то, что SQL Server не использует те же правила определения переменных и жизненных циклов, что и C++ или другие стандартные языки программирования. Даже когда вы объявляете свою переменную таблицы в блоке курсора, вы получите только одну таблицу @Log, которая затем живет для оставшейся части партии и которая получает несколько строк, вставленных в нее.

В результате ваше использование TOP 1 на самом деле не имеет смысла, так как нет пункта ORDER BY, чтобы наложить какой-либо детерминированный порядок на стол. Без этого вы получите любой заказ SQL Server, который вам подходит, который в этом случае выглядит как порядок вставки, предоставляя вам первый вставленный элемент этой таблицы журналов каждый раз, когда вы запускаете SELECT.

Если вы действительно хотите получить только последнее значение идентификатора, вам необходимо будет предоставить какой-либо реальный критерий заказа для вашей таблицы @Log - некоторую форму автономера или поля даты рядом с столбцом данных, которые могут использоваться для обеспечения правильного упорядочения для что ты хочешь делать.

+0

Я удалил транзакционную купюру, поскольку она не имеет отношения к указанной проблеме. –

+0

Я использовал верхнюю часть 1 как меру. Но ваша точка в предложении order by действительна, учитывая применимые правила изменения видимости и срока жизни.В качестве решения я объявил переменную таблицы снаружи и удалив все строки внутри курсора до того, как инструкция insert вставляет какой-либо. –

Смежные вопросы