Проблема : чи відома проблема із визначеними користувачем типами таблиць як параметрів sp_executesql ? Відповідь - ні, я ідіот.
Налаштування сценарію
Цей скрипт створює один із таблиць, процедур та визначених користувачем типів таблиці (обмежений лише для SQL Server 2008+).
Мета купи - перевірити, що так, дані внесли це в процедуру. Немає жодних обмежень і нічого, що заважає вставляти дані.
Процедура приймає за параметр тип таблиці, визначений користувачем. Все, що робиться, вставити в таблицю.
Користувальницький тип таблиці також є простою, лише один стовпець
Я запустив наступне проти, 11.0.1750.32 (X64)
і 10.0.4064.0 (X64)
так, я знаю, що поле може бути зафіксовано, я не контролюю це.
-- this table record that something happened
CREATE TABLE dbo.UDTT_holder
(
ServerName varchar(200)
, insert_time datetime default(current_timestamp)
)
GO
-- user defined table type transport mechanism
CREATE TYPE dbo.UDTT
AS TABLE
(
ServerName varchar(200)
)
GO
-- stored procedure to reproduce issue
CREATE PROCEDURE dbo.Repro
(
@MetricData dbo.UDTT READONLY
)
AS
BEGIN
SET NOCOUNT ON
INSERT INTO dbo.UDTT_holder
(ServerName)
SELECT MD.* FROM @MetricData MD
END
GO
Проблемне відтворення
Цей скрипт демонструє проблему, і на його виконання знадобиться п'ять секунд. Я створюю два екземпляри моїх визначених користувачем типів таблиць, а потім пробую два різні засоби передачі їх у sp_executesql
. Зображення параметрів у першій виклику імітує те, що я захопив у SQL Profiler. Потім я називаю процедуру без sp_executesql
обгортки.
SET NOCOUNT ON
DECLARE
@p3 dbo.UDTT
, @MetricData dbo.UDTT
INSERT INTO @p3 VALUES(N'SQLB\SQLB')
INSERT INTO @MetricData VALUES(N'SQLC\SQLC')
-- nothing up my sleeve
SELECT * FROM dbo.UDTT_holder
SELECT CONVERT(varchar(24), current_timestamp, 121) + ' Firing sp_executesql' AS commentary
-- This does nothing
EXECUTE sp_executesql N'dbo.Repro',N'@MetricData dbo.UDTT READONLY',@MetricData=@p3
-- makes no matter if we're mapping variables
EXECUTE sp_executesql N'dbo.Repro',N'@MetricData dbo.UDTT READONLY',@MetricData
-- Five second delay
waitfor delay '00:00:05'
SELECT CONVERT(varchar(24), current_timestamp, 121) + ' Firing proc' AS commentary
-- this does
EXECUTE dbo.Repro @p3
-- Should only see the latter timestamp
SELECT * FROM dbo.UDTT_holder
GO
Результати : нижче мої результати. Таблиця спочатку порожня. Я випромінюю поточний час і здійснюю два дзвінки на sp_executesql
. Я чекаю 5 секунд, щоб пройти та випромінювати поточний час з подальшим викликом самої збереженої процедури і, нарешті, скинути таблицю аудиту.
Як видно з мітки часу, запис для запису B відповідає прямому виклику збереженої процедури. Також немає запису SQLC.
ServerName insert_time
------------------------------------------------------------------------
commentary
-------------------------------------------------
2012-02-02 13:09:05.973 Firing sp_executesql
commentary
-------------------------------------------------
2012-02-02 13:09:10.983 Firing proc
ServerName insert_time
------------------------------------------------------------------------
SQLB\SQLB 2012-02-02 13:09:10.983
Розрушити сценарій
Цей скрипт видаляє об'єкти у правильному порядку (ти не можеш скинути тип до видалення посилання процедури)
-- cleanup
DROP TABLE dbo.UDTT_holder
DROP PROCEDURE dbo.Repro
DROP TYPE dbo.UDTT
GO
Чому це має значення
Код здається нерозумним, але я не маю особливого контролю над використанням sp_execute проти "прямого" дзвінка до проц. Вище в ланцюзі викликів я використовую бібліотеку ADO.NET і передаю TVP, як я це робив раніше . Тип параметра правильно встановлено на System.Data.SqlDbType.Structured, а CommandType встановлено на System.Data.CommandType.StoredProcedure .
Чому я нуб (редагувати)
Роб і Мартін Сміт побачили те, чого я не бачив - у заяві, переданому sp_executesql, @MetricData
параметр не використовується . Неперевершений / зіставлений параметр не збирається використовувати.
Я перекладав свій робочий код параметра C #-значення-параметр в PowerShell, і оскільки він склався, він не міг бути кодом з вини; хіба що було. Під час написання цього питання, я зрозумів , що ні встановити CommandType
на StoredProcedure
так що я додав цю лінію і знову побіг код , але слід в профайлер не змінилися , тому я припустив , що була проблемою.
Смішна історія - якщо ви не збережете оновлений файл, запуск його не змінить. Я потрапив сьогодні вранці і повторно запустив його (після збереження), і ADO.NET переклав його, EXEC dbo.Repro @MetricData=@p3
що працює чудово.
dbo.Repro
і зовсім не використовує передані параметри.