SqlCommand.Prepare () का उपयोग करने का क्या अर्थ और लाभ है?


14

मैं डेवलपर कोड में आया था, जहां SqlCommand.Prepare () (MSDN देखें) विधि का उपयोग बड़े पैमाने पर SQL प्रश्नों के निष्पादन के लिए किया जाता है। और मुझे आश्चर्य है कि इसका क्या लाभ है?

नमूना:

command.Prepare();
command.ExecuteNonQuery();
//...
command.Parameters[0].Value = 20;
command.ExecuteNonQuery();

मैंने थोड़ा-बहुत खेला है और पता लगाया है। कॉल करने के बाद कमांड का निष्पादनPrepare()विधि को Sql सर्वर निम्नलिखित कथन को निष्पादित करता है:

declare @p1 int
set @p1=1
exec sp_prepexec @p1 output,N'@id int,@desc text',N'INSERT INTO dbo.testtable (id) VALUES (@id)',@id=20'
select @p1

इसके बाद जब Parameter को इसकी वैल्यू मिलती है और SqlCommand.ExecuteNonQuery() उसे कॉल किया जाता है, तो निम्न Sql-Server पर निष्पादित हो जाता है:

exec sp_execute 1,@id=20

मेरे लिए यह ऐसा लगता है कि जैसे ही कथन Prepare()निष्पादित किया जाता है, वैसे ही संकलित हो जाता है। मुझे आश्चर्य है कि इसका क्या लाभ है? क्या इसका मतलब यह है कि इसे योजना कैश में डाल दिया गया है और वांछित पैरामीटर मानों के साथ अंतिम क्वेरी निष्पादित होते ही इसे फिर से उपयोग किया जा सकता है?

मुझे पता चला (और इसे एक अन्य प्रश्न में प्रलेखित किया गया है ) कि SqlCommands जिसे SqlParameters के साथ निष्पादित किया जाता है, हमेशा इसमें लपेटा जाता हैsp_executesql प्रक्रिया कॉल । यह Sql सर्वर को पैरामीटर मानों से स्वतंत्र योजनाओं को संग्रहीत और पुन: उपयोग करने में सक्षम बनाता है।

इस बारे में मुझे आश्चर्य है कि क्या prepare()विधि बेकार या अप्रचलित है या यदि मैं यहां कुछ याद कर रहा हूं?


मैंने हमेशा सोचा था कि SQL इंजेक्शन को रोकने के साथ इसका कुछ करना है, इसलिए आप कुछ प्रकारों के कुछ चर को स्वीकार करने के लिए क्वेरी तैयार करेंगे। इस तरह यदि आप उन प्रकारों के चर में पास नहीं होते हैं, तो प्रश्न विफल हो जाता है
मार्क सिनकिन्सन

हालांकि यह कहते हुए कि, यह PHP स्पष्टीकरण शायद .NET php.net/manual/en/pdo.prepared-statements.php के
मार्क सिनकिन्सन

"हाँ, सर। ड्राइवर, बाहर निकलने की तैयारी करो।" "तुम क्या तैयारी कर रहे हो? तुम हमेशा तैयारी कर रहे हो! बस जाओ!"
जॉन ऑफ ऑल ट्रेड्स

जवाबों:


19

तैयार किए गए SQL बैच को निष्पादित करने से अलग एक SQL बैच तैयार करना एक निर्माण है जो प्रभावी रूप से ** है SQL सर्वर के लिए बेकार है, यह देखते हुए कि निष्पादन योजनाओं को कैसे कैश किया जाता है। तैयारी के चरणों को अलग करना (पार्स करना, किसी भी पैरामीटर को बांधना और संकलन करना) और निष्पादन केवल तभी समझ में आता है जब कोई कैशिंग नहीं होता है। उद्देश्य मौजूदा योजना का पुन: उपयोग करके पार्सिंग और संकलन पर खर्च किए गए समय को बचाना है, और यही आंतरिक योजना कैश करती है। लेकिन सभी RDBMS इस तरह के कैशिंग (इन कार्यों के अस्तित्व से स्पष्ट रूप से) नहीं करते हैं और इसलिए यह ग्राहक कोड के लिए छोड़ दिया जाता है ताकि यह अनुरोध किया जा सके कि एक योजना "कैश्ड" हो, और इसलिए उस कैश आईडी को सहेजें, और फिर से उपयोग करें यह। यह ग्राहक कोड पर अतिरिक्त बोझ है (और प्रोग्रामर को इसे करने के लिए याद रखना और इसे ठीक करना) जो अनावश्यक है। असल में,IDbCommand.Prepare () विधि राज्यों के :

सर्वर स्वचालित रूप से आवश्यक के रूप में पुन: उपयोग के लिए योजना को कैश करता है; इसलिए, इस विधि को सीधे आपके क्लाइंट एप्लिकेशन में कॉल करने की आवश्यकता नहीं है।

यह ध्यान दिया जाना चाहिए कि, जहाँ तक मेरा परीक्षण दिखाता है (जो कि आप प्रश्न में दिखाते हैं), SqlCommand.Prepare () को कॉल करते हुए, एक "तैयारी" नहीं करता है- केवल ऑपरेशन: यह कॉल करता है sp_prepexec - SQL को तैयार और निष्पादित करता है ; यह sp_prepare को कॉल नहीं करता है जो केवल पार्स है और संकलन करता है (और किसी भी पैरामीटर मान में नहीं लेता है, केवल उनके नाम और डेटाटिप्स)। इसलिए, कॉल करने का कोई "पूर्व-संकलन" लाभ नहीं हो सकता है SqlCommand.Prepareक्योंकि यह एक तत्काल निष्पादन करता है (लक्ष्य को निष्पादन को स्थगित करना है)। फिर भी , SqlCommand.Prepare()कॉल sp_prepexecकरने का एक दिलचस्प संभावित मामूली लाभ है , भले ही वह इसके बजाय कॉल करता हो sp_prepare। कृपया नोट देखें (** ) विवरण के लिए नीचे।

मेरे परीक्षण से पता चलता है कि जब SqlCommand.Prepare()भी कॉल किया जाता है (और कृपया ध्यान दें कि यह कॉल SQL सर्वर पर कोई आदेश जारी नहीं करता है ), ExecuteNonQueryया ExecuteReaderजो इस प्रकार पूरी तरह से निष्पादित होता है, और यदि यह ExecuteReader है, तो यह पंक्तियों को लौटाता है। फिर भी, SQL सर्वर प्रोफाइलर दिखाता है कि SqlCommand.Execute______()कॉल के बाद पहला कॉल SqlCommand.Prepare()"SQL तैयार करें" ईवेंट के .Execute___()रूप में पंजीकृत होता है , और बाद में कॉल "Exec Ready SQL" ईवेंट के रूप में रजिस्टर होता है।


टेस्ट कोड

इसे PasteBin पर: http://pastebin.com/Yc2Tfvup पर अपलोड किया गया है । कोड एक .NET / C # कंसोल ऐप बनाता है, जो एक SQL Server Profiler ट्रेस चल रहा है (या एक विस्तारित ईवेंट सत्र) चलाने के लिए अभिप्रेत है। यह प्रत्येक चरण के बाद रुक जाता है इसलिए यह स्पष्ट हो जाएगा कि किन बयानों का एक विशेष प्रभाव है।


अपडेट करें

अधिक जानकारी मिली, और कॉल से बचने के लिए एक मामूली संभावित कारण SqlCommand.Prepare()। एक बात जो मैंने अपने परीक्षण में देखी, और उस परीक्षण कंसोल ऐप को चलाने वाले किसी के लिए क्या ध्यान दिया जाना चाहिए: कोई स्पष्ट कॉल नहीं किया गया sp_unprepare। मैंने कुछ खुदाई की और MSDN मंचों में निम्नलिखित पोस्ट पाया:

SqlCommand - तैयार करने के लिए या नहीं?

स्वीकृत उत्तर में जानकारी का एक समूह होता है, लेकिन मुख्य बिंदु निम्न हैं:

  • ** क्या तैयारी वास्तव में बचाता है आप मुख्य रूप से तार पर क्वेरी स्ट्रिंग को प्रसारित करने में लगने वाला समय है।
  • तैयार क्वेरी में कैश्ड प्लान के सेव होने की कोई गारंटी नहीं है, और यह सर्वर तक पहुँचने के बाद किसी ऐड-हॉक क्वेरी से तेज़ नहीं है।
  • RPC की कमांड (CommandType.StoredProcedure) तैयारी से कुछ भी हासिल नहीं करती है।
  • सर्वर पर, एक तैयार किया गया हैंडल मूल रूप से एक इन-मेमोरी मैप में एक इंडेक्स होता है जिसमें निष्पादित करने के लिए TSQL होता है।
  • नक्शा प्रति-कनेक्शन के आधार पर संग्रहीत किया जाता है, कोई क्रॉस-कनेक्शन उपयोग संभव नहीं है।
  • क्लाइंट द्वारा पूल से कनेक्शन का पुन: उपयोग करने पर भेजा गया रीसेट मैप को साफ करता है।

मैंने SQLCAT टीम से निम्न पोस्ट भी पाया, जो संबंधित प्रतीत होता है, लेकिन यह केवल ODBC के लिए एक समस्या हो सकती है जबकि SqlClient SqlConnection के निपटान पर सही ढंग से सफाई करता है। यह कहना मुश्किल है क्योंकि SQLCAT पोस्ट में किसी भी अतिरिक्त परीक्षण का उल्लेख नहीं है जो इसे एक कारण के रूप में साबित करने में मदद करेगा, जैसे कि कनेक्शन पूल को साफ करना, आदि।

उन तैयार किए गए एसक्यूएल बयानों को देखें


निष्कर्ष

मान लीजिये:

  • कॉलिंग का एकमात्र वास्तविक लाभ यह SqlCommand.Prepare()प्रतीत होता है कि आपको नेटवर्क पर फिर से क्वेरी टेक्स्ट सबमिट करने की आवश्यकता नहीं है,
  • कनेक्शन की मेमोरी (यानी SQL सर्वर मेमोरी) के हिस्से के रूप में क्वेरी टेक्स्ट को कॉल sp_prepareऔर sp_prepexecस्टोर करना

मैं कॉल करने के खिलाफ सिफारिश करूंगा SqlCommand.Prepare()क्योंकि केवल संभावित लाभ नेटवर्क पैकेट को बचा रहा है जबकि नकारात्मक पक्ष अधिक सर्वर मेमोरी ले रहा है। जबकि अधिकांश मामलों में खपत की गई मेमोरी संभवतः बहुत कम है, नेटवर्क बैंडविड्थ शायद ही कभी एक समस्या है क्योंकि अधिकांश डीबी सर्वर सीधे 10 मेगाबिट न्यूनतम (अधिक संभावना 100 मेगाबिट या गीगाबिट इन दिनों) कनेक्शन पर ऐप सर्वर से जुड़े होते हैं ( और कुछ एक ही बॉक्स पर भी ;-) यह और मेमोरी लगभग हमेशा नेटवर्क बैंडविड्थ की तुलना में अधिक दुर्लभ संसाधन है।

मुझे लगता है, सॉफ्टवेयर लिखने वाले किसी भी व्यक्ति के लिए, जो कई डेटाबेस से जुड़ सकता है, फिर एक मानक इंटरफ़ेस की सुविधा इस लागत / लाभ विश्लेषण को बदल सकती है। लेकिन उस मामले में भी, मेरा मानना ​​है कि "मानक" DB इंटरफ़ेस को अमूर्त करना काफी आसान है जो विभिन्न प्रदाताओं के लिए अनुमति देता है (और इसलिए उनके बीच अंतर, जैसे कॉल करने की आवश्यकता नहीं है Prepare()) यह देखते हुए कि ऐसा करना मूल वस्तु है ओरिएंटेड प्रोग्रामिंग जो आप अपने ऐप कोड में पहले से कर रहे हैं ;-)।


इस व्यापक उत्तर के लिए धन्यवाद। मैंने आपके चरणों का परीक्षण किया और आपकी अपेक्षाओं / परिणामों से दो विपथन थे: चरण 4 में मुझे एक अतिरिक्त परिणाम मिला। चरण 10 में मुझे चरण 2 की तुलना में एक और plan_handle मिला है
Magier

1
@ मैगियर चरण 4 के बारे में निश्चित नहीं है ... शायद आपके पास अलग सेट विकल्प थे या किसी तरह दोनों के बीच क्वेरी पाठ अलग था? यहां तक ​​कि एक एकल चरित्र (दृश्य या नहीं) अंतर एक अलग योजना होगी। इस वेब पेज से कॉपी और पेस्ट करने से मदद नहीं मिल सकती है, इसलिए क्वेरी (सिंगल कोट्स के बीच सब कुछ) sp_prepareको कॉल से sp_executesql, बस सुनिश्चित करने के लिए कॉपी करें। चरण 10 के लिए, हाँ, मैंने कल अधिक परीक्षण किया और कुछ अवसरों के लिए अलग-अलग हैंडल के साथ देखा sp_prepare, लेकिन फिर भी कभी भी ऐसा नहीं हुआ sp_executesql। मैं एक और चीज का परीक्षण करूंगा और अपने उत्तर को अपडेट करूंगा।
सोलोमन रटज़की

1
@ मैगियर वास्तव में, परीक्षण मैं इसे कवर करने से पहले चला गया था और मुझे नहीं लगता है कि योजना का कोई भी सही पुन: उपयोग है, विशेष रूप से उस MSDN मंच पोस्ट के प्रकाश में यह कहते हुए कि यह सिर्फ SQL को कैश करता है। मैं अभी भी उत्सुक हूं कि यह कैसे plan_handle का फिर से उपयोग करने में सक्षम है जबकि sp_executesqlनहीं या नहीं कर सकता। लेकिन परवाह किए बिना, पार्स समय अभी भी है sp_prepareअगर योजना को कैश से हटा दिया गया है तो मैं अपने उत्तर के उस हिस्से (दिलचस्प लेकिन अप्रासंगिक) को हटा दूंगा। यह हिस्सा वैसे भी एक दिलचस्प पक्ष नोट का अधिक था क्योंकि यह आपके प्रत्यक्ष प्रश्न को संबोधित नहीं करता था। बाकी सब अभी भी लागू होता है।
सोलोमन रटज़की

1
इस तरह की व्याख्या मैं देख रहा था।
CSharpie

5

इस बारे में मुझे आश्चर्य है कि क्या तैयारी () पद्धति बेकार या अप्रचलित है या यदि मैं यहां कुछ याद कर रहा हूं?

मैं कहूंगा कि तैयार विधि SqlCommand दुनिया सीमित मूल्य है, नहीं है कि यह पूरी तरह से बेकार है। एक लाभ यह है कि तैयार IDbCommand इंटरफ़ेस का हिस्सा है जो SqlCommand लागू करता है। यह समान कोड को अन्य डीबीएमएस प्रदाताओं के खिलाफ चलाने की अनुमति देता है (जिसे तैयारी की आवश्यकता हो सकती है) सशर्त तर्क के बिना यह निर्धारित करने के लिए कि क्या तैयारी को बुलाया जाना चाहिए या नहीं।

इस उपयोग के मामलों, AFAIK के अलावा SQL सर्वर के लिए .NET प्रदाता के साथ तैयारी का कोई मूल्य नहीं है।

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