क्या आप SQL सबक्वेरी का उपयोग करते हैं या उनका उपयोग करने से बचते हैं?
मान लें कि मुख्य क्रेडिट और संग्रह अधिकारी आपको लोगों के नाम, प्रति माह उनके भुगतान न किए गए शेष और वर्तमान चल रहे शेष को सूचीबद्ध करने के लिए कहते हैं और चाहते हैं कि आप इस डेटा सरणी को एक्सेल में आयात करें। इसका उद्देश्य डेटा का विश्लेषण करना और COVID19 महामारी के प्रभावों को कम करने के लिए भुगतान को आसान बनाने वाली पेशकश के साथ आना है।
क्या आप एक क्वेरी और एक नेस्टेड सबक्वेरी या एक जॉइन का उपयोग करने का विकल्प चुनते हैं? आप क्या निर्णय लेंगे?
एसक्यूएल सबक्वेरी - वे क्या हैं?
इससे पहले कि हम वाक्य रचना, प्रदर्शन प्रभाव और चेतावनियों में गहराई से उतरें, पहले एक सबक्वेरी को परिभाषित क्यों न करें?
सबसे सरल शब्दों में, एक सबक्वेरी एक क्वेरी के भीतर एक क्वेरी है। जबकि एक सबक्वेरी को शामिल करने वाली एक क्वेरी बाहरी क्वेरी है, हम एक सबक्वेरी को आंतरिक क्वेरी या आंतरिक चयन के रूप में संदर्भित करते हैं। और कोष्ठक नीचे की संरचना के समान एक उपश्रेणी संलग्न करते हैं:
SELECT
col1
,col2
,(subquery) as col3
FROM table1
[JOIN table2 ON table1.col1 = table2.col2]
WHERE col1 <operator> (subquery)
हम इस पोस्ट में निम्नलिखित बिंदुओं पर गौर करने जा रहे हैं:
- एसक्यूएल सबक्वेरी सिंटैक्स विभिन्न सबक्वेरी प्रकारों और ऑपरेटरों पर निर्भर करता है।
- कब और किस तरह के बयानों में कोई सबक्वेरी का उपयोग कर सकता है।
- प्रदर्शन प्रभाव बनाम जॉइन ।
- एसक्यूएल सबक्वेरी का उपयोग करते समय सामान्य चेतावनी।
जैसा कि प्रथागत है, हम समझ बढ़ाने के लिए उदाहरण और उदाहरण प्रदान करते हैं। लेकिन ध्यान रखें कि इस पोस्ट का मुख्य फोकस SQL सर्वर में सबक्वेरी पर है।
अब, चलिए शुरू करते हैं।
एसक्यूएल सबक्वेरी बनाएं जो स्व-निहित या सहसंबद्ध हों
एक बात के लिए, उपश्रेणियों को बाहरी क्वेरी पर उनकी निर्भरता के आधार पर वर्गीकृत किया जाता है।
मुझे बताएं कि एक स्व-निहित सबक्वेरी क्या है।
स्व-निहित उपश्रेणियाँ (या कभी-कभी गैर-सहसंबद्ध या साधारण उपश्रेणी के रूप में संदर्भित) बाहरी क्वेरी में तालिकाओं से स्वतंत्र होती हैं। मैं इसे स्पष्ट करता हूँ:
-- Get sales orders of customers from Southwest United States
-- (TerritoryID = 4)
USE [AdventureWorks]
GO
SELECT CustomerID, SalesOrderID
FROM Sales.SalesOrderHeader
WHERE CustomerID IN (SELECT [CustomerID]
FROM [AdventureWorks].[Sales].[Customer]
WHERE TerritoryID = 4)
जैसा कि उपरोक्त कोड में दिखाया गया है, सबक्वेरी (नीचे कोष्ठक में संलग्न) में बाहरी क्वेरी में किसी भी कॉलम का कोई संदर्भ नहीं है। इसके अतिरिक्त, आप SQL सर्वर प्रबंधन स्टूडियो में सबक्वेरी को हाइलाइट कर सकते हैं और बिना किसी रनटाइम त्रुटि के इसे निष्पादित कर सकते हैं।
जो, बदले में, स्व-निहित उपश्रेणियों की आसान डिबगिंग की ओर ले जाती है।
विचार करने वाली अगली बात सहसंबद्ध उपश्रेणियाँ हैं। अपने स्वयं के समकक्ष की तुलना में, इसमें कम से कम एक कॉलम बाहरी क्वेरी से संदर्भित किया जा रहा है। स्पष्ट करने के लिए, मैं एक उदाहरण प्रदान करूंगा:
USE [AdventureWorks]
GO
SELECT DISTINCT a.LastName, a.FirstName, b.BusinessEntityID
FROM Person.Person AS p
JOIN HumanResources.Employee AS e ON p.BusinessEntityID = e.BusinessEntityID
WHERE 1262000.00 IN
(SELECT [SalesQuota]
FROM Sales.SalesPersonQuotaHistory spq
WHERE p.BusinessEntityID = spq.BusinessEntityID)
क्या आपने पर्याप्त ध्यान दिया था कि BusinessEntityID . के संदर्भ पर ध्यान दिया जाए व्यक्ति . से टेबल? अच्छा किया!
एक बार बाहरी क्वेरी के कॉलम को सबक्वेरी में संदर्भित करने के बाद, यह एक सहसंबद्ध सबक्वेरी बन जाता है। विचार करने के लिए एक और बिंदु:यदि आप एक सबक्वेरी को हाइलाइट करते हैं और इसे निष्पादित करते हैं, तो एक त्रुटि होगी।
और हाँ, आप बिल्कुल सही हैं:यह सहसंबद्ध उपश्रेणियों को डीबग करना बहुत कठिन बना देता है।
डिबगिंग को संभव बनाने के लिए, इन चरणों का पालन करें:
- सबक्वायरी को अलग करें।
- बाहरी क्वेरी के संदर्भ को एक स्थिर मान से बदलें।
डिबगिंग के लिए सबक्वेरी को अलग करने से यह इस तरह दिखेगा:
SELECT [SalesQuota]
FROM Sales.SalesPersonQuotaHistory spq
WHERE spq.BusinessEntityID = <constant value>
अब, उपश्रेणियों के आउटपुट में थोड़ी गहराई से खुदाई करें।
3 संभावित रिटर्न वैल्यू के साथ SQL सबक्वेरी बनाएं
ठीक है, सबसे पहले, आइए सोचते हैं कि हम SQL सबक्वेरी से किन रिटर्न वैल्यू की उम्मीद कर सकते हैं।
वास्तव में, 3 संभावित परिणाम हैं:
- एकल मान
- एकाधिक मान
- संपूर्ण टेबल
एकल मान
आइए एकल-मूल्यवान आउटपुट से शुरू करें। इस प्रकार की सबक्वायरी बाहरी क्वेरी में कहीं भी प्रकट हो सकती है जहां अभिव्यक्ति अपेक्षित है, जैसे WHERE खंड।
-- Output a single value which is the maximum or last TransactionID
USE [AdventureWorks]
GO
SELECT TransactionID, ProductID, TransactionDate, Quantity
FROM Production.TransactionHistory
WHERE TransactionID = (SELECT MAX(t.TransactionID)
FROM Production.TransactionHistory t)
जब आप MAX . का उपयोग करते हैं () फ़ंक्शन, आप एक मान पुनर्प्राप्त करते हैं। ठीक यही ऊपर हमारे सबक्वेरी के साथ हुआ। बराबर का उपयोग करना (= ) ऑपरेटर SQL सर्वर को बताता है कि आप एकल मान की अपेक्षा करते हैं। एक और बात:यदि सबक्वेरी बराबर का उपयोग करके कई मान लौटाता है (= ) ऑपरेटर, आपको एक त्रुटि, . मिलता है नीचे दिए गए के समान:
Msg 512, Level 16, State 1, Line 20
Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression.
एकाधिक मान
अगला, हम बहु-मूल्यवान आउटपुट की जांच करते हैं। इस प्रकार की सबक्वेरी एकल कॉलम वाले मानों की सूची लौटाती है। इसके अतिरिक्त, IN . जैसे ऑपरेटर और में नहीं एक या अधिक मूल्यों की अपेक्षा करेगा।
-- Output multiple values which is a list of customers with lastnames that --- start with 'I'
USE [AdventureWorks]
GO
SELECT [SalesOrderID], [OrderDate], [ShipDate], [CustomerID]
FROM Sales.SalesOrderHeader
WHERE [CustomerID] IN (SELECT c.[CustomerID] FROM Sales.Customer c
INNER JOIN Person.Person p ON c.PersonID = p.BusinessEntityID
WHERE p.lastname LIKE N'I%' AND p.PersonType='SC')
संपूर्ण तालिका मान
और आखिरी लेकिन कम से कम, क्यों न पूरे टेबल आउटपुट में तल्लीन किया जाए।
-- Output a table of values based on sales orders
USE [AdventureWorks]
GO
SELECT [ShipYear],
COUNT(DISTINCT [CustomerID]) AS CustomerCount
FROM (SELECT YEAR([ShipDate]) AS [ShipYear], [CustomerID]
FROM Sales.SalesOrderHeader) AS Shipments
GROUP BY [ShipYear]
ORDER BY [ShipYear]
क्या आपने FROM . पर ध्यान दिया है खंड?
एक टेबल का उपयोग करने के बजाय, यह एक सबक्वायरी का उपयोग करता था। इसे डिराइव्ड टेबल या टेबल सबक्वेरी कहा जाता है।
और अब, इस प्रकार की क्वेरी का उपयोग करते समय मैं आपको कुछ बुनियादी नियम प्रस्तुत करता हूं:
- सबक्वायरी के सभी कॉलमों में अद्वितीय नाम होने चाहिए। भौतिक तालिका की तरह, व्युत्पन्न तालिका में अद्वितीय स्तंभ नाम होने चाहिए।
- आदेश दें अनुमति नहीं है जब तक कि TOP भी निर्दिष्ट है। ऐसा इसलिए है क्योंकि व्युत्पन्न तालिका एक संबंधपरक तालिका का प्रतिनिधित्व करती है जहां पंक्तियों का कोई परिभाषित क्रम नहीं होता है।
इस मामले में, एक व्युत्पन्न तालिका में भौतिक तालिका के लाभ होते हैं। इसलिए हमारे उदाहरण में, हम COUNT . का उपयोग कर सकते हैं () व्युत्पन्न तालिका के एक कॉलम में।
यह सब सबक्वायरी आउटपुट के बारे में है। लेकिन इससे पहले कि हम और आगे बढ़ें, आपने देखा होगा कि कई मूल्यों और अन्य के लिए उदाहरण के पीछे का तर्क भी जॉइन का उपयोग करके किया जा सकता है। ।
-- Output multiple values which is a list of customers with lastnames that start with 'I'
USE [AdventureWorks]
GO
SELECT o.[SalesOrderID], o.[OrderDate], o.[ShipDate], o.[CustomerID]
FROM Sales.SalesOrderHeader o
INNER JOIN Sales.Customer c on o.CustomerID = c.CustomerID
INNER JOIN Person.Person p ON c.PersonID = p.BusinessEntityID
WHERE p.LastName LIKE N'I%' AND p.PersonType = 'SC'
वास्तव में, आउटपुट समान होगा। लेकिन कौन सा बेहतर प्रदर्शन करता है?
इससे पहले कि हम इसमें शामिल हों, मैं आपको बता दूं कि मैंने इस गर्म विषय पर एक खंड समर्पित किया है। हम पूरी निष्पादन योजनाओं के साथ इसकी जांच करेंगे और दृष्टांतों पर एक नज़र डालेंगे।
तो, एक पल के लिए मेरे साथ रहो। आइए आपके उपश्रेणियों को रखने के दूसरे तरीके पर चर्चा करें।
अन्य कथन जहां आप SQL सबक्वेरी का उपयोग कर सकते हैं
अब तक, हम SELECT . पर SQL सबक्वेरी का उपयोग कर रहे हैं बयान। और बात यह है कि, आप INSERT . पर उपश्रेणियों के लाभों का आनंद ले सकते हैं , अपडेट करें , और हटाएं बयान या किसी टी-एसक्यूएल बयान में जो एक अभिव्यक्ति बनाता है।
तो, आइए कुछ और उदाहरणों की श्रृंखला पर एक नज़र डालते हैं।
अपडेट स्टेटमेंट में SQL सबक्वेरी का उपयोग करना
अद्यतन . में उपश्रेणियों को शामिल करना काफी आसान है बयान। क्यों न इस उदाहरण को देखें?
-- In the products inventory, transfer all products of Vendor 1602 to ----
-- location 6
USE [AdventureWorks]
GO
UPDATE [Production].[ProductInventory]
SET LocationID = 6
WHERE ProductID IN
(SELECT ProductID
FROM Purchasing.ProductVendor
WHERE BusinessEntityID = 1602)
GO
क्या आपने देखा कि हमने वहां क्या किया?
बात यह है कि, आप उपश्रेणियों को WHERE . में डाल सकते हैं एक अद्यतन . का खंड बयान।
चूंकि हमारे पास यह उदाहरण में नहीं है, आप SET के लिए एक सबक्वेरी का भी उपयोग कर सकते हैं SET . जैसा खंड कॉलम =(सबक्वेरी) . लेकिन सावधान रहें:इसे एकल मान आउटपुट करना चाहिए क्योंकि अन्यथा, एक त्रुटि होती है।
हम आगे क्या करते हैं?
INSERT कथनों में SQL उपश्रेणियों का उपयोग करना
जैसा कि आप पहले से ही जानते हैं, आप चयन . का उपयोग करके तालिका में रिकॉर्ड सम्मिलित कर सकते हैं बयान। मुझे यकीन है कि आपको इस बात का अंदाजा है कि सबक्वेरी संरचना क्या होगी, लेकिन इसे एक उदाहरण के साथ प्रदर्शित करते हैं:
-- Impose a salary increase for all employees in DepartmentID 6
-- (Research and Development) by 10 (dollars, I think)
-- effective June 1, 2020
USE [AdventureWorks]
GO
INSERT INTO [HumanResources].[EmployeePayHistory]
([BusinessEntityID]
,[RateChangeDate]
,[Rate]
,[PayFrequency]
,[ModifiedDate])
SELECT
a.BusinessEntityID
,'06/01/2020' as RateChangeDate
,(SELECT MAX(b.Rate) FROM [HumanResources].[EmployeePayHistory] b
WHERE a.BusinessEntityID = b.BusinessEntityID) + 10 as NewRate
,2 as PayFrequency
,getdate() as ModifiedDate
FROM [HumanResources].[EmployeeDepartmentHistory] a
WHERE a.DepartmentID = 6
and StartDate = (SELECT MAX(c.StartDate)
FROM HumanResources.EmployeeDepartmentHistory c
WHERE c.BusinessEntityID = a.BusinessEntityID)
तो, हम यहाँ क्या देख रहे हैं?
- पहली सबक्वेरी अतिरिक्त 10 जोड़ने से पहले किसी कर्मचारी की अंतिम वेतन दर प्राप्त करती है।
- दूसरी सबक्वेरी को कर्मचारी का अंतिम वेतन रिकॉर्ड मिलता है।
- आखिरकार, चुनें . का परिणाम कर्मचारी भुगतान इतिहास . में डाला जाता है टेबल.
अन्य T-SQL स्टेटमेंट में
चुनें . के अलावा , सम्मिलित करें , अपडेट करें , और हटाएं , आप निम्न में भी SQL उपश्रेणियों का उपयोग कर सकते हैं:
संग्रहीत कार्यविधियों और कार्यों में परिवर्तनीय घोषणाएं या SET कथन
मुझे इस उदाहरण का उपयोग करके स्पष्ट करने दें:
DECLARE @maxTransId int = (SELECT MAX(TransactionID)
FROM Production.TransactionHistory)
वैकल्पिक रूप से, आप इसे निम्न तरीके से कर सकते हैं:
DECLARE @maxTransId int
SET @maxTransId = (SELECT MAX(TransactionID)
FROM Production.TransactionHistory)
सशर्त अभिव्यक्तियों में
आप इस उदाहरण पर एक नज़र क्यों नहीं डालते:
IF EXISTS(SELECT [Name] FROM sys.tables where [Name] = 'MyVendors')
BEGIN
DROP TABLE MyVendors
END
इसके अलावा, हम इसे इस तरह कर सकते हैं:
IF (SELECT count(*) FROM MyVendors) > 0
BEGIN
-- insert code here
END
तुलना या लॉजिकल ऑपरेटर्स के साथ SQL सबक्वेरी बनाएं
अब तक, हमने बराबर देखा है (= ) ऑपरेटर और IN ऑपरेटर। लेकिन तलाशने के लिए और भी बहुत कुछ है।
तुलना ऑपरेटरों का उपयोग करना
जब एक तुलना ऑपरेटर जैसे =, <,>, <>,>=, या <=का उपयोग सबक्वेरी के साथ किया जाता है, तो सबक्वेरी को एक मान वापस करना चाहिए। इसके अलावा, एक त्रुटि तब होती है जब सबक्वेरी एक से अधिक मान लौटाती है।
नीचे दिया गया उदाहरण रनटाइम त्रुटि उत्पन्न करेगा।
USE [AdventureWorks]
GO
SELECT b.LastName, b.FirstName, b.MiddleName, a.JobTitle, a.BusinessEntityID
FROM HumanResources.Employee a
INNER JOIN Person.Person b on a.BusinessEntityID = b.BusinessEntityID
INNER JOIN HumanResources.EmployeeDepartmentHistory c on a.BusinessEntityID
= c.BusinessEntityID
WHERE c.DepartmentID = 6
and StartDate = (SELECT d.StartDate
FROM HumanResources.EmployeeDepartmentHistory d
WHERE d.BusinessEntityID = a.BusinessEntityID)
क्या आप जानते हैं कि उपरोक्त कोड में क्या गलत है?
सबसे पहले, कोड सबक्वेरी के साथ बराबर (=) ऑपरेटर का उपयोग करता है। इसके अतिरिक्त, सबक्वेरी प्रारंभ तिथियों की एक सूची देता है।
समस्या को ठीक करने के लिए, सबक्वेरी को MAX . जैसे फ़ंक्शन का उपयोग करने के लिए कहें () प्रारंभ दिनांक कॉलम पर एकल मान वापस करने के लिए।
लॉजिकल ऑपरेटरों का उपयोग करना
EXISTS का उपयोग करना या EXISTS का उपयोग नहीं करना
मौजूद हैं रिटर्न सत्य यदि सबक्वेरी कोई पंक्तियाँ लौटाती है। अन्यथा, यह FALSE returns लौटाता है . इस बीच, नहीं . का उपयोग कर रहे हैं मौजूद हैं सत्य लौटाएगा यदि कोई पंक्तियाँ नहीं हैं और FALSE , अन्यथा।
नीचे दिए गए उदाहरण पर विचार करें:
IF EXISTS(SELECT name FROM sys.tables where name = 'Token')
BEGIN
DROP TABLE Token
END
सबसे पहले, मुझे समझाने की अनुमति दें। यदि यह sys.tables . में पाया जाता है, तो उपरोक्त कोड तालिका टोकन को छोड़ देगा , जिसका अर्थ है यदि यह डेटाबेस में मौजूद है। एक अन्य बिंदु:कॉलम नाम का संदर्भ अप्रासंगिक है।
ऐसा क्यों है?
यह पता चला है कि डेटाबेस इंजन को केवल EXISTS . का उपयोग करके कम से कम 1 पंक्ति प्राप्त करने की आवश्यकता है . हमारे उदाहरण में, यदि सबक्वेरी एक पंक्ति लौटाती है, तो तालिका हटा दी जाएगी। दूसरी ओर, अगर सबक्वेरी ने एक भी पंक्ति नहीं लौटाई, तो बाद के बयानों को निष्पादित नहीं किया जाएगा।
इस प्रकार, EXISTS . की चिंता केवल पंक्तियाँ हैं और कोई स्तंभ नहीं हैं।
इसके अतिरिक्त, मौजूद दो-मूल्यवान तर्क का उपयोग करता है:सत्य या गलत . ऐसा कोई मामला नहीं है कि यह NULL लौटाएगा . ऐसा ही तब होता है जब आप EXISTS . को नकारते हैं नहीं . का उपयोग कर रहे हैं ।
IN या NOT IN का उपयोग करना
IN . के साथ पेश की गई एक सबक्वेरी या में नहीं शून्य या अधिक मानों की सूची लौटाएगा। और EXISTS के विपरीत , उपयुक्त डेटा प्रकार के साथ एक मान्य कॉलम की आवश्यकता है।
मैं इसे एक और उदाहरण से स्पष्ट करता हूं:
-- From the product inventory, extract the products that are available
-- (Quantity >0)
-- except for products from Vendor 1676, and introduce a price cut for the --- whole month of June 2020.
-- Insert the results in product price history.
USE [AdventureWorks]
GO
INSERT INTO [Production].[ProductListPriceHistory]
([ProductID]
,[StartDate]
,[EndDate]
,[ListPrice]
,[ModifiedDate])
SELECT
a.ProductID
,'06/01/2020' as StartDate
,'06/30/2020' as EndDate
,a.ListPrice - 2 as ReducedListPrice
,getdate() as ModifiedDate
FROM [Production].[ProductListPriceHistory] a
WHERE a.StartDate = (SELECT MAX(StartDate)
FROM Production.ProductListPriceHistory
WHERE ProductID = a.ProductID)
AND a.ProductID IN (SELECT ProductID
FROM Production.ProductInventory
WHERE Quantity > 0)
AND a.ProductID NOT IN (SELECT ProductID
FROM [Purchasing].[ProductVendor]
WHERE BusinessEntityID = 1676
जैसा कि आप उपरोक्त कोड से देख सकते हैं, दोनों IN और में नहीं ऑपरेटरों को पेश किया गया है। और दोनों ही मामलों में, पंक्तियों को वापस कर दिया जाएगा। एक उत्पाद जो हाथ में है और एक उत्पाद जो विक्रेता 1676 से नहीं है, प्राप्त करने के लिए बाहरी क्वेरी में प्रत्येक पंक्ति का मिलान प्रत्येक सबक्वेरी के परिणाम से किया जाएगा।
एसक्यूएल सबक्वेरी का नेस्टिंग
आप 32 स्तरों तक भी उपश्रेणियों को नेस्ट कर सकते हैं। फिर भी, यह क्षमता सर्वर की उपलब्ध मेमोरी और क्वेरी में अन्य अभिव्यक्तियों की जटिलता पर निर्भर करती है।
इस पर आपकी क्या राय है?
मेरे अनुभव में, मुझे 4 तक नेस्टिंग याद नहीं है। मैं शायद ही कभी 2 या 3 स्तरों का उपयोग करता हूं। लेकिन यह सिर्फ मैं और मेरी आवश्यकताएं हैं।
इसका पता लगाने के लिए एक अच्छा उदाहरण कैसा होगा:
-- List down the names of employees who are also customers.
USE [AdventureWorks]
GO
SELECT
LastName
,FirstName
,MiddleName
FROM Person.Person
WHERE BusinessEntityID IN (SELECT BusinessEntityID
FROM Sales.Customer
WHERE BusinessEntityID IN
(SELECT BusinessEntityID
FROM HumanResources.Employee))
जैसा कि हम इस उदाहरण में देख सकते हैं, नेस्टिंग 2 स्तरों तक पहुंच गई है।
क्या SQL सबक्वेरी प्रदर्शन के लिए खराब हैं?
संक्षेप में:हाँ और नहीं। दूसरे शब्दों में, यह निर्भर करता है।
और मत भूलो, यह SQL सर्वर के संदर्भ में है।
शुरुआत के लिए, उप-प्रश्नों का उपयोग करने वाले कई टी-एसक्यूएल कथन वैकल्पिक रूप से जॉइन का उपयोग करके फिर से लिखे जा सकते हैं एस। और दोनों के लिए प्रदर्शन आमतौर पर समान होता है। इसके बावजूद, ऐसे विशेष मामले हैं जब एक जुड़ाव तेज होता है। और ऐसे मामले भी होते हैं जब सबक्वेरी अधिक तेज़ी से काम करती है।
उदाहरण 1
आइए एक सबक्वायरी उदाहरण की जांच करें। उन्हें क्रियान्वित करने से पहले, Control-M press दबाएं या वास्तविक निष्पादन योजना शामिल करें enable सक्षम करें SQL सर्वर प्रबंधन स्टूडियो के टूलबार से।
USE [AdventureWorks]
GO
SELECT Name
FROM Production.Product
WHERE ListPrice = SELECT ListPrice
FROM Production.Product
WHERE Name = 'Touring End Caps')
वैकल्पिक रूप से, उपरोक्त क्वेरी को एक ही परिणाम देने वाले जॉइन का उपयोग करके फिर से लिखा जा सकता है।
USE [AdventureWorks]
GO
SELECT Prd1.Name
FROM Production.Product AS Prd1
INNER JOIN Production.Product AS Prd2 ON (Prd1.ListPrice = Prd2.ListPrice)
WHERE Prd2.Name = 'Touring End Caps'
अंत में, दोनों प्रश्नों का परिणाम 200 पंक्तियाँ हैं।
साथ ही, आप दोनों कथनों के लिए निष्पादन योजना देख सकते हैं।
चित्र 1:एक सबक्वेरी का उपयोग करके निष्पादन योजना
चित्र 2:एक जॉइन का उपयोग करके निष्पादन योजना
तुम क्या सोचते हो? क्या वे व्यावहारिक रूप से समान हैं? प्रत्येक नोड के वास्तविक बीते हुए समय को छोड़कर, बाकी सब मूल रूप से समान है।
लेकिन यहाँ दृश्य अंतर से अलग इसकी तुलना करने का एक और तरीका है। मेरा सुझाव है कि शोप्लान की तुलना करें . का उपयोग करें ।
इसे करने के लिए, इन चरणों का पालन करें:
- सबक्वायरी का उपयोग करके स्टेटमेंट की निष्पादन योजना पर राइट-क्लिक करें।
- निष्पादन योजना को इस रूप में सहेजेंSelect चुनें ।
- फ़ाइल को नाम दें subquery-execution-plan.sqlplan ।
- जॉइन का उपयोग करके स्टेटमेंट की निष्पादन योजना पर जाएं और उस पर राइट-क्लिक करें।
- शोप्लान की तुलना करें का चयन करें ।
- #3 में आपके द्वारा सहेजे गए फ़ाइल नाम का चयन करें।
अब, शोप्लान की तुलना करें . के बारे में अधिक जानकारी के लिए इसे देखें ।
आपको कुछ ऐसा ही देखने में सक्षम होना चाहिए:
चित्र 3:एक सबक्वेरी का उपयोग करके जॉइन बनाम शोप्लान का उपयोग करने के लिए शोप्लान की तुलना करें
समानताओं पर ध्यान दें:
- अनुमानित पंक्तियां और लागतें समान हैं।
- QueryPlanHash भी वही है, जिसका अर्थ है कि उनके पास समान निष्पादन योजनाएं हैं।
फिर भी, मतभेदों पर ध्यान दें:
- सबक्वायरी का उपयोग करने की तुलना में शामिल होने का उपयोग करके कैश योजना का आकार बड़ा होता है
- केबी में मेमोरी सहित सीपीयू और समय (एमएस में) को संकलित करें, जो निष्पादन योजना को पार्स, बाइंड और ऑप्टिमाइज़ करने के लिए उपयोग किया जाता है, सबक्वेरी का उपयोग करने की तुलना में जॉइन का उपयोग करने से अधिक है
- योजना को क्रियान्वित करने के लिए सीपीयू समय और बीता हुआ समय (एमएस में) जॉइन बनाम सबक्वेरी का उपयोग करके थोड़ा अधिक है
इस उदाहरण में, सबक्वेरी शामिल होने की तुलना में अधिक तेज़ है, भले ही परिणामी पंक्तियाँ समान हों।
उदाहरण 2
पिछले उदाहरण में, हमने केवल एक तालिका का उपयोग किया था। निम्नलिखित उदाहरण में, हम 3 अलग-अलग तालिकाओं का उपयोग करने जा रहे हैं।
आइए ऐसा करते हैं:
-- Subquery example
USE [AdventureWorks]
GO
SELECT [SalesOrderID], [OrderDate], [ShipDate], [CustomerID]
FROM Sales.SalesOrderHeader
WHERE [CustomerID] IN (SELECT c.[CustomerID] FROM Sales.Customer c
INNER JOIN Person.Person p ON c.PersonID =
p.BusinessEntityID
WHERE p.PersonType='SC')
-- Join example
USE [AdventureWorks]
GO
SELECT o.[SalesOrderID], o.[OrderDate], o.[ShipDate], o.[CustomerID]
FROM Sales.SalesOrderHeader o
INNER JOIN Sales.Customer c on o.CustomerID = c.CustomerID
INNER JOIN Person.Person p ON c.PersonID = p.BusinessEntityID
WHERE p.PersonType = 'SC'
दोनों क्वेरीज़ समान 3806 पंक्तियों को आउटपुट करती हैं।
आगे, आइए उनकी निष्पादन योजनाओं पर एक नज़र डालते हैं:
चित्र 4:एक सबक्वेरी का उपयोग करके हमारे दूसरे उदाहरण के लिए निष्पादन योजना
चित्र 5:एक जॉइन का उपयोग करके हमारे दूसरे उदाहरण के लिए निष्पादन योजना
क्या आप 2 निष्पादन योजनाओं को देख सकते हैं और उनके बीच कोई अंतर पा सकते हैं? एक नज़र में वे एक जैसे दिखते हैं।
लेकिन शोप्लान की तुलना करें . के साथ अधिक सावधानीपूर्वक जांच करें वास्तव में अंदर क्या है प्रकट करता है।
चित्र 6:दूसरे उदाहरण के लिए शोप्लान की तुलना करें का विवरण
आइए कुछ समानताओं का विश्लेषण करके शुरुआत करें:
- निष्पादन योजना में गुलाबी हाइलाइट दोनों प्रश्नों के लिए समान संचालन का खुलासा करता है। चूंकि आंतरिक क्वेरी नेस्टिंग सबक्वेरी के बजाय जॉइन का उपयोग करती है, यह काफी समझ में आता है।
- अनुमानित ऑपरेटर और सबट्री लागत समान हैं।
इसके बाद, आइए अंतरों पर एक नज़र डालें:
- सबसे पहले, जब हम जॉइन करते थे तो संकलन में अधिक समय लगता था। आप इसे कंपाइल सीपीयू और कंपाइल टाइम में देख सकते हैं। हालांकि, सबक्वेरी वाली क्वेरी ने KB में उच्च कंपाइल मेमोरी ली।
- फिर, दोनों प्रश्नों का QueryPlanHash अलग है, जिसका अर्थ है कि उनकी एक अलग निष्पादन योजना है।
- अंत में, योजना को क्रियान्वित करने के लिए बीता हुआ समय और CPU समय शामिल होने का उपयोग करने में तेज़ है सबक्वेरी का उपयोग करने के बजाय।
सबक्वायरी बनाम जॉइन परफॉर्मेंस टेकअवे
आपको कई अन्य क्वेरी-संबंधी समस्याओं का सामना करने की संभावना है, जिन्हें शामिल होने या सबक्वेरी का उपयोग करके हल किया जा सकता है।
लेकिन नीचे की रेखा एक सबक्वायरी है जो जुड़ने की तुलना में स्वाभाविक रूप से खराब नहीं है। और इस बात का कोई नियम नहीं है कि किसी विशेष स्थिति में एक सबक्वायरी या दूसरी तरह से शामिल होना बेहतर होता है।
इसलिए, यह सुनिश्चित करने के लिए कि आपके पास सबसे अच्छा विकल्प है, निष्पादन योजनाओं की जांच करें। इसका उद्देश्य यह जानना है कि SQL सर्वर किसी विशेष क्वेरी को कैसे संसाधित करेगा।
हालांकि, यदि आप एक सबक्वेरी का उपयोग करना चुनते हैं, तो सावधान रहें कि समस्याएं उत्पन्न हो सकती हैं जो आपके कौशल का परीक्षण करेंगी।
SQL उपश्रेणियों का उपयोग करने में सामान्य चेतावनी
SQL सबक्वायरीज़ का उपयोग करते समय 2 सामान्य समस्याएं हैं जिनके कारण आपकी क्वेरीज़ बेतहाशा व्यवहार कर सकती हैं।
कॉलम नाम समाधान का दर्द
यह समस्या आपके प्रश्नों में तार्किक बग पेश करती है और उन्हें ढूंढना बहुत मुश्किल हो सकता है। एक उदाहरण इस समस्या को और स्पष्ट कर सकता है।
आइए डेमो उद्देश्यों के लिए एक तालिका बनाकर और इसे डेटा से भरकर शुरू करें।
USE [AdventureWorks]
GO
-- Create the table for our demonstration based on Vendors
CREATE TABLE Purchasing.MyVendors
(
BusinessEntity_id int,
AccountNumber nvarchar(15),
Name nvarchar(50)
)
GO
-- Populate some data to our new table
INSERT INTO Purchasing.MyVendors
SELECT BusinessEntityID, AccountNumber, Name
FROM Purchasing.Vendor
WHERE BusinessEntityID IN (SELECT BusinessEntityID
FROM Purchasing.ProductVendor)
AND BusinessEntityID like '14%'
GO
अब जब तालिका सेट हो गई है, तो आइए इसका उपयोग करते हुए कुछ उपश्रेणियों को सक्रिय करें। लेकिन नीचे दी गई क्वेरी को निष्पादित करने से पहले, याद रखें कि पिछले कोड से हमने जिस विक्रेता आईडी का उपयोग किया है, वह '14' से शुरू होती है।
SELECT b.Name, b.ListPrice, a.BusinessEntityID
FROM Purchasing.ProductVendor a
INNER JOIN Production.Product b on a.ProductID = b.ProductID
WHERE a.BusinessEntityID IN (SELECT BusinessEntityID
FROM Purchasing.MyVendors)
उपरोक्त कोड त्रुटियों के बिना चलता है, जैसा कि आप नीचे देख सकते हैं। वैसे भी, अपना ध्यान BusinessEntityIDs . की सूची पर दें ।
चित्र 7:परिणाम सेट के BusinessEntityIDs MyVendors तालिका के रिकॉर्ड के साथ असंगत हैं
क्या हमने BusinessEntityID . के साथ डेटा नहीं डाला '14' से शुरू? फिर क्या बात है? वास्तव में, हम BusinessEntityIDs . देख सकते हैं जो '15' और '16' से शुरू होते हैं। ये कहां से आए?
दरअसल, क्वेरी में ProductVendor . के सभी डेटा सूचीबद्ध हैं टेबल।
उस स्थिति में, आप सोच सकते हैं कि एक उपनाम इस समस्या का समाधान करेगा ताकि वह MyVendors को संदर्भित कर सके। नीचे दी गई तालिका की तरह:
चित्र 8:BusinessEntityID में उपनाम जोड़ने से त्रुटि होती है
सिवाय इसके कि अब वास्तविक समस्या रनटाइम त्रुटि के कारण दिखाई दी।
MyVendors . देखें तालिका फिर से और आप देखेंगे कि इसके बजाय BusinessEntityID , कॉलम का नाम BusinessEntity_id . होना चाहिए (अंडरस्कोर के साथ)।
इस प्रकार, सही कॉलम नाम का उपयोग करने से अंततः यह समस्या ठीक हो जाएगी, जैसा कि आप नीचे देख सकते हैं:
चित्र 9:सबक्वेरी को सही कॉलम नाम से बदलने से समस्या हल हो गई
जैसा कि आप ऊपर देख सकते हैं, अब हम BusinessEntityIDs . देख सकते हैं जैसा हमने पहले उम्मीद की थी, '14' से शुरू करें।
लेकिन आप सोच सकते हैं: पहली बार में SQL सर्वर ने पृथ्वी पर क्वेरी को सफलतापूर्वक चलाने की अनुमति क्यों दी?
यहां किकर है:बिना किसी उपनाम वाले कॉलम नामों का संकल्प सबक्वायरी के संदर्भ में बाहरी क्वेरी से बाहर जाने से काम करता है। इसीलिए BusinessEntityID . का संदर्भ सबक्वेरी के अंदर त्रुटि ट्रिगर नहीं हुई क्योंकि यह सबक्वेरी के बाहर पाई जाती है - उत्पाद विक्रेता में टेबल।
दूसरे शब्दों में, SQL सर्वर गैर-अलियास कॉलम की तलाश करता है BusinessEntityID MyVendors . में टेबल। चूंकि यह वहां नहीं है, इसलिए उसने बाहर देखा और उसे उत्पाद विक्रेता में पाया टेबल। पागल, है ना?
आप कह सकते हैं कि यह SQL सर्वर में एक बग है, लेकिन, वास्तव में, यह SQL मानक में डिज़ाइन द्वारा है और Microsoft ने इसका अनुसरण किया।
ठीक है, यह स्पष्ट है, हम मानक के बारे में कुछ नहीं कर सकते, लेकिन हम किसी त्रुटि से कैसे बच सकते हैं?
- सबसे पहले, कॉलम नामों को तालिका नाम के साथ उपसर्ग करें या उपनाम का उपयोग करें। दूसरे शब्दों में, गैर-उपसर्ग या गैर-अलियास तालिका नामों से बचें।
- दूसरा, कॉलम का लगातार नामकरण है। BusinessEntityID . दोनों के होने से बचें और BusinessEntity_id , उदाहरण के लिए।
बढ़िया है? हाँ, इससे स्थिति में थोड़ी समझदारी आती है।
लेकिन यह इसका अंत नहीं है।
क्रेज़ी NULLs
जैसा कि मैंने उल्लेख किया है, कवर करने के लिए और भी बहुत कुछ है। NULL . के समर्थन के कारण T-SQL 3-मान तर्क का उपयोग करता है . और नल जब हम NOT IN . के साथ SQL सबक्वायरी का उपयोग करते हैं, तो हमें लगभग पागल कर सकते हैं ।
मैं इस उदाहरण को पेश करके शुरू करता हूं:
SELECT b.Name, b.ListPrice, a.BusinessEntityID
FROM Purchasing.ProductVendor a
INNER JOIN Production.Product b on a.ProductID = b.ProductID
WHERE a.BusinessEntityID NOT IN (SELECT c.BusinessEntity_id
FROM Purchasing.MyVendors c)
क्वेरी का आउटपुट हमें उन उत्पादों की सूची में ले जाता है जो MyVendors . में नहीं हैं तालिका।, जैसा कि नीचे देखा गया है:
चित्र 10:NOT IN का उपयोग करके नमूना क्वेरी का आउटपुट
अब, मान लीजिए कि किसी ने अनजाने में MyVendors . में एक रिकॉर्ड डाला है नल . के साथ तालिका BusinessEntity_id . हम उसके बारे में क्या करने जा रहे हैं?
चित्र 11:MyVendors में NULL BusinessEntity_id डालने पर परिणाम सेट खाली हो जाता है
सारा डेटा कहां गया?
आप देखें, नहीं ऑपरेटर ने IN . को अस्वीकार कर दिया विधेय तो, सच नहीं अब FALSE . हो जाएगा . लेकिन पूर्ण नहीं अज्ञात है। इसने फ़िल्टर को उन पंक्तियों को त्यागने का कारण बना दिया जो UNKNOWN हैं, और यही अपराधी है।
यह सुनिश्चित करने के लिए कि आपके साथ ऐसा न हो:
- या तो तालिका कॉलम को NULL की अनुमति न दें अगर डेटा उस तरह से नहीं होना चाहिए।
- या कॉलम_नाम जोड़ें शून्य नहीं है आपके कहां . को खंड। हमारे मामले में, सबक्वेरी इस प्रकार है:
SELECT b.Name, b.ListPrice, a.BusinessEntityID
FROM Purchasing.ProductVendor a
INNER JOIN Production.Product b on a.ProductID = b.ProductID
WHERE a.BusinessEntityID NOT IN (SELECT c.BusinessEntity_id
FROM Purchasing.MyVendors c
WHERE c.BusinessEntity_id IS NOT NULL)
टेकअवे
हमने उपश्रेणियों के बारे में काफी बात की है, और समय आ गया है कि इस पोस्ट के मुख्य अंशों को एक संक्षिप्त सूची के रूप में प्रदान किया जाए:
एक सबक्वेरी:
- एक क्वेरी के भीतर एक क्वेरी है।
- कोष्ठक में संलग्न है।
- किसी भी व्यंजक को कहीं भी प्रतिस्थापित कर सकते हैं।
- इसका उपयोग चयन में किया जा सकता है , सम्मिलित करें , अपडेट करें , हटाएं, या अन्य टी-एसक्यूएल स्टेटमेंट।
- स्व-निहित या सहसंबद्ध हो सकता है।
- एकल, एकाधिक या तालिका मान आउटपुट करता है।
- तुलना ऑपरेटरों जैसे =, <>,>, <,>=, <=और तार्किक ऑपरेटरों जैसे IN पर काम करता है /में नहीं और मौजूद /मौजूद नहीं है ।
- बुरा या बुरा नहीं है। यह शामिल हों . से बेहतर या खराब प्रदर्शन कर सकता है एस एक स्थिति पर निर्भर करता है। इसलिए मेरी सलाह लें और हमेशा निष्पादन योजनाओं की जांच करें।
- NULL पर बुरा व्यवहार कर सकते हैं s जब नहीं में . के साथ प्रयोग किया जाता है , and when a column is not explicitly identified with a table or table alias.
Get familiarized with several additional references for your reading pleasure:
- Discussion of Subqueries from Microsoft.
- IN (Transact-SQL)
- EXISTS (Transact-SQL)
- ALL (Transact-SQL)
- SOME | ANY (Transact-SQL)
- Comparison Operators