Vấn đề : có một vấn đề đã biết với các loại bảng do người dùng định nghĩa là tham số cho sp_executesql không? Trả lời - không, tôi là một thằng ngốc.
Thiết lập kịch bản
Kịch bản lệnh này tạo một trong mỗi loại bảng, thủ tục và loại bảng do người dùng xác định (chỉ giới hạn SQL Server 2008+).
Mục đích của heap là cung cấp một cuộc kiểm toán rằng có, dữ liệu đã đưa nó vào quy trình. Không có ràng buộc, không có gì để ngăn chặn dữ liệu được chèn vào.
Quy trình lấy làm tham số loại bảng do người dùng xác định. Tất cả các Proc làm là chèn vào bảng.
Kiểu bảng do người dùng định nghĩa cũng đơn giản, chỉ là một cột
Tôi đã chạy theo sau 11.0.1750.32 (X64)
và 10.0.4064.0 (X64)
Có, tôi biết hộp đó có thể được vá, tôi không kiểm soát được.
-- 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
Vấn đề sinh sản
Kịch bản này cho thấy vấn đề và sẽ mất năm giây để thực thi. Tôi tạo hai trường hợp loại bảng do người dùng xác định và sau đó thử hai phương tiện khác nhau để chuyển chúng vào sp_executesql
. Ánh xạ tham số trong lệnh gọi đầu tiên bắt chước những gì tôi đã thu được từ SQL Profiler. Sau đó tôi gọi thủ tục mà không có sp_executesql
trình bao bọc.
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
Kết quả : dưới đây là kết quả của tôi. Bàn ban đầu trống. Tôi phát ra thời gian hiện tại và thực hiện hai cuộc gọi đến sp_executesql
. Tôi đợi trong 5 giây để vượt qua và phát ra thời gian hiện tại, sau đó gọi chính thủ tục được lưu trữ và cuối cùng kết xuất bảng kiểm toán.
Như bạn có thể thấy từ dấu thời gian, bản ghi cho bản ghi B tương ứng với lời gọi thủ tục được lưu trữ thẳng. Ngoài ra, không có bản ghi 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
Xé kịch bản
Tập lệnh này xóa các đối tượng theo đúng thứ tự (bạn không thể loại bỏ trước khi tham chiếu của thủ tục đã bị xóa)
-- cleanup
DROP TABLE dbo.UDTT_holder
DROP PROCEDURE dbo.Repro
DROP TYPE dbo.UDTT
GO
Tại sao điều này lại quan trọng
Mã này có vẻ ngớ ngẩn nhưng tôi không có nhiều quyền kiểm soát việc sử dụng sp_execute so với cuộc gọi "thẳng" tới Proc. Nâng cao chuỗi cuộc gọi, tôi đang sử dụng thư viện ADO.NET và chuyển qua TVP như tôi đã làm trước đây . Kiểu của tham số được đặt chính xác thành System.Data.SqlDbType.Sturationured và CommandType được đặt thành System.Data.CommandType.StoredProcedure .
Tại sao tôi là một người mới (chỉnh sửa)
Rob và Martin Smith đã thấy những gì tôi không thấy - câu lệnh được truyền cho sp_executesql không sử dụng @MetricData
tham số. Một tham số bỏ qua / ánh xạ sẽ không được sử dụng.
Tôi đã dịch mã tham số có giá trị bảng C # đang hoạt động của mình sang PowerShell và vì nó được biên dịch, nó không thể là mã bị lỗi; ngoại trừ nó đã được. Trong khi viết lên câu hỏi này, tôi nhận ra rằng tôi đã không đặt CommandType
để StoredProcedure
nên tôi thêm vào dòng đó và tái chạy mã nhưng dấu vết trong hồ sơ không thay đổi vì vậy tôi cho rằng đó không phải là vấn đề.
Câu chuyện vui - nếu bạn không lưu tệp cập nhật, chạy nó sẽ không tạo ra sự khác biệt. Tôi đã nhận được vào sáng nay và chạy lại nó (sau khi lưu) và ADO.NET đã dịch nó để EXEC dbo.Repro @MetricData=@p3
hoạt động tốt.
dbo.Repro
và hoàn toàn không sử dụng thông số đã truyền.