ปัญหา : มีปัญหาที่ทราบเกี่ยวกับประเภทตารางที่ผู้ใช้กำหนดเป็นพารามิเตอร์สำหรับsp_executesqlหรือไม่ คำตอบ - ไม่ฉันเป็นคนงี่เง่า
ตั้งค่าสคริปต์
สคริปต์นี้สร้างหนึ่งในแต่ละตารางขั้นตอนและประเภทตารางที่ผู้ใช้กำหนด (จำกัด SQL Server 2008+ เท่านั้น)
จุดประสงค์ของกองคือเพื่อให้การตรวจสอบว่าใช่ข้อมูลทำให้เป็นขั้นตอน ไม่มีข้อ จำกัด ไม่มีสิ่งใดที่จะป้องกันไม่ให้มีการแทรกข้อมูล
ขั้นตอนใช้เป็นพารามิเตอร์ที่ผู้ใช้กำหนดประเภทตาราง proc ทั้งหมดจะถูกแทรกลงในตาราง
ประเภทตารางที่ผู้ใช้กำหนดนั้นเรียบง่ายเช่นกันเพียงแค่คอลัมน์เดียว
ฉันเรียกใช้สิ่งต่อไปนี้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
ทำไมฉันถึงเป็น noob (แก้ไข)
Rob และ Martin Smith เห็นสิ่งที่ฉันไม่เห็น - คำสั่งที่ส่งผ่านไปยัง sp_executesql ไม่ได้ใช้@MetricData
พารามิเตอร์ พารามิเตอร์ที่ไม่ผ่าน / แมปจะไม่ถูกใช้
ฉันแปลโค้ดพารามิเตอร์มูลค่าตารางการทำงาน C # ของฉันเป็น PowerShell และเมื่อรวบรวมมันก็ไม่สามารถเป็นรหัสที่ผิดได้ ยกเว้นมันเป็น ในขณะที่เขียนคำถามนี้ผมรู้ว่าผมไม่ได้ตั้งค่าCommandType
ไปStoredProcedure
ดังนั้นฉันเพิ่มบรรทัดที่และอีกครั้งวิ่งรหัส แต่ร่องรอยใน Profiler ไม่เปลี่ยนแปลงดังนั้นผมจึงคิดว่าไม่ได้เป็นปัญหา
เรื่องตลก - หากคุณไม่บันทึกไฟล์ที่อัปเดตการรันมันจะไม่สร้างความแตกต่าง ฉันได้รับในเช้านี้และวิ่งอีกครั้ง (หลังจากบันทึก) และ ADO.NET แปลว่ามันใช้EXEC dbo.Repro @MetricData=@p3
งานได้ดี
dbo.Repro
และไม่ใช้พารามิเตอร์ส่งผ่านเลย