ユーザー定義のテーブルタイプを使用したSP_EXECUTESQLは正しく動作しません
-
16-10-2019 - |
質問
問題: :パラメーターとしてユーザー定義のテーブルタイプに既知の問題がありますか SP_EXECUTESQL?答え - いいえ、私はばかです。
スクリプトを設定します
このスクリプトは、テーブル、手順、およびユーザー定義のテーブルタイプのそれぞれを作成します(制限付きSQL Server 2008+のみ)。
ヒープの目的は、はい、データが手順になったという監査を提供することです。制約はなく、データが挿入されないようにすることはありません。
この手順では、ユーザー定義のテーブルタイプをパラメーターとして使用します。 Procが行うのはすべてテーブルに挿入されます。
ユーザー定義のテーブルタイプもシンプルで、1つの列だけです
私は以下を実行しました 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.
なぜ私がnoobなのか(編集)
ロブとマーティン・スミスは私が見ていないものを見ました - sp_executesqlに渡される声明は使用しませんでした @MetricData
パラメーター。未払い/マッピングされたパラメーターは使用されません。
私は動作しているC#table-valued-parameterコードをPowerShellに翻訳していましたが、コンパイルして以来、 できませんでした 障害のあるコードになります。それを除いて。この質問を書いている間、私は自分が設定していなかったことに気付きました CommandType
に StoredProcedure
そのため、そのラインを追加してコードを再実行しましたが、プロファイラーのトレースは変更されなかったので、それが問題ではないと思いました。
面白い話 - そうでないなら 保存する 更新されたファイル、実行すると違いはありません。私は今朝に行き、それを再ラン(保存後)とado.netをに翻訳しました EXEC dbo.Repro @MetricData=@p3
うまく機能します。
解決
sp_executesql
アドホックT-SQLを実行するためです。だからあなたは試してみるべきです:
EXECUTE sp_executesql N'exec dbo.Repro @MetricData',N'@MetricData dbo.UDTT READONLY',@MetricData=@p3
他のヒント
私はこの質問を削除するように誘惑されましたが、他の誰かが同様の状況に遭遇する可能性があると考えました。
根本的な原因は私のものでした $updateCommand
CommandTypeを設定していませんでした。デフォルト値はテキストであるため、ストアドプロシージャは正しく呼ばれていました。私のパラメーターは作成されて渡されていましたが、パラメーターが 利用可能 彼らがいるという意味ではありません 使用済み.
誰かが私の間違いに興味を持っていた場合のコード
$updateConnection = New-Object System.Data.SqlClient.SqlConnection("Data Source=localhost\localsqla;Initial Catalog=baseline;Integrated Security=SSPI;")
$updateConnection.Open()
$updateCommand = New-Object System.Data.SqlClient.SqlCommand($updateQuery)
$updateCommand.Connection = $updateConnection
# This is what I was missing
$updateCommand.CommandType = [System.Data.CommandType]::StoredProcedure
#$updateCommand.CommandType = [System.Data.CommandType]::Text
#$updateCommand.CommandType = [System.Data.CommandType]::TableDirect
$DataTransferFormat = $updateCommand.Parameters.AddWithValue("@MetricData", $dataTable)
$DataTransferFormat.SqlDbType = [System.Data.SqlDbType]::Structured
$DataTransferFormat.TypeName = $udtt
$results = $updateCommand.ExecuteNonQuery()
テキストのコマンドタイプの値で実行するには、 @rob_farleyの値を変更するというソリューションが必要です $updateQuery
「exec dbo.repro @metricdata」へ。その時点で、SP_EXECUTESQLは値を正しくマッピングし、人生は良好です。
で実行します CommandType
の TableDirect
ドキュメントが示すように、SQLClientデータプロバイダーによってサポートされていません。
を使って CommandType
の StoredProcedure
ストアドプロシージャのない直接の呼び出しに変換されます sp_executesql
ラッパーとうまく機能します。