奇怪的 SQL Server 延迟加载表变量?

发布于 2024-11-17 11:11:59 字数 1233 浏览 6 评论 0原文

我在 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 语句?是否有某种延迟处理?表变量的延迟加载?

  • 到底为什么“字符串或二进制数据将被截断”消息没有指向它实际发生的位置?或者我做错了?

谢谢, Ray

编辑

我认为这一定与表变量的 LEFT JOIN 有关,并且 WHERE 子句中没有涉及它的列。 SQL Server 似乎已经将表的准备工作留到需要时为止(在 INNER JOIN 完成之后),并且当它已经准备好 SELECT 的大部分结果集时会出错。

我还注意到,成功插入到 @cross_reference 中的值确实显示在返回的结果集中。而因太大而未能插入的行则仅显示 NULL。

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

别把无礼当个性 2024-11-24 11:11:59

仅仅因为发生了错误,并不会阻止存储过程的进行。您不会看到错误消息,因为默认情况下,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 - 但我自己还没有使用它们编写太多代码,所以不能只是敲出一个例子)

您当前的代码使每个插入都是独立的 - 所以那些插入可以工作,做,而那些不能工作,会产生错误消息。然后它继续处理您的最终查询。因为表是在 LEFT JOIN 中使用的,所以表变量中是否存在任何行当然对于最终查询来说并不重要。

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.

飞烟轻若梦 2024-11-24 11:11:59

我无法准确重现您所描述的内容。我要说的一件事是,重点关注将数据插入到表 var 中,如果您将 ANSI_WARNINGS 设置为 ON,那么它应该在插入时出错。

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 是打开还是关闭?

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?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文