开发者

Weird SQL Server lazy loading of table variables?

I came across a misleading error in SQL Server 2008 and I wonder if anyone can explain what's happening to me?

I have a stored procedure something like this:

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 

The actual stored proc is more complex but this is the gist.

Upon running the SP I get a load of output rows before it errors on "string or binary data would be truncated". The specific line references in the error pointed to somewhere in the main SELECT statement above, but after debugging a while, I found the values I was putting in the cross reference at the start were too large and that fixed it.

But my questions are:

  • how was SQL Server executing the main SELECT statement without having finishing inserting the cross reference rows? Is there some sort of deferred processing? Lazy loading of table variables?

  • Why on earth do "string or binary data would be truncated" message not point to where it is actually happening? Or am I doing it wrong?

Thanks, Ray

EDIT

I think it must 开发者_开发知识库be something to do with the LEFT JOIN to the table variable and that none of its columns are involved in the WHERE clause. SQL Server seems to have left the preparation of the table until it's needed (after the INNER JOINs are complete) and at that point errored when it already has the majority of the result set ready to SELECT.

I've noticed too that the values that are successfully INSERTed into @cross_reference do show in the result set that's returned. Whereas the rows that failed to insert for being too big just show NULL.


Just because an error has occurred, that doesn't stop the stored proc from progressing. You won't see the error message because by default, SSMS shows the result grid until the query has finished processing.

This shows results for 10 seconds before switching to the error message:

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

If you want the stored proc to exit early if an error occurs, you have to bail out early. You could re-write your insert to be something like:

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

Or if you don't know the row count, change the condition to @@ROWCOUNT = 0

(Or, of course, use TRY/CATCH - but I've not written much code using them myself yet, so can't just knock out an example)

Your current code has made each insert independent - so those inserts that can work, do, and those that cannot, produce error messages. Then it carries on with processing your final query. Because the table is used in a LEFT JOIN, of course it doesn't matter to the final query whether any rows exist in the table variable.


I can't reproduce exactly what you've described. One thing I would say, focusing on the inserting of data into the table var, is that if you have ANSI_WARNINGS ON then it should error at the point of insert.

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')

Do you have ANSI_WARNINGS ON or OFF for the connection?

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜