Masalah : apakah ada masalah yang diketahui dengan tipe tabel yang ditentukan pengguna sebagai parameter untuk sp_executesql ? Jawab - tidak, saya idiot.
Siapkan skrip
Script ini membuat masing-masing masing-masing tabel, prosedur dan tipe tabel yang ditentukan pengguna (hanya SQL Server 2008+).
Tujuan tumpukan adalah untuk memberikan audit bahwa ya, data membuatnya menjadi prosedur. Tidak ada kendala, tidak ada apa pun untuk mencegah data dimasukkan.
Prosedur ini mengambil sebagai parameter tipe tabel yang ditentukan pengguna. Semua proc tidak dimasukkan ke dalam tabel.
Tipe tabel yang ditentukan pengguna juga sederhana, hanya satu kolom
Saya telah menjalankan yang berikut melawan 11.0.1750.32 (X64)
dan 10.0.4064.0 (X64)
Ya, saya tahu kotak itu bisa ditambal, saya tidak mengendalikan itu.
-- 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
Masalah reproduksi
Script ini menunjukkan masalah dan harus memakan waktu lima detik untuk dieksekusi. Saya membuat dua contoh tipe tabel yang ditentukan pengguna dan kemudian mencoba dua cara yang berbeda untuk meneruskannya sp_executesql
. Pemetaan parameter dalam doa pertama meniru apa yang saya ambil dari SQL Profiler. Saya kemudian memanggil prosedur tanpa sp_executesql
bungkus.
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
Hasil : di bawah ini adalah hasil saya. Tabel awalnya kosong. Saya memancarkan waktu saat ini dan melakukan dua panggilan ke Internet sp_executesql
. Saya menunggu 5 detik untuk lulus dan memancarkan waktu saat ini diikuti dengan memanggil prosedur tersimpan itu sendiri dan akhirnya membuang tabel audit.
Seperti yang dapat Anda lihat dari stempel waktu, catatan untuk catatan B terkait dengan pemanggilan prosedur tersimpan langsung. Juga, tidak ada catatan 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
Runtuhkan skrip
Script ini menghapus objek dalam urutan yang benar (Anda tidak dapat menjatuhkan jenis sebelum referensi prosedur telah dihapus)
-- cleanup
DROP TABLE dbo.UDTT_holder
DROP PROCEDURE dbo.Repro
DROP TYPE dbo.UDTT
GO
Mengapa ini penting?
Kode tampaknya konyol tapi saya tidak punya banyak kendali atas penggunaan sp_execute vs panggilan "langsung" ke proc. Semakin tinggi rantai panggilan, saya menggunakan perpustakaan ADO.NET dan mengirimkan TVP seperti yang telah saya lakukan sebelumnya . Tipe parameter diatur dengan benar ke System.Data.SqlDbType.Structured dan CommandType diatur ke System.Data.CommandType.StoredProcedure .
Mengapa saya seorang noob (edit)
Rob dan Martin Smith melihat apa yang tidak saya lihat - pernyataan yang dikirimkan ke sp_executesql tidak menggunakan @MetricData
parameter. Parameter yang tidak dilass / dipetakan tidak akan digunakan.
Saya menerjemahkan kode parameter C # table-rated-parameter saya ke PowerShell dan karena dikompilasi, itu bukan kode yang salah; kecuali itu. Saat menulis pertanyaan ini, saya menyadari saya tidak mengatur CommandType
untuk StoredProcedure
jadi saya menambahkan baris itu dan re-lari kode tapi jejak di profiler tidak berubah sehingga aku menduga itu bukan masalah.
Cerita lucu - jika Anda tidak menyimpan file yang diperbarui, menjalankannya tidak akan membuat perbedaan. Saya masuk pagi ini dan menjalankannya kembali (setelah disimpan) dan ADO.NET menerjemahkannya EXEC dbo.Repro @MetricData=@p3
yang berfungsi dengan baik.
sumber
dbo.Repro
dan tidak menggunakan parameter yang diteruskan sama sekali.Saya tergoda untuk menghapus pertanyaan ini tetapi mengira orang lain mungkin akan mengalami keadaan yang serupa.
Penyebab utamanya adalah saya
$updateCommand
belum menetapkan CommandType. Nilai standarnya adalah Teks sehingga prosedur tersimpan saya dipanggil dengan benar. Parameter saya sedang dibuat dan diteruskan tetapi seperti dicatat dalam jawaban lain hanya karena parameter tersedia tidak berarti mereka sedang digunakan .Kode kalau-kalau ada yang tertarik dengan kesalahan saya
Menjalankan dengan nilai CommandType of Text akan membutuhkan solusi @ rob_farley untuk mengubah nilai
$updateQuery
menjadi "EXEC dbo.Repro @MetricData". Pada titik itu, sp_executesql akan memetakan nilai dengan benar dan umurnya baik.Berjalan dengan
CommandType
dariTableDirect
tidak didukung oleh Penyedia SqlClient Data, sebagai dokumentasi menunjukkan.Menggunakan
CommandType
dariStoredProcedure
diterjemahkan ke doa langsung dari prosedur yang tersimpan tanpasp_executesql
pembungkus dan karya besar.sumber