उपयोगकर्ता परिभाषित तालिका प्रकार के साथ sp_executesql सही ढंग से व्यवहार नहीं कर रहा है


11

समस्या : क्या sp_executesql के मापदंडों के रूप में उपयोगकर्ता परिभाषित टेबल प्रकार के साथ एक ज्ञात समस्या है ? जवाब - नहीं, मैं एक बेवकूफ हूं।

स्क्रिप्ट सेट करें

यह स्क्रिप्ट प्रत्येक तालिका, प्रक्रिया और उपयोगकर्ता परिभाषित तालिका प्रकार (प्रतिबंधित SQL सर्वर 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

समस्या प्रजनन

यह स्क्रिप्ट समस्या को प्रदर्शित करती है और निष्पादित करने में पाँच सेकंड का समय लेना चाहिए। मैं अपने उपयोगकर्ता परिभाषित तालिका प्रकारों के दो उदाहरण बनाता हूं और फिर उन्हें में पास करने के दो अलग-अलग साधनों का प्रयास करता हूं 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 सेकंड के लिए प्रतीक्षा करता हूं और वर्तमान समय का उत्सर्जन करता हूं, इसके बाद संग्रहीत कार्यविधि को स्वयं कॉल करता हूं और अंत में ऑडिट टेबल को डंप करता हूं।

जैसा कि आप टाइमस्टैम्प से देख सकते हैं, बी रिकॉर्ड के लिए रिकॉर्ड सीधे संग्रहीत प्रक्रिया के आह्वान से मेल खाती है। इसके अलावा, कोई 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 बनाम "सीधा" कॉल के उपयोग पर नियंत्रण के लिए बहुत अधिक नियंत्रण नहीं है। कॉल श्रृंखला से ऊपर, मैं ADO.NET लाइब्रेरी का उपयोग कर रहा हूं और टीवीपी में गुजर रहा हूं जैसा कि मैंने पहले किया है । पैरामीटर्स का प्रकार सही ढंग से System.Data.SqlDbType.Structured पर सेट है और CommandType System.Data.CommandType.StoredProcedure पर सेट है ।

मैं एक noob क्यों हूँ (संपादित करें)

रॉब और मार्टिन स्मिथ ने देखा कि मैं क्या नहीं देख रहा था - sp_executesql को दिए गए बयान ने @MetricDataपैरामीटर का उपयोग नहीं किया । एक नायाब / मैप किया गया पैरामीटर उपयोग में नहीं आने वाला है।

मैं अपना कार्य C # टेबल-वेल्यू-पैरामीटर कोड PowerShell में अनुवाद कर रहा था और चूंकि यह संकलित है, इसलिए यह गलती पर कोड नहीं हो सकता है; सिवाय इसके था। इस सवाल का लिखाई है, मुझे एहसास हुआ कि मैं सेट नहीं था CommandTypeकरने के लिए StoredProcedureतो मैं उस लाइन को जोड़ा और कोड को फिर से भाग गया लेकिन तो मैं मान लिया है कि मुद्दा नहीं था प्रोफाइलर में ट्रेस नहीं बदला।

मजेदार कहानी - अगर आप अपडेटेड फाइल को सेव नहीं करते हैं , तो इसे चलाने से कोई फर्क नहीं पड़ेगा। मैं आज सुबह मिला और इसे (बचत करने के बाद) फिर से चलाया और ADO.NET ने इसका अनुवाद किया EXEC dbo.Repro @MetricData=@p3जो ठीक काम करता है।

जवाबों:


10

sp_executesqlएड-हॉक टी-एसक्यूएल को निष्पादित करने के लिए है। इसलिए आपको प्रयास करना चाहिए:

EXECUTE sp_executesql N'exec dbo.Repro @MetricData',N'@MetricData dbo.UDTT READONLY',@MetricData=@p3

1
+1 वर्तमान में ओपी में लिपि में केवल कथन है dbo.Reproऔर पारित मापदंडों का बिल्कुल भी उपयोग नहीं करता है।
मार्टिन स्मिथ

Yesssss, मैंने रोब की टिप्पणी देखी और जब इसमें EXEC भाग की आवश्यकता नहीं थी, तो यह समझ में आया कि कॉल के साथ पैरामीटर का उपयोग नहीं किया जा रहा था क्योंकि स्क्रिप्ट में पैरामीटर को संदर्भित नहीं किया गया था। मेरी मूर्खता को दर्शाने के लिए टिकट अपडेट करना
बिलिंक

@billinkc - आह मैंने अभी और विस्तृत विवरण के साथ एक उत्तर जोड़ा, फिर आपकी टिप्पणी देखी। मैं उस उत्तर को थोड़ा सा हटा दूंगा।
मार्टिन स्मिथ

सही। आपने उल्लेख नहीं किया था कि आप यह ado.net से कर रहे हैं। sp_executesql एक .CommandText, not .StoredProcedure के बराबर है।
रॉब फ़र्ले

3

मुझे इस प्रश्न को हटाने के लिए लुभाया गया, लेकिन लगा कि कोई और भी इसी तरह की परिस्थिति में भाग सकता है।

मूल कारण यह था कि मैंने $updateCommandCommandType सेट नहीं किया था। डिफ़ॉल्ट मान पाठ है इसलिए मेरी संग्रहीत प्रक्रिया को सही ढंग से कहा जा रहा था। मेरे पैरामीटर बनाए और पास किए जा रहे थे, लेकिन जैसा कि अन्य उत्तरों में उल्लेख किया गया है क्योंकि पैरामीटर उपलब्ध हैं इसका मतलब यह नहीं है कि उनका उपयोग किया जा रहा है

अगर किसी को मेरी गलती में दिलचस्पी थी तो कोड

    $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()

पाठ के CommandType के मान के साथ चलने के $updateQueryलिए " rob_farley के समाधान को" EXEC dbo.Repro @MetricData " के मान को बदलने की आवश्यकता होगी । उस बिंदु पर, sp_executesql मूल्यों को सही ढंग से मैप करेगा और जीवन अच्छा है।

एक CommandTypeके साथ चल रहा TableDirectहै SqlClient डेटा प्रदाता द्वारा समर्थित नहीं है, जैसा कि दस्तावेज़ इंगित करता है।

एक का उपयोग CommandTypeके StoredProcedureबिना संग्रहीत प्रक्रिया का एक सीधा मंगलाचरण के लिए अनुवाद sp_executesqlआवरण और महान काम करता है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.