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

Posted

技术标签:

【中文标题】奇怪的 SQL Server 延迟加载表变量?【英文标题】:Weird SQL Server lazy loading of table variables? 【发布时间】:2011-06-27 10:36:59 【问题描述】:

我在 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 似乎已将表的准备工作留到需要它之前(在 INNER JOIN 完成之后),并且当它已经准备好要选择的大部分结果集时出错。

我也注意到成功插入@cross_reference 的值确实显示在返回的结果集中。而因为太大而无法插入的行只显示 NULL。

【问题讨论】:

【参考方案1】:

仅仅因为发生了错误,这并不能阻止存储过程的进行。您不会看到错误消息,因为默认情况下,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中使用的,当然最终查询表变量中是否存在any行并不重要。

【讨论】:

【参考方案2】:

我无法准确重现您所描述的内容。我想说的一件事是,专注于将数据插入到表 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?

【讨论】:

我尝试使用 ANSI_WARNINGS 明确打开,但它的行为方式完全相同。我在上面添加了更多细节。 在 INSERT 之后,您可以暂时插入“SELECT * FROM @TableVar”以使其转储内容吗?发生什么了?我有点难过,所以想建议我如何进行诊断。

以上是关于奇怪的 SQL Server 延迟加载表变量?的主要内容,如果未能解决你的问题,请参考以下文章

SQL Server 中的可延迟约束

Mybatis延迟加载和缓存

MyBatis学习——分步查询与延迟加载

MyBatis 延迟加载的三种加载方式深入,你get了吗?

mybatis 延迟加载

Mybatis查询延迟加载