Posso criar um tipo de tabela definido pelo usuário e usá-lo na mesma transação?

13

Quando executo o seguinte (no estúdio de gerenciamento, o GO separará os comandos em lotes)

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

Recebo uma mensagem de erro de conflito. Meu processo entrou em conflito consigo mesmo. Eu já vi esse comportamento em 2008, 2008R2 e 2012.

Existe uma maneira de usar meu tipo recém-criado dentro da mesma transação que foi criada?

Michael J Swart
fonte
Por que você está fazendo isso dentro de uma transação? Você está esperando uma UDT 'temporária'?
Max Vernon
2
Eu sabia que iria receber essa pergunta. Faz parte de um teste de integração. A estrutura de teste de integração executa tudo em uma única transação.
Michael J Swart
1
Uma solução óbvia seria criar os tipos necessários para o teste antes de executá-lo. Claramente, isso não ajuda a automatizar o teste.
Max Vernon
@MichaelJSwart, você poderia elaborar um pouco mais, o que você está tentando alcançar? Os tipos de tabela são tão restritivos, que não consigo ver por que você está indo com isso.
Sebastian Meine
1
FYI eu escrevi sobre este sqlperformance.com/2013/11/t-sql-queries/single-tx-deadlock
Aaron Bertrand

Respostas:

15

Isso foi relatado nada menos que quatro vezes. Este foi fechado como corrigido:

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

Mas isso não era verdade. (Observe também a seção de soluções alternativas - a solução que sugeri nem sempre será aceitável.)

Este foi fechado como por design / não será corrigido:

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

Estes dois são mais novos e ainda ativos :

http://connect.microsoft.com/SQLServer/feedback/details/800919/ (agora fechado como não será corrigido )

http://connect.microsoft.com/SQLServer/feedback/details/804365/ (agora fechado como por design )

Até que a Microsoft possa se convencer do contrário, você precisará encontrar uma solução alternativa - basta ter todos os tipos implantados antes de executar seu teste ou dividi-lo em vários testes.

Tentarei obter confirmação de meus contatos sobre o que Umachandar quis dizer com corrigido no item mais antigo, porque obviamente isso entra em conflito com declarações posteriores.

ATUALIZAÇÃO # 1 (de, espero, exatamente 2)

O bug original (que foi fechado como corrigido) envolvia tipos de alias, mas não do tipo TABLE. Foi relatado no SQL Server 2005, que obviamente não tinha tipos de tabela e TVPs. Parece que a UC relatou que o bug com tipos de alias que não são de tabela foi corrigido com base em como eles lidam com transações internas, mas não cobriu um cenário semelhante posteriormente introduzido nos tipos de tabela. Ainda estou aguardando a confirmação de que o bug original deveria ter sido fechado como corrigido; Sugeri que todos os quatro fossem fechados como planejado. Isso ocorre em parte porque é do tipo que eu esperava que funcionasse, e em parte porque eu entendo pela UC que "corrigi-lo" para funcionar de uma maneira diferente é extremamente complexo, poderia quebrar a compatibilidade com versões anteriores e seria útil em alguns casos. número muito limitado de casos de uso. Nada contra você ou seu caso de uso, mas fora dos cenários de teste, eu '

ATUALIZAÇÃO # 2

Eu escrevi sobre esse problema no blog:

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

Aaron Bertrand
fonte
1

Eu era capaz de reproduzir isso. O gráfico de deadlock é bastante curioso:

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

Parece-me um bug e eu recomendo que você abra um item de conexão para ele.


Para solucionar seu problema imediato, você pode usar tSQLt.NewConnection(presumo que você esteja usando o 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

Ainda não entendo de onde vem a necessidade de criar um tipo de tabela rapidamente e presumo que você esteja complicando demais o teste. Envie-me um e-mail se você quiser discutir.

Sebastian Meine
fonte
2
Obrigado pela sua ajuda, Sebastian. Infelizmente, não estou usando o tSQLt. Você não entende de onde vem a necessidade de criar um tipo rapidamente, porque eu não expliquei isso. Não estou complicando demais as coisas, mas não sinto necessidade de demonstrá-las.
Michael J Swart
Bem, veja o código-fonte do tSQLt de como o tSQLt.NewConnection é implementado. É bastante simples e deve funcionar em sua estrutura também.
Sebastian Meine
1
Qualquer tentativa de criar o tipo e usá-lo na mesma transação resulta em impasse (veja meu relatório de erros, vinculado no post de Aaron - último link); essa solução alternativa não funcionará (bem, supondo que ela não faça algo estúpido como confirmar uma transação aberta antes de executar a instrução de entrada).
quer
-1

A menos que alguém saiba diferente, não acho que haja uma maneira de fazer isso em uma única transação. Eu não acho que isso seja um bug.

Primeiro, você precisa fazer um bloqueio de modificação de esquema (Sch-M) ao criar o tipo. Como você não confirma a transação, o bloqueio permanece aberto. Então você tenta declarar uma variável para esse tipo na mesma transação. Isso tenta usar um bloqueio de estabilidade do esquema (Sch-S). Esses dois tipos são incompatíveis simultaneamente no mesmo objeto. Como eles estão na mesma transação, o SQL o trata como um impasse, porque o Sch-S nunca pode ser concedido enquanto a transação está aberta.

Execute cada lote, um de cada vez, e selecione sys.dm_tran_locks assim que tentar declarar a variável. Você verá o mesmo processo segurando um Sch-M e aguardando um Sch-S no mesmo objeto.

dmmaxwell
fonte
3
Os tipos são incompatíveis, mas pensei que o mesmo processo não teria que esperar por si mesmo. Por exemplo, eu posso adicionar uma coluna a uma tabela e usá-la na mesma transação.
Michael J Swart
3
O gerenciador de bloqueios deve descobrir que o processo já está mantendo um bloqueio (realmente mais forte) no recurso e o processo não deve esperar. Afinal, você pode primeiro atualizar uma linha e depois lê-la novamente - na mesma situação.
Sebastian Meine