我可以创建用户定义的表类型并在同一事务中使用它吗?


13

当我执行以下命令时(在Management Studio中,GO会将命令分成批处理)

use tempdb

begin tran
go

CREATE TYPE dbo.IntIntSet AS TABLE(
    Value0 Int NOT NULL,
    Value1 Int NOT NULL
)
go

declare @myPK dbo.IntIntSet;
go

rollback

我收到死锁错误消息。我的过程已经陷入僵局。我已经在2008、2008R2和2012中看到了这种行为。

有没有办法在创建的同一笔交易中使用我新创建的类型?


您为什么要在交易中执行此操作?您是否希望使用“临时” UDT?
Max Vernon

2
我知道我会得到这个问题。这是集成测试的一部分。集成测试框架在单个事务中执行所有操作。
Michael J Swart

1
一个明显的解决方法是在执行测试之前创建测试所需的类型。显然,但这并不能帮助您自动化测试。
Max Vernon

@MichaelJSwart,能否详细说明一下您要实现的目标?表类型的限制如此之大,以至于我无法确定您是否正在使用此表。
塞巴斯蒂安·梅因

1
仅供参考,我在此发布了
亚伦·贝特朗

Answers:


15

据报道不少于四次。这是固定关闭的:

http://connect.microsoft.com/SQLServer/feedback/details/365876/

但这不是事实。(另请参阅解决方法部分-我建议的解决方法并非总是可以接受的。)

这是因为设计而关闭/无法解决:

http://connect.microsoft.com/SQLServer/feedback/details/581193/

这两个是较新的并且仍然活跃

http://connect.microsoft.com/SQLServer/feedback/details/800919/(由于无法修复,现已关闭)

http://connect.microsoft.com/SQLServer/feedback/details/804365/(现在按设计关闭)

除非可以使Microsoft确信,否则您将必须找到一种解决方法-在运行测试之前就已经部署了所有类型,或者将其分解为多个测试。

我将尽力从我的联系人那里获得关于Umachandar在最早的项目中所指的含义的确认,因为显然,这与以后的声明相冲突。

更新#1(希望准确地是2)

原始错误(已固定修复)涉及别名类型,但不涉及类型 TABLE。据报道是针对SQL Server 2005,它显然没有表类型和TVP。UC似乎报告说,基于非表别名类型的错误是基于它们处理内部事务的方式而修复的,但是并未涵盖后来引入表类型的类似情况。我仍在等待确认该原始错误是否应该按已修复的方式关闭;我建议按照设计关闭所有四个。这部分是因为这是我期望其工作的方式,部分是因为我从UC得知,以其他方式“修复”它非常复杂,可能会破坏向后兼容性,并且对用例数量非常有限。不会对您或您的用例造成不利影响,但是在测试方案之外,我会

更新#2

我已经就此问题写了博客:

http://www.sqlperformance.com/2013/11/t-sql-queries/single-tx-deadlock


1

我能够重现这一点。死锁图很奇怪:

<deadlock-list>
  <deadlock victim="process47f948">
    <process-list>
      <process id="process47f948" taskpriority="0" logused="0" waitresource="METADATA: database_id = 2 USER_TYPE(user_type_id = 257)" waittime="3607" ownerId="14873" transactionname="@myPK" lasttranstarted="2013-11-06T13:23:12.177" XDES="0x80f6d950" lockMode="Sch-S" schedulerid="1" kpid="2672" status="suspended" spid="54" sbid="0" ecid="0" priority="0" trancount="1" lastbatchstarted="2013-11-06T13:23:12.167" lastbatchcompleted="2013-11-06T13:23:12.163" clientapp="Microsoft SQL Server Management Studio - Query" hostname="xxxxx" hostpid="5276" loginname="xxxxx\xxxxx" isolationlevel="read committed (2)" xactid="14867" currentdb="2" lockTimeout="4294967295" clientoption1="671090784" clientoption2="390200">
        <executionStack>
          <frame procname="adhoc" line="2" sqlhandle="0x010002002d9fe3155066b380000000000000000000000000">
declare @myPK dbo.IntIntSet;     </frame>
        </executionStack>
        <inputbuf>

declare @myPK dbo.IntIntSet;
    </inputbuf>
      </process>
    </process-list>
    <resource-list>
      <metadatalock subresource="USER_TYPE" classid="user_type_id = 257" dbid="2" id="lock8009cc00" mode="Sch-M">
        <owner-list>
          <owner id="process47f948" mode="Sch-M" />
        </owner-list>
        <waiter-list>
          <waiter id="process47f948" mode="Sch-S" requestType="wait" />
        </waiter-list>
      </metadatalock>
    </resource-list>
  </deadlock>
</deadlock-list>

在我看来,它像个错误,我建议您为此打开一个连接项。


为了解决您眼前的问题,您可以使用tSQLt.NewConnection(我假设您正在使用tSQLt)

use tempdb

begin tran
go
EXEC tSQLt.NewConnection '
CREATE TYPE dbo.IntIntSet AS TABLE(
    Value0 Int NOT NULL,
    Value1 Int NOT NULL
)
';
go

declare @myPK dbo.IntIntSet;
go

rollback

我仍然不知道从何处动态创建表类型的需求来自何处,并且我认为您的测试过于复杂。如果您想讨论给我发电子邮件。


2
感谢您的帮助塞巴斯蒂安。不幸的是,我没有使用tSQLt。您不了解在哪里动态创建类型的需求来自哪里,因为我没有解释。我并没有使事情复杂化,但是我不觉得需要证明它。
Michael J Swart

好吧,看看如何实现tSQLt.NewConnection的tSQLt源代码。这很简单,也应该在您的框架中工作。
塞巴斯蒂安·梅因

1
任何试图创建类型然后在同一事务中使用它的尝试都会导致死锁(请参阅我的错误报告,链接在Aaron的文章-最后的链接中);这种解决方法将不起作用(嗯,假设它没有执行诸如在执行输入语句之前提交一个打开的事务之类的愚蠢动作)。
乔恩·塞格尔

-1

除非有人知道不同,否则我认为没有办法在一次交易中做到这一点。我不认为这是一个错误。

首先,在创建类型时,您需要使用模式修改锁(Sch-M)。由于您不提交事务,因此锁保持打开状态。然后,您尝试在同一事务中声明该类型的变量。这将尝试获取架构稳定性锁(Sch-S)。这两种类型在同一对象上同时不兼容。由于它们在同一个事务中,因此SQL会将其视为死锁,因为在事务打开时永远无法授予Sch-S。

一次运行每个批处理,并在尝试声明变量时立即针对sys.dm_tran_locks选择。您将看到相同的过程,其中包含一个Sch-M并在同一对象上等待一个Sch-S。


3
类型是不兼容的,但我认为相同的过程不必等待本身。例如,我可以在表中添加一列,然后在同一事务中使用它。
Michael J Swart 2013年

3
锁管理器应确定该进程已经在资源上持有了(实际上更强)的锁,并且该进程不必等待。毕竟,您可以先更新一行,然后在同样的情况下再读一次。
塞巴斯蒂安·梅因
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.