Странная ленивая загрузка табличных переменных SQL Server?

Я столкнулся с вводящей в заблуждение ошибкой в ​​SQL Server 2008, и мне интересно, может ли кто-нибудь объяснить, что со мной происходит?

У меня есть хранимая процедура примерно так:

declare @cross_reference table (
      context    varchar(20)    not null
    , value1     varchar(10)    not null
    , value2     varchar(10)    not null
)

insert into @cross_reference values ('map', 'from_value', 'to_value')
insert into @cross_reference values ('map', 'from_value', 'to_value')
insert into @cross_reference values ('map', 'from_value_xxxxx', 'to_value_xxxxxxx')

select 
    t1.field1
    , t1.field2
    , xref.value2
from table_1 t1
inner join table_2 t2 on t2.id = t1.id
left join @cross_reference xref on xref.context = 'map' and xref.value1 = t2.map_id 

Фактическая хранимая процедура более сложна, но суть в этом.

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

Но мои вопросы:

  • как SQL Server выполнял основной оператор SELECT, не завершив вставку строк перекрестных ссылок? Есть ли какая-то отложенная обработка? Ленивая загрузка табличных переменных?

  • С какой стати сообщение «строка или двоичные данные будут усечены» не указывает на то, где это происходит на самом деле? Или я делаю это неправильно?

Спасибо, Рэй

РЕДАКТИРОВАТЬ

Я думаю, что это должно быть как-то связано с LEFT JOIN для переменной таблицы и с тем, что ни один из ее столбцов не участвует в предложении WHERE. SQL Server, похоже, оставил подготовку таблицы до тех пор, пока она не понадобится (после завершения ВНУТРЕННИХ СОЕДИНЕНИЙ), и в этот момент произошла ошибка, когда большая часть набора результатов уже готова к SELECT.

Я также заметил, что значения, которые были успешно ВСТАВЛЕНЫ в @cross_reference, действительно отображаются в возвращаемом наборе результатов. В то время как строки, которые не удалось вставить из-за того, что они слишком велики, просто показывают NULL.


person Ray    schedule 27.06.2011    source источник


Ответы (2)


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

Это показывает результаты в течение 10 секунд, прежде чем перейти к сообщению об ошибке:

RAISERROR('Error',16,1)
select top 1000 * from sys.objects so1,sys.objects so2,sys.objects so3
WAITFOR DELAY '00:00:10'

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

declare @cross_reference table (
      context    varchar(20)    not null
    , value1     varchar(10)    not null
    , value2     varchar(10)    not null
)

insert into @cross_reference (context,value1,value2)
values ('map', 'from_value', 'to_value'),
       ('map', 'from_value', 'to_value'),
       ('map', 'from_value_xxxxx', 'to_value_xxxxxxx');
if @@ERROR !=0 or @@ROWCOUNT !=3 return

Или, если вы не знаете количество строк, измените условие на @@ROWCOUNT = 0

(Или, конечно, использовать TRY/CATCH - но я еще не написал много кода, используя их, поэтому не могу просто выбить пример)

Ваш текущий код сделал каждую вставку независимой, поэтому те вставки, которые могут работать, работают, а те, которые не могут, выдают сообщения об ошибках. Затем он продолжает обработку вашего окончательного запроса. Поскольку таблица используется в ЛЕВОМ СОЕДИНЕНИИ, конечно, для окончательного запроса не имеет значения, существуют ли какие-либо строки в табличной переменной.

person Damien_The_Unbeliever    schedule 27.06.2011

Я не могу воспроизвести именно то, что вы описали. Одна вещь, которую я хотел бы сказать, сосредоточившись на вставке данных в таблицу var, заключается в том, что если у вас есть ANSI_WARNINGS ON, тогда в точке вставки должна возникнуть ошибка.

i.e.

SET ANSI_WARNINGS ON -- Gives the truncation error 
DECLARE @T1 TABLE (value1 varchar(10) not null)
INSERT @T1 VALUES ('1234567890ABC')

SET ANSI_WARNINGS OFF -- Does not give the truncation error. "1234567890" will be inserted
DECLARE @T1 TABLE (value1 varchar(10) not null)
INSERT @T1 VALUES ('1234567890ABC')

У вас есть ANSI_WARNINGS ON или OFF для соединения?

person AdaTheDev    schedule 27.06.2011
comment
Я попытался с явно включенным ANSI_WARNINGS, но он вел себя точно так же. Я добавил больше деталей выше. - person Ray; 27.06.2011
comment
Сразу после INSERT, можете ли вы временно вставить SELECT * FROM @TableVar, чтобы он выгрузил содержимое? Что случается? Я немного в тупике, поэтому пытаюсь предложить, как я могу поставить диагноз. - person AdaTheDev; 27.06.2011