Puis-je créer un type de table défini par l'utilisateur et l'utiliser dans la même transaction?

13

Lorsque j'exécute ce qui suit (dans le studio de gestion, GO séparera les commandes en lots)

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

Je reçois un message d'erreur de blocage. Mon processus s'est bloqué avec lui-même. J'ai vu ce comportement en 2008, 2008R2 et 2012.

Existe-t-il un moyen d'utiliser mon type nouvellement créé dans la même transaction qu'il a été créé?

Michael J Swart
la source
Pourquoi faites-vous cela dans une transaction? Espérez-vous un UDT «temporaire»?
Max Vernon
2
Je savais que j'aurais cette question. Cela fait partie d'un test d'intégration. Le cadre de test d'intégration effectue tout en une seule transaction.
Michael J Swart
1
Une solution de contournement évidente serait de créer les types nécessaires pour le test avant d'exécuter le test. De toute évidence, cela ne vous aide toutefois pas à automatiser les tests.
Max Vernon
@MichaelJSwart, pourriez-vous élaborer un peu plus sur ce que vous essayez de réaliser? Les types de tableaux sont si restrictifs que je ne vois pas très bien où vous allez avec ça.
Sebastian Meine
1
Pour info j'ai blogué sur ce sqlperformance.com/2013/11/t-sql-queries/single-tx-deadlock
Aaron Bertrand

Réponses:

15

Cela a été signalé pas moins de quatre fois. Celui-ci a été fermé comme fixé:

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

Mais ce n'était pas vrai. (Consultez également la section des solutions de contournement - la solution de contournement que j'ai suggérée ne sera pas toujours acceptable.)

Celui-ci a été fermé comme par conception / ne résoudra pas:

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

Ces deux sont plus récents et toujours actifs :

http://connect.microsoft.com/SQLServer/feedback/details/800919/ (désormais fermé comme ne sera pas résolu )

http://connect.microsoft.com/SQLServer/feedback/details/804365/ (désormais fermé en tant que By Design )

Jusqu'à ce que Microsoft soit convaincu du contraire, vous devrez trouver une solution de contournement: il vous suffit de déployer tous les types avant d'exécuter votre test ou de le diviser en plusieurs tests.

J'essaierai d'obtenir la confirmation de mes contacts sur ce qu'Umachandar entendait par «corrigé» dans le premier élément, car il est évident que cela entre en conflit avec des déclarations ultérieures.

MISE À JOUR # 1 (de, espérons-le, exactement 2)

Le bogue d'origine (qui a été fermé comme corrigé) impliquait des types d'alias, mais pas de type TABLE. Il a été signalé contre SQL Server 2005, qui n'avait évidemment pas de types de table et de TVP. Il semble que UC ait signalé que le bogue avec les types d'alias non-table a été corrigé en fonction de la façon dont ils gèrent les transactions internes, mais il ne couvrait pas un scénario similaire introduit plus tard avec les types de table. J'attends toujours la confirmation si ce bogue d'origine aurait dû être fermé comme corrigé; J'ai proposé que tous les quatre soient fermés comme par conception. C'est en partie parce que c'est un peu la façon dont je m'attendais à ce qu'il fonctionne, et en partie parce que j'ai le sentiment de UC que "le fixer" pour fonctionner d'une manière différente est extrêmement complexe, pourrait briser la compatibilité descendante et serait utile dans un nombre très limité de cas d'utilisation. Rien contre vous ou votre cas d'utilisation, mais en dehors des scénarios de test, je '

MISE À JOUR # 2

J'ai blogué sur ce problème:

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

Aaron Bertrand
la source
1

J'ai pu reproduire cela. Le graphique de l'impasse est assez curieux:

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

Cela me semble être un bug et je vous recommande d'ouvrir un élément de connexion pour cela.


Pour contourner votre problème immédiat, vous pouvez utiliser tSQLt.NewConnection(je suppose que vous utilisez 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

Je ne comprends toujours pas d'où vient le besoin de créer un type de tableau à la volée et je suppose que vous compliquez trop votre test. Envoyez-moi un e-mail si vous souhaitez discuter.

Sebastian Meine
la source
2
Merci pour votre aide Sebastian. Malheureusement, je n'utilise pas tSQLt. Vous ne comprenez pas d'où vient le besoin de créer un type à la volée car je ne l'ai pas expliqué. Je ne complique pas trop les choses, mais je ne ressens pas le besoin de le démontrer.
Michael J Swart
Eh bien, regardez le code source tSQLt de la façon dont tSQLt.NewConnection est implémenté. C'est assez simple et devrait également fonctionner dans votre cadre.
Sebastian Meine
1
Toute tentative de créer le type puis de l'utiliser dans la même transaction entraîne un blocage (voir mon rapport de bogue, lié dans le post d'Aaron - dernier lien); cette solution de contournement ne fonctionnera pas (enfin, en supposant qu'il ne fait pas quelque chose de stupide comme valider une transaction ouverte avant d'exécuter l'instruction d'entrée).
Jon Seigel
-1

À moins que quelqu'un ne sache différent, je ne pense pas qu'il existe un moyen de le faire en une seule transaction. Je ne pense pas que ce soit un bug.

Tout d'abord, vous devez prendre un verrou de modification de schéma (Sch-M) lorsque vous créez le type. Puisque vous ne validez pas la transaction, le verrou reste ouvert. Ensuite, vous essayez de déclarer une variable de ce type dans la même transaction. Cela essaie de prendre un verrou de stabilité du schéma (Sch-S). Ces deux types sont incompatibles simultanément sur le même objet. Puisqu'ils sont dans la même transaction, SQL la traite comme un blocage car le Sch-S ne peut jamais être accordé lorsque la transaction est ouverte.

Exécutez chaque lot un par un et sélectionnez contre sys.dm_tran_locks dès que vous essayez de déclarer la variable. Vous verrez le même processus tenir un Sch-M et attendre un Sch-S sur le même objet.

dmmaxwell
la source
3
Les types sont incompatibles, mais je pensais que le même processus n'aurait pas à s'attendre à lui-même. Par exemple, je peux ajouter une colonne à une table, puis l'utiliser dans la même transaction.
Michael J Swart
3
Le gestionnaire de verrous doit comprendre que le processus détient déjà un verrou (en fait plus fort) sur la ressource et que le processus ne doit pas attendre. Après tout, vous pouvez d'abord mettre à jour une ligne, puis la relire dans la même situation.
Sebastian Meine