हम अक्सर खराब लिखित जटिल SQL क्वेरी को डेटाबेस में किसी तालिका या तालिकाओं के विरुद्ध चलते हुए देखते हैं। वे प्रश्न निष्पादन के समय को बहुत लंबा बनाते हैं और विशाल CPU और अन्य संसाधनों की खपत का कारण बनते हैं। फिर भी, जटिल प्रश्न कई मामलों में उन्हें चलाने वाले एप्लिकेशन/व्यक्ति को बहुमूल्य जानकारी प्रदान करते हैं। इसलिए, वे सभी प्रकार के अनुप्रयोगों में उपयोगी संपत्ति हैं।
जटिल प्रश्नों को डीबग करना कठिन है
यदि हम समस्याग्रस्त प्रश्नों को करीब से देखें, तो उनमें से कई जटिल हैं, विशेष रूप से वे विशिष्ट प्रश्न जिनका रिपोर्ट में उपयोग किया गया है।
जटिल प्रश्नों में अक्सर पाँच या अधिक बड़ी तालिकाएँ होती हैं और कई उप-प्रश्नों द्वारा एक साथ जुड़ जाते हैं। प्रत्येक उप-क्वेरी में एक WHERE क्लॉज होता है जो कॉलम से संबंधित तालिकाओं को एक साथ जोड़ते हुए सरल से जटिल गणना और/या डेटा परिवर्तन करता है।
इस तरह के प्रश्न बहुत सारे संसाधनों का उपभोग किए बिना डीबग करना चुनौतीपूर्ण हो सकता है। इसका कारण यह है कि यह निर्धारित करना कठिन है कि प्रत्येक उप-क्वेरी और/या सम्मिलित उप-क्वेरी सही परिणाम देती है या नहीं।
एक विशिष्ट परिदृश्य है:वे आपको एक व्यस्त डेटाबेस सर्वर पर एक जटिल क्वेरी के साथ एक समस्या को हल करने के लिए देर रात को कॉल करते हैं, और आपको इसे जल्दी से ठीक करने की आवश्यकता होती है। एक डेवलपर या डीबीए के रूप में, आपके पास बहुत सीमित समय और सिस्टम संसाधन देर से उपलब्ध हो सकते हैं। इस प्रकार, पहली चीज़ जो आपको चाहिए वह है समस्यात्मक क्वेरी को डीबग करने की योजना।
कभी-कभी, डिबगिंग प्रक्रिया अच्छी तरह से चलती है। कभी-कभी, लक्ष्य तक पहुँचने और समस्या को हल करने में बहुत समय और मेहनत लगती है।
CTE संरचना में प्रश्न लिखना
लेकिन क्या होगा अगर जटिल प्रश्नों को लिखने का कोई तरीका था ताकि कोई उन्हें जल्दी से डिबग कर सके, टुकड़े-टुकड़े?
एक ऐसा तरीका है। इसे कॉमन टेबल एक्सप्रेशन या सीटीई कहा जाता है।
अधिकांश आधुनिक डेटाबेस जैसे SQLServer, MySQL (संस्करण 8.0 के अनुसार), MariaDB (संस्करण 10.2.1), Db2 और Oracle में सामान्य तालिका अभिव्यक्ति एक मानक विशेषता है। इसकी एक सरल संरचना है जो एक या कई उप-प्रश्नों को अस्थायी नामित परिणाम सेट में समाहित करती है। आप इस परिणाम सेट का उपयोग अन्य नामित सीटीई या उप-प्रश्नों में कर सकते हैं।
एक कॉमन टेबल एक्सप्रेशन, कुछ हद तक, एक ऐसा दृश्य होता है जो केवल मौजूद होता है और निष्पादन के समय क्वेरी द्वारा संदर्भित होता है।
एक जटिल क्वेरी को CTE स्टाइल क्वेरी में बदलने के लिए कुछ संरचित सोच की आवश्यकता होती है। सीटीई संरचना में एक जटिल क्वेरी को फिर से लिखते समय ओओपी के लिए एनकैप्सुलेशन के साथ भी यही होता है।
आपको इस बारे में सोचने की जरूरत है:
- डेटा का प्रत्येक सेट जिसे आप प्रत्येक तालिका से खींच रहे हैं।
- निकटतम उप-प्रश्नों को एक अस्थायी नामित परिणाम सेट में समाहित करने के लिए उन्हें एक साथ कैसे जोड़ा जाता है।
प्रत्येक उप-क्वेरी और शेष डेटा के सेट के लिए इसे तब तक दोहराएं जब तक आप क्वेरी के अंतिम परिणाम तक नहीं पहुंच जाते। ध्यान दें कि प्रत्येक अस्थायी नामित परिणाम सेट भी एक उप-क्वेरी है।
क्वेरी का अंतिम भाग एक बहुत ही "सरल" चयन होना चाहिए, जो आवेदन के लिए अंतिम परिणाम लौटाता है। एक बार जब आप इस अंतिम भाग पर पहुंच जाते हैं, तो आप इसे एक क्वेरी के साथ बदल सकते हैं जो व्यक्तिगत रूप से नामित अस्थायी परिणाम सेट से डेटा का चयन करती है।
इस तरह, प्रत्येक अस्थायी परिणाम सेट की डिबगिंग एक आसान काम बन जाती है।
यह समझने के लिए कि हम अपने प्रश्नों को सरल से जटिल कैसे बना सकते हैं, आइए सीटीई संरचना को देखें। सबसे सरल रूप इस प्रकार है:
WITH CTE_1 as (
select .... from some_table where ...
)
select ... from CTE_1
where ...
यहां CTE_1 एक अनूठा नाम है जिसे आप अस्थायी नामित परिणाम सेट को देते हैं। आवश्यकतानुसार कई परिणाम सेट हो सकते हैं। उसके द्वारा, फॉर्म का विस्तार इस प्रकार है, जैसा कि नीचे दिखाया गया है:
WITH CTE_1 as (
select .... from some_table where ...
), CTE_2 as (
select .... from some_other_table where ...
)
select ... from CTE_1 c1,CTE_2 c2
where c1.col1 = c2.col1
....
सबसे पहले, प्रत्येक सीटीई भाग अलग से बनाया जाता है। फिर यह आगे बढ़ता है, क्योंकि CTE को क्वेरी के अंतिम परिणाम सेट को बनाने के लिए एक साथ जोड़ा जाता है।
अब, हम एक अन्य मामले की जांच करते हैं, एक काल्पनिक बिक्री डेटाबेस को क्वेरी करते हुए। हम यह जानना चाहते हैं कि पिछले महीने प्रत्येक श्रेणी में कौन से उत्पाद, मात्रा और कुल बिक्री सहित, बेचे गए, और उनमें से किस महीने की तुलना में कुल बिक्री अधिक हुई।
हम अपनी क्वेरी को कई CTE भागों में बनाते हैं, जहाँ प्रत्येक भाग पिछले वाले को संदर्भित करता है। सबसे पहले, हम बाकी क्वेरी बनाने के लिए अपनी टेबल से आवश्यक विस्तृत डेटा को सूचीबद्ध करने के लिए एक परिणाम सेट तैयार करते हैं:
WITH detailed_data as (
select o.order_date, c.category_name,p.product_name,oi.quantity, oi.listprice, oi.discount
from Orders o, Order_Item oi, Products p, Category c
where o.order_id = oi.order_id
and oi.product_id = p.product_id
and p.category_id = c.category_id
)
select dt.*
from detailed_data dt.
order by dt.order_date desc, dt.category_name, dt.product_name
अगला कदम प्रत्येक श्रेणी और उत्पाद के नाम से मात्रा और कुल बिक्री डेटा को सारांशित करना है:
WITH detailed_data as (
select o.order_date, c.category_name,p.product_name,oi.quantity, oi.listprice, oi.discount
from Orders o, Order_Item oi, Products p, Category c
where o.order_id = oi.order_id
and oi.product_id = p.product_id
and p.category_id = c.category_id
), product_sales as (
select year(dt.order_date) year, month(dt.order_date) month, dt.category_name,dt.product_name,sum(dt.quantity) total_quantity, sum(dt.listprice * (1 - dt.discount)) total_product_sales
from detailed_data dt
group by year(dt.order_date) year, month(dt.order_date) month, dt.category_name,dt.product_name
)
select ps.*
from product_sales ps
order by ps.year desc, ps.month desc, ps.category_name,ps.product_name
अंतिम चरण पिछले महीने और पिछले महीने के डेटा का प्रतिनिधित्व करने वाले दो अस्थायी परिणाम सेट बनाना है। उसके बाद, अंतिम परिणाम सेट के रूप में लौटाए जाने वाले डेटा को फ़िल्टर करें:
WITH detailed_data as (
select o.order_date, c.category_name,p.product_name,oi.quantity, oi.listprice, oi.discount
from Orders o, Order_Item oi, Products p, Category c
where o.order_id = oi.order_id
and oi.product_id = p.product_id
and p.category_id = c.category_id
), product_sales as (
select year(dt.order_date) year, month(dt.order_date) month, dt.category_name,dt.product_name,sum(dt.quantity) total_quantity, sum(dt.listprice * (1 - dt.discount)) total_product_sales
from detailed_data dt
group by year(dt.order_date) year, month(dt.order_date) month, dt.category_name,dt.product_name
), last_month_data (
select ps.*
from product_sales ps.
where ps.year = year(CURRENT_DATE) -1
and ps.month = month(CURRENT_DATE) -1
), prev_month_data (
select ps.*
from product_sales ps.
where ps.year = year(CURRENT_DATE) -2
and ps.month = month(CURRENT_DATE) -2
)
select lmd.*
from last_month_data lmd, prev_month_data pmd
where lmd.category_name = pmd.category_name
and lmd.product_name = pmd.product_name
and ( lmd.total_quantity > pmd.total_quantity
or lmd.total_product_sales > pmd.total_product_sales )
order by lmd.year desc, lmd.month desc, lmd.category_name,lmd.product_name, lmd.total_product_sales desc, lmd.total_quantity desc
ध्यान दें कि SQLServer में आप CURRENT_DATE के बजाय getdate() सेट करते हैं।
इस तरह, हम अंतिम भाग को एक चयन के साथ बदल सकते हैं जो एक चयनित भाग के परिणाम को देखने के लिए अलग-अलग सीटीई भागों से पूछताछ करता है। परिणामस्वरूप, हम समस्या को शीघ्रता से डीबग कर सकते हैं।
साथ ही, प्रत्येक सीटीई भाग (और पूरी क्वेरी) पर एक व्याख्या निष्पादित करके, हम अनुमान लगाते हैं कि प्रत्येक भाग और/या पूरी क्वेरी टेबल और डेटा पर कितना अच्छा प्रदर्शन करेगी।
इसके अनुरूप, आप प्रत्येक भाग को पुनर्लेखन और/या शामिल तालिकाओं में उचित अनुक्रमणिका जोड़कर अनुकूलित कर सकते हैं। फिर आप अंतिम क्वेरी योजना देखने के लिए पूरी क्वेरी की व्याख्या करते हैं और यदि आवश्यक हो तो अनुकूलन के साथ आगे बढ़ते हैं।
CTE संरचना का उपयोग कर पुनरावर्ती क्वेरी
सीटीई की एक अन्य उपयोगी विशेषता पुनरावर्ती क्वेरी बनाना है।
पुनरावर्ती SQL क्वेरी आपको ऐसी चीज़ें प्राप्त करने देती हैं जिनकी आप इस प्रकार के SQL और इसकी दर से कल्पना नहीं कर सकते हैं। आप कई व्यावसायिक समस्याओं को हल कर सकते हैं और यहां तक कि कुछ जटिल SQL/एप्लिकेशन लॉजिक को डेटाबेस में एक साधारण पुनरावर्ती SQL-कॉल में फिर से लिख सकते हैं।
डेटाबेस सिस्टम के बीच पुनरावर्ती क्वेरी बनाने में थोड़ी भिन्नता है। हालांकि, लक्ष्य वही है।
पुनरावर्ती CTE की उपयोगिता के कुछ उदाहरण:
- आप इसका उपयोग डेटा में अंतराल खोजने के लिए कर सकते हैं।
- आप संगठन चार्ट बना सकते हैं।
- आप किसी अन्य सीटीई भाग में आगे उपयोग करने के लिए पूर्व-गणना डेटा बना सकते हैं
- आखिरकार, आप परीक्षण डेटा बना सकते हैं।
शब्द पुनरावर्ती यह सब कहता है। आपके पास एक प्रश्न है जो बार-बार खुद को कुछ शुरुआती बिंदु के साथ बुलाता है, और, अत्यंत महत्वपूर्ण, एक समापन बिंदु (a असफल निकास जैसा कि मैं इसे कहता हूं)।
यदि आपके पास असफल-सुरक्षित निकास नहीं है, या आपका पुनरावर्ती सूत्र इससे आगे जाता है, तो आप गहरे संकट में हैं। क्वेरी अनंत लूप . में जाएगी जिसके परिणामस्वरूप बहुत अधिक CPU और बहुत अधिक लॉग उपयोग होता है। इससे मेमोरी और/या स्टोरेज खत्म हो जाएगी।
यदि आपकी क्वेरी खराब हो जाती है, तो आपको इसे अक्षम करने के लिए बहुत तेज़ी से सोचना चाहिए। यदि आप ऐसा नहीं कर सकते हैं, तो तुरंत अपने डीबीए को सचेत करें, ताकि वे डेटाबेस सिस्टम को अवरुद्ध होने से रोक सकें, जिससे भगोड़ा धागा समाप्त हो जाए।
उदाहरण देखें:
with RECURSIVE mydates (level,nextdate) as (
select 1 level, FROM_UNIXTIME(RAND()*2147483647) nextdate from DUAL
union all
select level+1, FROM_UNIXTIME(RAND()*2147483647) nextdate
from mydates
where level < 1000
)
SELECT nextdate from mydates
);
यह उदाहरण एक MySQL/MariaDB पुनरावर्ती CTE सिंटैक्स है। इसके साथ, हम एक हजार यादृच्छिक तिथियां उत्पन्न करते हैं। पुनरावर्ती क्वेरी से सुरक्षित रूप से बाहर निकलने के लिए स्तर हमारा काउंटर और असफल-सुरक्षित निकास है।
जैसा कि दिखाया गया है, लाइन 2 हमारा शुरुआती बिंदु है, जबकि लाइन 4-5 WHERE क्लॉज (लाइन 6) में अंतिम बिंदु के साथ रिकर्सिव कॉल हैं। पंक्ति 8 और 9 पुनरावर्ती क्वेरी निष्पादित करने और डेटा पुनर्प्राप्त करने में कॉल हैं।
एक और उदाहरण:
DECLARE @today as date;
DECLARE @1stjanprevyear as date;
select @today = DATEADD(DAY, 0, DATEDIFF(DAY, 0, getdate())),
@1stjanprevyear = DATEFROMPARTS(YEAR(GETDATE())-1, 1, 1) ;
WITH DatesCTE as (
SELECT @1stjanprevyear as CalendarDate
UNION ALL
SELECT dateadd(day , 1, CalendarDate) AS CalendarDate FROM DatesCTE
WHERE dateadd (day, 1, CalendarDate) < @today
), MaxMinDates as (
SELECT Max(CalendarDate) MaxDate,Min(CalendarDate) MinDate
FROM DatesCTE
)
SELECT i.*
FROM InvoiceTable i, MaxMinDates t
where i.INVOICE_DATE between t.MinDate and t.MaxDate
OPTION (MAXRECURSION 1000);
यह उदाहरण एक SQLServer सिंटैक्स है। यहां, हम DatesCTE भाग को पिछले वर्ष के आज और 1 जनवरी के बीच की सभी तिथियों को प्रस्तुत करने देते हैं। हम इसका उपयोग उन तिथियों से संबंधित सभी चालानों को वापस करने के लिए करते हैं।
शुरुआती बिंदु @1stjanprevyear . है परिवर्तनीय और असफल-सुरक्षित निकास @आज . अधिकतम 730 दिन संभव है। इस प्रकार, यह सुनिश्चित करने के लिए अधिकतम रिकर्सन विकल्प 1000 पर सेट है कि यह रुक जाए।
हम MaxMinDates . को भी छोड़ सकते हैं भाग और अंतिम भाग लिखें, जैसा कि नीचे दिखाया गया है। यह एक तेज़ तरीका हो सकता है, क्योंकि हमारे पास एक मिलान WHERE क्लॉज़ है।
....
SELECT i.*
FROM InvoiceTable i, DatesCTE t
where i.INVOICE_DATE = t.CalendarDate
OPTION (MAXRECURSION 1000);
निष्कर्ष
कुल मिलाकर, हमने संक्षेप में चर्चा की है और दिखाया है कि एक जटिल क्वेरी को सीटीई संरचित क्वेरी में कैसे बदलना है। जब एक क्वेरी को अलग-अलग सीटीई भागों में विभाजित किया जाता है, तो आप उन्हें अन्य भागों में उपयोग कर सकते हैं और डिबगिंग उद्देश्यों के लिए अंतिम SQL क्वेरी में स्वतंत्र रूप से कॉल कर सकते हैं।
एक अन्य महत्वपूर्ण बिंदु यह है कि सीटीई का उपयोग करने से जटिल क्वेरी को डिबग करना आसान हो जाता है जब इसे सही और अपेक्षित परिणाम सेट वापस करने के लिए प्रबंधनीय भागों में विभाजित किया जाता है। यह महसूस करना महत्वपूर्ण है कि प्रत्येक क्वेरी भाग पर एक व्याख्या चलाना और पूरी क्वेरी यह सुनिश्चित करने के लिए महत्वपूर्ण है कि क्वेरी और DBMS यथासंभव इष्टतम रूप से चले।
मैंने एक शक्तिशाली रिकर्सिव सीटीई क्वेरी/भाग को एक प्रश्न में आगे उपयोग करने के लिए फ्लाई पर डेटा उत्पन्न करने में भी चित्रित किया है।
विशेष रूप से, पुनरावर्ती क्वेरी लिखते समय, बहुत सावधान रहें कि विफल-सुरक्षित निकास को न भूलें . स्टॉप सिग्नल उत्पन्न करने के लिए विफल-सुरक्षित निकास में उपयोग की गई गणनाओं को दोबारा जांचना सुनिश्चित करें और/या maxrecursion का उपयोग करें विकल्प जो SQLServer प्रदान करता है।
इसी तरह, अन्य DBMS या तो cte_max_recursion_depth . का उपयोग कर सकते हैं (MySQL 8.0) या max_recursive_iterations (MariaDB 10.3) अतिरिक्त विफल-सुरक्षित निकास के रूप में।
यह भी पढ़ें
एसक्यूएल सीटीई के बारे में सब कुछ जो आपको एक ही स्थान पर जानना आवश्यक है