मुझे एसक्यूएल 7 दिनों में कुछ प्रदर्शन कारणों के बारे में पता था, लेकिन SQL Server 2005 में अभी भी वही समस्याएं मौजूद हैं? अगर मेरे पास संग्रहीत प्रक्रिया में कोई परिणाम है जो मैं अलग-अलग कार्य करना चाहता हूं, तो क्या कर्सर अभी भी खराब विकल्प हैं? यदि हां, तो क्यों?एसक्यूएल सर्वर में कर्सर का उपयोग करने के लिए इसे बुरा अभ्यास क्यों माना जाता है?
उत्तर
क्योंकि कर्सर स्मृति लेते हैं और ताले बनाते हैं।
आप वास्तव में क्या कर रहे हैं सेट-आधारित तकनीक को गैर-सेट आधारित कार्यक्षमता में मजबूर करने का प्रयास कर रहा है। और, सभी निष्पक्षता में, मुझे यह इंगित करना चाहिए कि कर्सर का उपयोग होता है, लेकिन वे इस पर फंसे हुए हैं क्योंकि सेट-आधारित समाधानों का उपयोग करने के लिए उपयोग किए जाने वाले कई लोग सेट-आधारित समाधान को समझने के बजाय कर्सर का उपयोग नहीं करते हैं।
लेकिन, जब आप एक कर्सर खोलते हैं, तो आप मूल रूप से उन पंक्तियों को स्मृति में लोड कर रहे हैं और संभावित लॉक बनाकर उन्हें लॉक कर रहे हैं। फिर, जैसा कि आप कर्सर के माध्यम से चक्र करते हैं, आप अन्य तालिकाओं में परिवर्तन कर रहे हैं और फिर भी कर्सर की सभी मेमोरी और ताले खोलते हैं।
जिनमें से सभी के पास अन्य उपयोगकर्ताओं के लिए प्रदर्शन समस्याओं का कारण बनने की क्षमता है।
तो, एक सामान्य नियम के रूप में, कर्सर फेंक रहे हैं। विशेष रूप से यदि समस्या का समाधान करने में यह पहला समाधान आया है।
मुझे लगता है कि कर्सर को एक बुरा नाम मिलता है क्योंकि एसक्यूएल नए लोग उन्हें खोजते हैं और सोचते हैं "हे लूप के लिए! मुझे पता है कि उनको कैसे उपयोग करें!" और फिर वे सबकुछ के लिए उनका उपयोग जारी रखते हैं।
यदि आप उन्हें उनके लिए डिज़ाइन किए गए कार्यों के लिए उपयोग करते हैं, तो मुझे इसके साथ गलती नहीं मिल सकती है।
मैं आपके साथ हूं कि कर्सर को फॉर-लूप नहीं माना जाना चाहिए या अनुपयुक्त रूप से उपयोग नहीं किया जाना चाहिए। मैंने हमेशा सी ++ में इटरेटर जैसे कर्सर के बारे में सोचा है। यह डेटासेट के सीधा ट्रैवर्सल को सक्षम बनाता है। यदि मैं अनिवार्य रूप से डेटासेट के कुछ मानों को एक वर्चर (जैसे कॉमा सीमांकित स्ट्रिंग) में जोड़ना चाहता हूं, तो क्या कर्सर एक स्वीकार्य समाधान होगा? यह या तो एक अस्थायी तालिका है या ऐसा लगता है, ऐसा लगता है। मैं डेटासेट में कुछ भी संशोधित नहीं कर रहा हूं, बस कुछ स्तंभों के मूल्य का संदर्भ देता हूं, स्थानीय वर्चर से जुड़ा हुआ हूं, और अगले तत्व को फिर से चालू करता हूं। –
@CaitLANJenner, इसके लिए एक कर्सर का सहारा लेने से पहले STUFF को देखने का प्रयास करें। – HLGEM
एसक्यूएल एक सेट आधारित भाषा है - यही वह सबसे अच्छा है।
मुझे लगता है कि कर्सर अभी भी एक खराब विकल्प हैं जब तक कि आप सीमित परिस्थितियों में उनके उपयोग को उचित ठहराने के लिए उनके बारे में पर्याप्त समझ नहीं लेते।
एक और कारण मुझे कर्सर पसंद नहीं है स्पष्टता है। कर्सर ब्लॉक इतना बदसूरत है कि एक स्पष्ट और प्रभावी तरीके से उपयोग करना मुश्किल है।
यह कहा गया है कि कुछ मामले हैं जहां एक कर्सर वास्तव में सबसे अच्छा है - वे आमतौर पर ऐसे मामले नहीं होते हैं जो शुरुआती लोग उनका उपयोग करना चाहते हैं।
मूल समस्या, मुझे लगता है कि डेटाबेस सेट-आधारित संचालन के लिए डिज़ाइन और ट्यून किए गए हैं - डेटा में संबंधों के आधार पर एक त्वरित चरण में बड़ी मात्रा में डेटा का चयन, अद्यतन और हटा देता है।
दूसरी ओर, इन-मेमोरी सॉफ़्टवेयर को व्यक्तिगत संचालन के लिए डिज़ाइन किया गया है, इसलिए डेटा के एक सेट पर लूपिंग और संभावित रूप से प्रत्येक आइटम पर अलग-अलग संचालन करने के लिए यह सबसे अच्छा है।
लूपिंग डेटाबेस या स्टोरेज आर्किटेक्चर के लिए डिज़ाइन नहीं किया गया है, और यहां तक कि SQL सर्वर 2005 में भी, यदि आप किसी कस्टम प्रोग्राम में सेट किए गए मूल डेटा को खींचते हैं तो आप कहीं भी प्रदर्शन प्राप्त नहीं करेंगे मेमोरी में लूपिंग, डेटा ऑब्जेक्ट्स/स्ट्रक्चर का उपयोग करके जो हल्के वजन के रूप में संभव है।
ऐसा नहीं है कि वे लूपिंग के लिए डिज़ाइन नहीं किए गए हैं। आंतरिक रूप से, ज़ाहिर है, वही है जो वे करते हैं। यह है कि एक सामान्य एसक्यूएल क्वेरी ऑप्टिमाइज़ेशन इंजन को आप जो कर रहे हैं उसकी पूरी जानकारी देता है, और, उतना ही महत्वपूर्ण है, जो आप * नहीं कर रहे हैं। यह जानकर कि आप क्या कर रहे हैं, क्वेरी ऑप्टिमाइज़र एक कुशल दृष्टिकोण चुनने देता है। यदि, इसके बजाय, आप पंक्ति-दर-पंक्ति पर जाते हैं और वहां मनमाने ढंग से प्रसंस्करण करते हैं, तो अनुकूलक सहायता नहीं कर सकता है, और सर्वर को समर्थन देने का वादा करने वाले लेनदेन अर्थशास्त्र को बनाए रखने के लिए रूढ़िवादी (यानी धीमी) निर्णय लेने की आवश्यकता है। – Doradus
कर्सर के पास उनकी जगह है, हालांकि मुझे लगता है कि यह मुख्य रूप से इसलिए होता है क्योंकि अक्सर उपयोग किया जाता है जब एकल चयन विवरण परिणामों के एकत्रीकरण और फ़िल्टरिंग के लिए पर्याप्त होगा।
कर्सर से बचने से SQL सर्वर क्वेरी के प्रदर्शन को और अधिक अनुकूलित करने की अनुमति देता है, जो बड़े सिस्टम में बहुत महत्वपूर्ण है।
कभी-कभी प्रसंस्करण की प्रकृति आपको कर्सर की आवश्यकता होती है, हालांकि प्रदर्शन कारणों से सेट-आधारित तर्क का उपयोग करके ऑपरेशन लिखना हमेशा बेहतर होता है।
मैं इसे कर्सर का उपयोग करने के लिए "खराब अभ्यास" नहीं कहूंगा, लेकिन वे सर्वर पर (समकक्ष सेट-आधारित दृष्टिकोण की तुलना में) अधिक संसाधनों का उपभोग करते हैं और अक्सर आवश्यक नहीं होने की तुलना में अधिक संसाधनों का उपभोग करते हैं। यह देखते हुए, मेरी सलाह एक कर्सर का सहारा लेने से पहले अन्य विकल्पों पर विचार करना होगा।
कई प्रकार के कर्सर (आगे-केवल, स्थिर, कीसेट, गतिशील) हैं। प्रत्येक में अलग-अलग प्रदर्शन विशेषताओं और संबंधित ओवरहेड होते हैं। सुनिश्चित करें कि आप अपने ऑपरेशन के लिए सही कर्सर प्रकार का उपयोग करें। फॉरवर्ड-केवल डिफ़ॉल्ट है।
एक कर्सर का उपयोग करने के लिए एक तर्क तब होता है जब आपको व्यक्तिगत पंक्तियों को संसाधित करने और अपडेट करने की आवश्यकता होती है, खासकर ऐसे डेटासेट के लिए जिसमें एक अच्छी अनूठी कुंजी नहीं है। उस स्थिति में आप कर्सर की घोषणा करते समय अद्यतन अद्यतन के लिए उपयोग कर सकते हैं और अद्यतन के साथ अद्यतन प्रक्रिया ... जहां वर्तमान है।
ध्यान दें कि "सर्वर-साइड" कर्सर लोकप्रिय थे (ओडीबीसी और ओएलई डीबी से), लेकिन एडीओ.NET उन्हें समर्थन नहीं देता है, और AFAIK कभी नहीं करेगा।
बहुत कम मामले हैं जहां कर्सर का उपयोग उचित है। लगभग कोई मामला नहीं है जहां यह एक संबंधपरक, सेट-आधारित क्वेरी से बेहतर प्रदर्शन करेगा। कभी-कभी प्रोग्रामर के लिए लूप के संदर्भ में सोचना आसान होता है, लेकिन सेट लॉजिक का उपयोग, उदाहरण के लिए तालिका में बड़ी संख्या में पंक्तियों को अपडेट करने के लिए, परिणामस्वरूप एक समाधान होगा जो SQL कोड की केवल कम लाइन नहीं है, लेकिन यह बहुत तेजी से चलता है, अक्सर तीव्रता तीव्रता के कई आदेश।
एसक्यूएल सर्वर 2005 में भी तेज़ आगे कर्सर सेट-आधारित क्वेरी के साथ प्रतिस्पर्धा नहीं कर सकता है। प्रदर्शन गिरावट का ग्राफ अक्सर सेट-आधारित की तुलना में एन^2 ऑपरेशन की तरह दिखने लगता है, जो डेटा सेट बहुत बड़ा हो जाता है क्योंकि अधिक रैखिक होता है।
एसक्यूएल के बारे में उपरोक्त टिप्पणियां सेट-आधारित वातावरण हैं, ये सब सच हैं। हालांकि ऐसे समय होते हैं जब पंक्ति-दर-पंक्ति संचालन उपयोगी होते हैं। मेटाडाटा और गतिशील-एसक्यूएल के संयोजन पर विचार करें।
एक बहुत ही सरल उदाहरण के रूप में, कहें कि मेरे पास एक टेबल में 100+ रिकॉर्ड हैं जो टेबल के नामों को परिभाषित करते हैं जिन्हें मैं कॉपी/ट्रंकेट/जो भी करना चाहता हूं। कौन सा सबसे अच्छा है? एसक्यूएल को कड़ी मेहनत करने के लिए जो मुझे चाहिए? या इस परिणाम के माध्यम से पुनरावृत्त करें और संचालन करने के लिए गतिशील-एसक्यूएल (sp_executesql) का उपयोग करें?
सेट-आधारित SQL का उपयोग करके उपर्युक्त उद्देश्य प्राप्त करने का कोई तरीका नहीं है।
तो, कर्सर या थोड़ी देर लूप (छद्म-कर्सर) का उपयोग करने के लिए? के रूप में आप सही विकल्प का उपयोग
एसक्यूएल कर्सर जब तक ठीक हैं:
असंवेदनशील अपने परिणाम सेट की अस्थायी प्रतिलिपि कर देगा (अपने छद्म कर्सर के लिए इस अपने आप को क्या करने वाले से बचाने)।
READ_ONLY सुनिश्चित करेगा कि अंतर्निहित परिणाम सेट पर कोई ताले नहीं हैं। अंतर्निहित परिणाम सेट में परिवर्तन बाद के fetches में दिखाई देंगे (जैसे कि आपके छद्म-कर्सर से शीर्ष 1 प्राप्त करना)।
FAST_FORWARD एक अनुकूलित फॉरवर्ड-केवल, केवल-पढ़ने वाले कर्सर बनाएगा।
सभी कर्सर को बुराई के रूप में सत्तारूढ़ करने से पहले उपलब्ध विकल्पों के बारे में पढ़ें।
FAST_FORWARD READ_ONLY का मतलब नहीं है। –
@ पॉल-सेबेस्टियन मोनोल हम्म [दस्तावेज़ पढ़े गए] (https://msdn.microsoft.com/en-us/library/ms180169.aspx?f=255&MSPPError=-2147217396) 'FAST_FORWARD एक FORWARD_ONLY निर्दिष्ट करता है, READ_ONLY कर्सर 'क्यों क्या आप अन्यथा विश्वास करते हैं? –
@ डैनियल पी -> आपको ऐसा करने के लिए कर्सर का उपयोग करने की आवश्यकता नहीं है। आप इसे करने के लिए सेट आधारित सिद्धांत का आसानी से उपयोग कर सकते हैं। उदाहरण: एसक्यूएल 2008
DECLARE @commandname NVARCHAR(1000) = '';
SELECT @commandname += 'truncate table ' + tablename + '; ';
FROM tableNames;
EXEC sp_executesql @commandname;
बस ऊपर जो कहा है वह करेगा। और आप एसक्यूएल 2000 के साथ ऐसा ही कर सकते हैं लेकिन क्वेरी का सिंटैक्स अलग होगा।
हालांकि, मेरी सलाह है कि जितना संभव हो सके कर्सर से बचें।
Gayam
वहाँ कर्सर है कि मैं हर बार मुझे इसकी आवश्यकता का उपयोग के बारे में चारों ओर एक काम है।
मैं इसमें एक पहचान कॉलम के साथ एक तालिका चर बनाते हैं।
उस सभी डेटा को सम्मिलित करें जिसमें मुझे इसके साथ काम करने की आवश्यकता है।
फिर काउंटर वैरिएबल के साथ थोड़ी देर ब्लॉक करें और तालिका वैरिएबल से इच्छित डेटा का चयन करें जिसमें एक चयन कथन है जहां पहचान कॉलम काउंटर से मेल खाता है।
इस तरह से मैं कुछ भी लॉक नहीं करता हूं और बहुत कम स्मृति और इसका सुरक्षित उपयोग करता हूं, मैं स्मृति भ्रष्टाचार या उसके जैसा कुछ भी नहीं खोऊंगा।
और ब्लॉक कोड देखना और संभालना आसान है।
यह एक सरल उदाहरण है:
DECLARE @TAB TABLE(ID INT IDENTITY, COLUMN1 VARCHAR(10), COLUMN2 VARCHAR(10))
DECLARE @COUNT INT,
@MAX INT,
@CONCAT VARCHAR(MAX),
@COLUMN1 VARCHAR(10),
@COLUMN2 VARCHAR(10)
SET @COUNT = 1
INSERT INTO @TAB VALUES('TE1S', 'TE21')
INSERT INTO @TAB VALUES('TE1S', 'TE22')
INSERT INTO @TAB VALUES('TE1S', 'TE23')
INSERT INTO @TAB VALUES('TE1S', 'TE24')
INSERT INTO @TAB VALUES('TE1S', 'TE25')
SELECT @MAX = @@IDENTITY
WHILE @COUNT <= @MAX BEGIN
SELECT @COLUMN1 = COLUMN1, @COLUMN2 = COLUMN2 FROM @TAB WHERE ID = @COUNT
IF @CONCAT IS NULL BEGIN
SET @CONCAT = ''
END ELSE BEGIN
SET @CONCAT = @CONCAT + ','
END
SET @CONCAT = @CONCAT + @COLUMN1 + @COLUMN2
SET @COUNT = @COUNT + 1
END
SELECT @CONCAT
मुझे वह तरीका पसंद है। मैंने अभी हाल ही में एसक्यूएल का उपयोग करने की आवश्यकता शुरू कर दी है, और एक कर्सर का उपयोग कर आया। यह देखना शुरू हुआ कि वे कैसे बुरे हैं, और सेट के आधार पर मेरे सिर को लपेटने की कोशिश कर रहे हैं। मुझे अभी भी इस विधि को पसंद है। – Casey
क्षमा करें, यह एक विरोधी पैटर्न है जिसे कार्गो पंथ प्रोग्रामिंग कहा जाता है: यह नहीं जानते कि कर्सर को क्यों बुरा माना जाता है, आप कुछ और भी खराब करते हैं! कल्पना कीजिए कि आप सेट आधारित क्वेरी में स्ट्रिंग्स को संयोजित नहीं कर सकते (उदाहरण के लिए, SQL सर्वर में COALESCE का उपयोग करके), तो कर्सर पसंद का तरीका होगा। सामग्री को अस्थायी तालिका या तालिका चर में स्थानांतरित करना और प्रत्येक अगली पंक्ति के लिए चयन करना बड़ा ओवरहेड जोड़ता है। पहचान/पीके पर कोई सूचकांक नहीं होने पर भी बदतर। –
मुझे लगता है कि आप समझते हैं कि concatenation सिर्फ एक उदाहरण है। परीक्षणों के आधार पर, जब मुझे एसक्यूएल सर्वर 2005 में बहुत समय पहले इस विधि का उपयोग करना पड़ा था, तो यह कर्सर से तेज था। मुझे पता है क्यों कर्सर खराब मानते हैं। मुझे आपकी पूर्वाग्रह या इस पूंजीकृत के साथ जो कुछ भी आपका इरादा था, उसके लिए अनिश्चित होना चाहिए। – Zorkind
कर्सर आमतौर पर बीमारी नहीं है, लेकिन यह का एक लक्षण हैं: का उपयोग नहीं कर सेट-आधारित दृष्टिकोण (के रूप में अन्य उत्तर में उल्लेख किया है)।
इस समस्या को समझना नहीं, और केवल यह मानना है कि "बुराई" कर्सर से बचने से यह हल हो जाएगा, चीजों को और भी खराब कर सकता है।
उदाहरण के लिए, इस तरह के अस्थायी टेबल या तालिका चर के डेटा, जैसे एक तरह से पंक्तियों पर पाश करने के लिए जाने के रूप में, अन्य पुनरावृत्ति कोड द्वारा कर्सर यात्रा की जगह:
SELECT * FROM @temptable WHERE [email protected]
या
SELECT TOP 1 * FROM @temptable WHERE Id>@lastId
इस तरह का एक दृष्टिकोण, जैसा कि किसी अन्य उत्तर के कोड में दिखाया गया है, चीजों को और भी बदतर बनाता है और मूल समस्या को ठीक नहीं करता है। यह कार्गो पंथ प्रोग्रामिंग नामक एंटी-पैटर्न है: यह नहीं जानना कि कुछ बुरा क्यों है और इस प्रकार इससे बचने के लिए कुछ और खराब कर रहा है! मैंने हाल ही में इस तरह के कोड को बदल दिया है (एक # अस्वीकार्य और पहचान/पीके पर कोई अनुक्रमणिका नहीं) एक कर्सर पर वापस, और 10000 पंक्तियों से थोड़ा अधिक अद्यतन करने से लगभग 3 मिनट के बजाय केवल 1 सेकंड लिया गया। अभी भी सेट-आधारित दृष्टिकोण (कम बुराई होने) की कमी है, लेकिन सबसे अच्छा मैं उस पल को कर सकता था।
समझने की कमी का एक अन्य लक्षण मैं कभी-कभी "एक ऑब्जेक्ट बीमारी" कह सकता हूं: डेटाबेस अनुप्रयोग जो डेटा एक्सेस परतों या ऑब्जेक्ट-रिलेशनल मैपर के माध्यम से एकल ऑब्जेक्ट को संभालते हैं।आमतौर पर कोड की तरह:
var items = new List<Item>();
foreach(int oneId in itemIds)
{
items.Add(dataAccess.GetItemById(oneId);
}
बजाय
var items = dataAccess.GetItemsByIds(itemIds);
पहले आम तौर पर चयन करता है की टन, प्रत्येक के लिए एक राउंड ट्रिप, खासकर जब वस्तु पेड़/रेखांकन खेलने के लिए और कुख्यात में आने के साथ डेटाबेस बाढ़ जाएगा एन + 1 समस्या स्ट्राइक चुनें।
यह संबंधपरक डेटाबेस और सेट आधारित दृष्टिकोण को समझने का अनुप्रयोग पक्ष है, वैसे ही कर्सर प्रक्रियात्मक डेटाबेस कोड, जैसे टी-एसक्यूएल या पीएल/एसक्यूएल का उपयोग करते समय भी होते हैं!
मैं जोसेफ से सहमत हूं। अपनी टिप्पणियों में जोड़ने के लिए, यदि आपको परिणामस्वरूप पूरी तरह से पुन: प्रयास करना होगा तो कर्सर के लिए एक अच्छा विकल्प तालिका चर का उपयोग करना है और WHILE लूप का उपयोग करके रिकॉर्ड्स के माध्यम से पुनरावृत्ति करना है। मुझे डाउनसाइड्स के बिना कर्सर के सभी लाभों के लिए यह विकल्प मिला है। –
जो बैरोन: 3 साल देर हो चुकी है ... लेकिन अगर आपको पूरी तरह से एक परिणाम के माध्यम से फिर से शुरू करना होगा तो आपको * कर्सर का उपयोग करना चाहिए। वास्तव में, एक टेबल वैरिएबल का उपयोग ** ** केवल पढ़ने के साथ, केवल कर्सर को आगे बढ़ाना एक अच्छा संयोजन है: कोई ताले के बारे में चिंता किए बिना, सभी रिकॉर्डों को तेजी से पुन: सक्रिय कर सकता है। इस आदमी की तरह (केवल एक्टिवेटर प्रक्रिया को देखें): http://blogs.msdn.com/b/sql_service_broker/archive/2008/07/25/reusing-dialogs-with-a-dialog-pool.aspx – rsenna