Проблема : есть известная проблема с определяемыми пользователем типами таблиц в качестве параметров 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 против «прямого» вызова proc. Выше цепочки вызовов я использую библиотеку 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
и не использует переданные параметры вообще.Я хотел удалить этот вопрос, но подумал, что кто-то еще может столкнуться с подобными обстоятельствами.
Основной причиной было то, что я
$updateCommand
не установил CommandType. Значением по умолчанию является Text, поэтому моя хранимая процедура вызывалась правильно. Мои параметры создавались и передавались, но, как отмечалось в других ответах, только то, что параметры доступны , не означает, что они используются .Код на случай, если кто-то заинтересовался моей ошибкой
Запуск со значением CommandType of Text потребует решения @ rob_farley об изменении значения
$updateQuery
на «EXEC dbo.Repro @MetricData». В этот момент sp_executesql правильно отобразит значения, и жизнь будет хорошей.Работа с параметром
CommandType
ofTableDirect
не поддерживается провайдером данных SqlClient, как указано в документации.Использование
CommandType
ofStoredProcedure
приводит к прямому вызову хранимой процедуры безsp_executesql
оболочки и прекрасно работает.источник