問題:sp_executesqlへのパラメーターとしてユーザー定義のテーブルタイプに既知の問題がありますか?回答-いいえ、私はばかです。
スクリプトを設定する
このスクリプトは、テーブル、プロシージャ、ユーザー定義のテーブルタイプをそれぞれ1つ作成します(制限付きの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
問題の再現
このスクリプトは問題を示しており、実行に5秒かかるはずです。ユーザー定義のテーブルタイプのインスタンスを2つ作成し、それらをに渡す2つの異なる方法を試しsp_executesql
ます。最初の呼び出しのパラメーターマッピングは、SQLプロファイラーからキャプチャしたものを模倣しています。次に、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
結果:以下は私の結果です。テーブルは最初は空です。現在の時刻を出力し、を2回呼び出します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に設定されます。
なぜ私は初心者です(編集)
RobとMartin Smithは私が見ていなかったものを見ました-sp_executesqlに渡されるステートメントは@MetricData
パラメーターを使用しませんでした。渡されていない/マップされたパラメーターは使用されません。
私は作業中のC#のテーブル値パラメーターコードをPowerShellに変換していましたが、コンパイルされたため、問題のあるコードにはなりませんでした。それ以外だった。この質問を書いているときに、を設定していないことに気付いCommandType
たStoredProcedure
ので、その行を追加してコードを再実行しましたが、プロファイラーのトレースは変更されなかったので、問題ではないと思いました。
面白い話- 更新されたファイルを保存しなくても、実行しても違いはありません。私は今朝入って、それを(保存後に)再実行し、ADO.NETはそれを適切に翻訳して翻訳しましEXEC dbo.Repro @MetricData=@p3
た。
dbo.Repro
、渡されたパラメーターをまったく使用しません。