Kann ich einen benutzerdefinierten Tabellentyp erstellen und in derselben Transaktion verwenden?

13

Wenn ich Folgendes ausführe (in Management Studio werden die Befehle von GO in Stapel aufgeteilt)

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

Ich erhalte eine Deadlock-Fehlermeldung. Mein Prozess hat mit sich selbst festgefahren. Ich habe dieses Verhalten in 2008, 2008R2 und 2012 gesehen.

Gibt es eine Möglichkeit, meinen neu erstellten Typ in derselben Transaktion zu verwenden, in der er erstellt wurde?

Michael J Swart
quelle
Warum machst du das in einer Transaktion? Hoffen Sie auf einen "vorübergehenden" UDT?
Max Vernon
2
Ich wusste, dass ich diese Frage bekommen würde. Es ist Teil eines Integrationstests. Das Integrationstest-Framework führt alles in einer einzigen Transaktion aus.
Michael J Swart
1
Eine naheliegende Problemumgehung besteht darin, die für den Test erforderlichen Typen zu erstellen, bevor der Test ausgeführt wird. Das hilft Ihnen natürlich nicht, das Testen zu automatisieren.
Max Vernon
@MichaelJSwart, könntest du noch etwas genauer erläutern, was du erreichen willst? Die Tabellentypen sind so restriktiv, dass ich nicht genau sehen kann, wo Sie sich befinden.
Sebastian Meine
1
Zu Ihrer Information
Aaron Bertrand

Antworten:

15

Dies wurde nicht weniger als vier Mal gemeldet. Dieser wurde als behoben geschlossen:

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

Das stimmte aber nicht. (Sehen Sie sich auch den Abschnitt mit den Problemumgehungen an - die von mir vorgeschlagene Problemumgehung ist nicht immer akzeptabel.)

Dieser wurde absichtlich geschlossen / wird nicht repariert:

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

Diese beiden sind neuer und immer noch aktiv :

http://connect.microsoft.com/SQLServer/feedback/details/800919/ (jetzt geschlossen als Won't Fix )

http://connect.microsoft.com/SQLServer/feedback/details/804365/ (jetzt geschlossen als By Design )

Solange Microsoft nicht davon überzeugt ist, müssen Sie eine Problemumgehung finden. Stellen Sie einfach alle Typen bereit, bevor Sie den Test ausführen, oder teilen Sie ihn in mehrere Tests auf.

Ich werde versuchen, von meinen Kontakten eine Bestätigung darüber zu erhalten, was Umachandar unter dem frühesten Punkt verstanden hat, da dies offensichtlich im Widerspruch zu späteren Aussagen steht.

UPDATE # 1 (von hoffentlich genau 2)

Der ursprüngliche Fehler (der als behoben geschlossen wurde) betraf Alias-Typen, jedoch keine Typen TABLE. Es wurde gegen SQL Server 2005 gemeldet, das offensichtlich keine Tabellentypen und TVPs hatte. Es scheint, dass UC berichtet hat, dass der Fehler mit Nicht-Tabellen-Alias-Typen behoben wurde, basierend auf der Art und Weise, wie interne Transaktionen behandelt werden. Ein ähnliches Szenario, das später mit Tabellentypen eingeführt wurde, wurde jedoch nicht behandelt. Ich warte immer noch auf die Bestätigung, ob dieser ursprüngliche Fehler jemals als behoben hätte geschlossen werden sollen. Ich habe vorgeschlagen, alle vier wie beabsichtigt zu schließen. Dies liegt zum Teil daran, dass ich erwartet habe, dass es funktioniert, und zum Teil daran, dass ich von UC das Gefühl bekomme, dass es extrem komplex ist, die Abwärtskompatibilität aufheben könnte und hilfreich für eine sehr begrenzte Anzahl von Anwendungsfällen. Nichts gegen Sie oder Ihren Anwendungsfall, aber außerhalb der Testszenarien

UPDATE # 2

Ich habe über dieses Problem gebloggt:

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

Aaron Bertrand
quelle
1

Ich konnte das reproduzieren. Das Deadlock-Diagramm ist ziemlich merkwürdig:

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

Es sieht für mich wie ein Fehler aus und ich würde empfehlen, dass Sie ein Verbindungselement dafür öffnen.


Um Ihr unmittelbares Problem zu tSQLt.NewConnectionumgehen, können Sie Folgendes verwenden (ich gehe davon aus, dass Sie tSQLt verwenden)

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

Ich verstehe immer noch nicht, woher die Notwendigkeit kommt, einen Tabellentyp im laufenden Betrieb zu erstellen, und ich gehe davon aus, dass Sie Ihren Test zu kompliziert machen. Senden Sie mir eine E-Mail, wenn Sie diskutieren möchten.

Sebastian Meine
quelle
2
Danke für deine Hilfe Sebastian. Leider benutze ich tSQLt nicht. Du verstehst nicht, woher die Notwendigkeit kommt, einen Typ im laufenden Betrieb zu erstellen, weil ich es nicht erklärt habe. Ich mache die Dinge nicht zu kompliziert, aber ich habe nicht das Bedürfnis, es zu demonstrieren.
Michael J Swart
Schauen Sie sich den tSQLt-Quellcode für die Implementierung von tSQLt.NewConnection an. Es ist ziemlich einfach und sollte auch in Ihrem Framework funktionieren.
Sebastian Meine
1
Jeder Versuch, den Typ zu erstellen und ihn dann in derselben Transaktion zu verwenden, führt zu einem Deadlock (siehe meinen Fehlerbericht, der in Aarons nachletztem Link verlinkt ist). Diese Problemumgehung funktioniert nicht (naja, vorausgesetzt, es ist nicht dumm, vor dem Ausführen der Eingabeanweisung eine offene Transaktion festzuschreiben).
Jon Seigel
-1

Ich glaube nicht, dass es eine Möglichkeit gibt, dies in einer einzigen Transaktion zu tun, es sei denn, jemand weiß etwas anderes. Ich denke nicht, dass dies ein Fehler ist.

Zunächst müssen Sie beim Erstellen des Typs eine Schemamodifikationssperre (Sch-M) aktivieren. Da Sie die Transaktion nicht festschreiben, bleibt das Schloss geöffnet. Anschließend versuchen Sie, in derselben Transaktion eine Variable für diesen Typ zu deklarieren. Dadurch wird versucht, eine Schemastabilitätssperre (Sch-S) zu übernehmen. Diese beiden Typen sind auf demselben Objekt gleichzeitig inkompatibel. Da sie sich in derselben Transaktion befinden, wird dies von SQL als Deadlock behandelt, da Sch-S niemals gewährt werden kann, solange die Transaktion geöffnet ist.

Führen Sie jeden Stapel einzeln aus und wählen Sie sys.dm_tran_locks aus, sobald Sie versuchen, die Variable zu deklarieren. Sie werden sehen, wie derselbe Prozess ein Sch-M hält und auf ein Sch-S für dasselbe Objekt wartet.

dmmaxwell
quelle
3
Die Typen sind nicht kompatibel, aber ich dachte, dass der gleiche Prozess nicht auf sich warten müsste. Beispielsweise kann ich einer Tabelle eine Spalte hinzufügen und sie dann in derselben Transaktion verwenden.
Michael J Swart
3
Der Sperrenmanager sollte herausfinden, dass der Prozess die Ressource bereits (tatsächlich stärker) sperrt und der Prozess nicht warten muss. Sie können immerhin zuerst eine Zeile aktualisieren und dann in derselben Situation wieder einlesen.
Sebastian Meine