Могу ли я создать определенный пользователем тип таблицы и использовать его в той же транзакции?

13

Когда я выполню следующее (в студии управления 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?
Макс Вернон,
2
Я знал, что получу этот вопрос. Это часть интеграционного теста. Среда тестирования интеграции выполняет все в одной транзакции.
Майкл Дж Сварт
1
Очевидным обходным решением будет создание типов, необходимых для теста, перед его выполнением. Очевидно, что это не поможет вам автоматизировать тестирование.
Макс Вернон
@MichaelJSwart, не могли бы вы рассказать подробнее, чего вы пытаетесь достичь? Типы таблиц настолько ограничены, что я не могу понять, куда вы идете с этим.
Себастьян Майн
1
К вашему сведению, я писал в блоге об этом sqlperformance.com/2013/11/t-sql-queries/single-tx-deadlock
Аарон Бертран

Ответы:

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 не убедится в обратном, вам придется искать обходной путь - просто разверните все типы перед запуском теста или разбейте его на несколько тестов.

Я постараюсь получить подтверждение от моих контактов о том, что имел в виду Умачандар, зафиксированный в самом раннем пункте, потому что, очевидно, это противоречит последующим утверждениям.

ОБНОВЛЕНИЕ № 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. Вы не понимаете, откуда возникает необходимость в создании типа на лету, потому что я этого не объяснял. Я не слишком усложняю вещи, но я не чувствую необходимости демонстрировать это.
Майкл Дж Сварт
Хорошо, посмотрите на исходный код tSQLt того, как реализован tSQLt.NewConnection. Это довольно просто и должно работать в ваших рамках тоже.
Себастьян Майн
1
Любая попытка создать тип и затем использовать его в той же транзакции приводит к тупику (см. Мой отчет об ошибке, связанный с постом Аарона - последняя ссылка); этот обходной путь не сработает (при условии, что он не делает ничего глупого, например, совершает открытую транзакцию перед выполнением оператора ввода).
Джон Зигель
-1

Если кто-то не знает другого, я не думаю, что есть способ сделать это за одну транзакцию. Я не думаю, что это ошибка.

Во-первых, вам нужно взять блокировку модификации схемы (Sch-M) при создании типа. Поскольку вы не фиксируете транзакцию, блокировка остается открытой. Затем вы пытаетесь объявить переменную этого типа в той же транзакции. Это пытается взять схему стабильности схемы (Sch-S). Эти два типа несовместимы одновременно на одном и том же объекте. Поскольку они находятся в одной транзакции, SQL рассматривает это как тупик, потому что Sch-S никогда не может быть предоставлен, пока транзакция открыта.

Запускайте каждый пакет по одному и выбирайте sys.dm_tran_locks, как только вы попытаетесь объявить переменную. Вы увидите тот же процесс, удерживающий Sch-M и ожидающий Sch-S на том же объекте.

dmmaxwell
источник
3
Типы несовместимы, но я подумал, что тот же процесс не будет ждать сам по себе. Например, я могу добавить столбец в таблицу и затем использовать его в той же транзакции.
Майкл Дж Сварт
3
Менеджер блокировок должен выяснить, что процесс уже удерживает (на самом деле более сильную) блокировку на ресурсе, и процесс не должен ждать. В конце концов, вы можете сначала обновить строку, а затем снова прочитать ее - тоже самое.
Себастьян Майн