एक्ज़िस्टिस्ट 1 या एक्सिस्ट्स * का उपयोग कर उपश्रेणी


88

मैं इस तरह से अपनी परीक्षाएँ लिखता था:

IF EXISTS (SELECT * FROM TABLE WHERE Columns=@Filters)
BEGIN
   UPDATE TABLE SET ColumnsX=ValuesX WHERE Where Columns=@Filters
END

पिछले जन्म में डीबीए में से एक ने मुझे बताया था कि जब मैं एक EXISTSखंड करता हूं, तो SELECT 1इसके बजाय उपयोग करेंSELECT *

IF EXISTS (SELECT 1 FROM TABLE WHERE Columns=@Filters)
BEGIN
   UPDATE TABLE SET ColumnsX=ValuesX WHERE Columns=@Filters
END

क्या इससे वास्तव में फर्क पड़ता है?


1
आप EXISTS (Select NULL FROM ...) को भूल गए। यह हाल ही में पूछा गया था
OMG पॉनीज ऑक्ट

16
ps को एक नया DBA मिलता है। अंधविश्वास का आईटी में कोई स्थान नहीं है, विशेष रूप से डेटाबेस प्रबंधन में (पूर्व डीबीए !!! से)
मैट रोजिश ऑक्ट

जवाबों:


135

नहीं, SQL सर्वर स्मार्ट है और जानता है कि इसका उपयोग EXISTS के लिए किया जा रहा है, और सिस्टम को NO DATA नहीं लौटाता है।

क्वॉथ माइक्रोसॉफ्ट: http://technet.microsoft.com/en-us/library/ms189259.aspx?ppud=4

EXISTS द्वारा शुरू की गई एक उपश्रेणी की चयन सूची में लगभग हमेशा एक तारांकन चिह्न (*) होता है। स्तंभ नामों को सूचीबद्ध करने का कोई कारण नहीं है क्योंकि आप अभी परीक्षण कर रहे हैं कि क्या पंक्तियाँ जो कि उपकुंजी में निर्दिष्ट शर्तों को पूरा करती हैं।

अपने आप को जांचने के लिए, निम्नलिखित को चलाने का प्रयास करें:

SELECT whatever
  FROM yourtable
 WHERE EXISTS( SELECT 1/0
                 FROM someothertable 
                WHERE a_valid_clause )

यदि यह वास्तव में SELECT लिस्ट के साथ कुछ कर रहा था, तो यह शून्य त्रुटि के द्वारा एक div को फेंक देगा। यह नहीं है

संपादित करें: ध्यान दें, SQL मानक वास्तव में इस बारे में बात करता है।

ANSI SQL 1992 स्टैंडर्ड, पृष्ठ 191 http://www.contrib.andrew.cmu.edu/~shadow/sql/sql1992.txt

3) का मामला:
एक) यदि <select list>"*" बस एक में निहित है <subquery> कि तुरंत एक में निहित है <exists predicate>, तो <select list> एक के बराबर है <value expression> एक मनमाना है कि <literal>


1
EXISTS1/0 के साथ चाल भी यह करने के लिए बढ़ाया जा सकता है SELECT 1 WHERE EXISTS(SELECT 1/0)... एक कदम और अधिक सार लगता है तो के रूप में दूसरा SELECTकोई नहीं है FROMखंड
whytheq

1
@whytheq - या SELECT COUNT(*) WHERE EXISTS(SELECT 1/0)। एक SELECTएक के बिना FROMएसक्यूएल सर्वर में व्यवहार किया जाता है जैसे कि यह एक ही पंक्ति तालिका ऐक्सेस कर रहे थे (जैसे से चयन करने के लिए इसी तरह की dualअन्य RDBMSs में टेबल)
मार्टिन स्मिथ

@MartinSmith चीयर्स - तो मुद्दा यह है कि SELECT1 पंक्ति तालिका बनाने से पहले यह कुछ और करता है भले ही 1/01 पंक्ति तालिका अभी भी बकवास है EXISTS?
Whytheq

क्या यह हमेशा मामला रहा है, या यह एक अनुकूलन है जो SQL सर्वर के किसी विशेष संस्करण में पेश किया गया था?
मार्टिन ब्राउन

1
@ मर्टिनस्मिथ टीआईएल "क्वॉथ"। इसे वापस ठीक करने के लिए धन्यवाद।
गुरविंदर सिंह

111

इस गलत धारणा का कारण संभवतः इस विश्वास के कारण है कि यह सभी कॉलमों को पढ़कर समाप्त हो जाएगा। यह देखना आसान है कि यह मामला नहीं है।

CREATE TABLE T
(
X INT PRIMARY KEY,
Y INT,
Z CHAR(8000)
)

CREATE NONCLUSTERED INDEX NarrowIndex ON T(Y)

IF EXISTS (SELECT * FROM T)
    PRINT 'Y'

योजना देता है

योजना

इससे पता चलता है कि SQL सर्वर इस तथ्य के बावजूद परिणाम को जांचने के लिए उपलब्ध सबसे संकीर्ण सूचकांक का उपयोग करने में सक्षम था कि सूचकांक में सभी कॉलम शामिल नहीं हैं। इंडेक्स एक्सेस एक सेमी जॉइन ऑपरेटर के तहत है, जिसका अर्थ है कि पहली पंक्ति में वापस आते ही यह स्कैन करना बंद कर सकता है।

तो यह स्पष्ट है कि उपरोक्त धारणा गलत है।

हालांकि क्वेरी ऑप्टिमाइज़र टीम के कॉनर कनिंघम ने यहां बताया कि वह SELECT 1इस मामले में आमतौर पर उपयोग करते हैं क्योंकि यह क्वेरी के संकलन में मामूली प्रदर्शन अंतर ला सकता है।

QP सभी का विस्तार और विस्तार करेगा * पाइपलाइन में का आरंभिक उन्हें वस्तुओं में बाँध देगा (इस मामले में, स्तंभों की सूची)। यह क्वेरी की प्रकृति के कारण अनावश्यक कॉलम हटा देगा।

तो EXISTSइस तरह एक सरल उपश्रेणी के लिए :

SELECT col1 FROM MyTable WHERE EXISTS (SELECT * FROM Table2 WHERE MyTable.col1=Table2.col2)*कुछ संभावित बड़ा स्तंभ सूची में विस्तार किया जाएगा और फिर इसे निर्धारित किया जाएगा कि के शब्दों EXISTS उन स्तंभों में से किसी की आवश्यकता नहीं है, तो मूल रूप से उन सभी हटाया जा सकता है।

" SELECT 1" क्वेरी संकलन के दौरान उस तालिका के लिए किसी भी अनावश्यक मेटाडेटा की जांच करने से बचना होगा।

हालाँकि, रनटाइम के दौरान क्वेरी के दो रूप समान होंगे और इसमें समान रनटाइम होंगे।

मैंने विभिन्न स्तंभों के साथ एक खाली टेबल पर इस क्वेरी को व्यक्त करने के चार संभावित तरीकों का परीक्षण किया। SELECT 1बनाम SELECT *बनाम SELECT Primary_Keyबनाम SELECT Other_Not_Null_Column

मैंने प्रश्नों को एक लूप में इस्तेमाल किया OPTION (RECOMPILE)और प्रति सेकंड निष्पादन की औसत संख्या को मापा। नीचे परिणाम

यहां छवि विवरण दर्ज करें

+-------------+----------+---------+---------+--------------+
| Num of Cols |    *     |    1    |   PK    | Not Null col |
+-------------+----------+---------+---------+--------------+
| 2           | 2043.5   | 2043.25 | 2073.5  | 2067.5       |
| 4           | 2038.75  | 2041.25 | 2067.5  | 2067.5       |
| 8           | 2015.75  | 2017    | 2059.75 | 2059         |
| 16          | 2005.75  | 2005.25 | 2025.25 | 2035.75      |
| 32          | 1963.25  | 1967.25 | 2001.25 | 1992.75      |
| 64          | 1903     | 1904    | 1936.25 | 1939.75      |
| 128         | 1778.75  | 1779.75 | 1799    | 1806.75      |
| 256         | 1530.75  | 1526.5  | 1542.75 | 1541.25      |
| 512         | 1195     | 1189.75 | 1203.75 | 1198.5       |
| 1024        | 694.75   | 697     | 699     | 699.25       |
+-------------+----------+---------+---------+--------------+
| Total       | 17169.25 | 17171   | 17408   | 17408        |
+-------------+----------+---------+---------+--------------+

के रूप में देखा जा सकता है के बीच कोई सुसंगत विजेता है SELECT 1और SELECT *और दोनों के बीच अंतर दृष्टिकोण नगण्य है। SELECT Not Null colऔर SELECT PKथोड़ा तेज हालांकि दिखाई देते हैं।

तालिका में स्तंभों की संख्या बढ़ने पर सभी चार प्रश्न प्रदर्शन में कम हो जाते हैं।

तालिका खाली होने के कारण यह संबंध केवल कॉलम मेटाडेटा की मात्रा से ही पता चलता है। के लिए COUNT(1)यह देखने के लिए कि यह फिर से लिखा जाता है आसान है COUNT(*)के नीचे से इस प्रक्रिया में कुछ बिंदु पर।

SET SHOWPLAN_TEXT ON;

GO

SELECT COUNT(1)
FROM master..spt_values

जो निम्न योजना देता है

  |--Compute Scalar(DEFINE:([Expr1003]=CONVERT_IMPLICIT(int,[Expr1004],0)))
       |--Stream Aggregate(DEFINE:([Expr1004]=Count(*)))
            |--Index Scan(OBJECT:([master].[dbo].[spt_values].[ix2_spt_values_nu_nc]))

SQL सर्वर प्रक्रिया के लिए डिबगर संलग्न करना और बेतरतीब ढंग से नीचे निष्पादित करते हुए तोड़ना

DECLARE @V int 

WHILE (1=1)
    SELECT @V=1 WHERE EXISTS (SELECT 1 FROM ##T) OPTION(RECOMPILE)

मैंने पाया कि उन मामलों में जहां टेबल में 1,024 कॉलम होते हैं, ज्यादातर समय कॉल स्टैक नीचे की तरह दिखता है, यह दर्शाता है कि यह वास्तव SELECT 1में उपयोग किए जाने पर भी कॉलम लोड मेटाडेटा का एक बड़ा हिस्सा खर्च कर रहा है (मामले के लिए जहां तालिका में 1 कॉलम बेतरतीब ढंग से टूट रहा है 10 प्रयासों में कॉल स्टैक के इस बिट को हिट नहीं किया है)

sqlservr.exe!CMEDAccess::GetProxyBaseIntnl()  - 0x1e2c79 bytes  
sqlservr.exe!CMEDProxyRelation::GetColumn()  + 0x57 bytes   
sqlservr.exe!CAlgTableMetadata::LoadColumns()  + 0x256 bytes    
sqlservr.exe!CAlgTableMetadata::Bind()  + 0x15c bytes   
sqlservr.exe!CRelOp_Get::BindTree()  + 0x98 bytes   
sqlservr.exe!COptExpr::BindTree()  + 0x58 bytes 
sqlservr.exe!CRelOp_FromList::BindTree()  + 0x5c bytes  
sqlservr.exe!COptExpr::BindTree()  + 0x58 bytes 
sqlservr.exe!CRelOp_QuerySpec::BindTree()  + 0xbe bytes 
sqlservr.exe!COptExpr::BindTree()  + 0x58 bytes 
sqlservr.exe!CScaOp_Exists::BindScalarTree()  + 0x72 bytes  
... Lines omitted ...
msvcr80.dll!_threadstartex(void * ptd=0x0031d888)  Line 326 + 0x5 bytes C
kernel32.dll!_BaseThreadStart@8()  + 0x37 bytes 

यह मैनुअल प्रोफाइलिंग प्रयास VS 2012 कोड प्रोफाइलर द्वारा समर्थित है, जो दो मामलों के लिए संकलन समय का उपभोग करने वाले कार्यों का एक बहुत अलग चयन दिखाता है ( शीर्ष 15 कार्य 1024 स्तंभ बनाम शीर्ष 15 कार्य 1 स्तंभ )।

दोनों SELECT 1और SELECT *संस्करण स्तंभ अनुमतियों की जाँच करते हैं और विफल हो जाते हैं यदि उपयोगकर्ता को तालिका के सभी स्तंभों तक पहुँच प्रदान नहीं की जाती है।

एक उदाहरण मैं ढेर पर एक बातचीत से घबरा गया

CREATE USER blat WITHOUT LOGIN;
GO
CREATE TABLE dbo.T
(
X INT PRIMARY KEY,
Y INT,
Z CHAR(8000)
)
GO

GRANT SELECT ON dbo.T TO blat;
DENY SELECT ON dbo.T(Z) TO blat;
GO
EXECUTE AS USER = 'blat';
GO

SELECT 1
WHERE  EXISTS (SELECT 1
               FROM   T); 
/*  ↑↑↑↑ 
Fails unexpectedly with 

The SELECT permission was denied on the column 'Z' of the 
           object 'T', database 'tempdb', schema 'dbo'.*/

GO
REVERT;
DROP USER blat
DROP TABLE T

तो कोई अनुमान लगा सकता है कि उपयोग करते समय मामूली स्पष्ट अंतर SELECT some_not_null_col यह है कि यह केवल उस विशिष्ट कॉलम पर जांच की अनुमति देता है (हालांकि अभी भी सभी के लिए मेटाडेटा लोड करता है)। हालांकि यह तथ्यों के साथ फिट नहीं लगता है क्योंकि दोनों दृष्टिकोणों के बीच प्रतिशत अंतर यदि कुछ भी छोटा हो जाता है तो अंतर्निहित तालिका में स्तंभों की संख्या बढ़ जाती है।

किसी भी सूरत में मैं इस फॉर्म में अपने सभी प्रश्नों को जल्दी से जल्दी नहीं बदलूंगा और क्योंकि यह अंतर बहुत ही मामूली है और केवल क्वेरी संकलन के दौरान स्पष्ट है। इसे हटा देना OPTION (RECOMPILE)ताकि बाद के निष्पादन एक कैश्ड योजना का उपयोग कर सकें, निम्नलिखित दिए गए हैं।

यहां छवि विवरण दर्ज करें

+-------------+-----------+------------+-----------+--------------+
| Num of Cols |     *     |     1      |    PK     | Not Null col |
+-------------+-----------+------------+-----------+--------------+
| 2           | 144933.25 | 145292     | 146029.25 | 143973.5     |
| 4           | 146084    | 146633.5   | 146018.75 | 146581.25    |
| 8           | 143145.25 | 144393.25  | 145723.5  | 144790.25    |
| 16          | 145191.75 | 145174     | 144755.5  | 146666.75    |
| 32          | 144624    | 145483.75  | 143531    | 145366.25    |
| 64          | 145459.25 | 146175.75  | 147174.25 | 146622.5     |
| 128         | 145625.75 | 143823.25  | 144132    | 144739.25    |
| 256         | 145380.75 | 147224     | 146203.25 | 147078.75    |
| 512         | 146045    | 145609.25  | 145149.25 | 144335.5     |
| 1024        | 148280    | 148076     | 145593.25 | 146534.75    |
+-------------+-----------+------------+-----------+--------------+
| Total       | 1454769   | 1457884.75 | 1454310   | 1456688.75   |
+-------------+-----------+------------+-----------+--------------+

मेरे द्वारा उपयोग की जाने वाली परीक्षण स्क्रिप्ट यहां पाई जा सकती है


3
+1 यह उत्तर वास्तविक डेटा प्राप्त करने के लिए किए गए प्रयास के लिए अधिक वोटों का हकदार है।
जॉन

1
SQL आँकड़ों के किस संस्करण पर ये आँकड़े उत्पन्न किए गए थे?
मार्टिन ब्राउन

3
@MartinBrown - IIRC मूल रूप से 2008 है, हालांकि मैंने 2012 में हाल ही में सबसे हाल ही में संपादित किए गए परीक्षणों को फिर से लिखा और वही पाया।
मार्टिन स्मिथ

8

पता करने का सबसे अच्छा तरीका दोनों संस्करणों का परीक्षण करना और दोनों संस्करणों के निष्पादन योजना की जांच करना है। बहुत सारे स्तंभों वाली एक तालिका चुनें।


2
+1। पता नहीं क्यों यह नीचे मतदान किया गया था। मैंने हमेशा सोचा कि एक आदमी को मछली सिखाना बेहतर था, सिर्फ उसे मछली देना। कैसे लोग कुछ भी सीखने जा रहे हैं?
ओगरे Psalm33

5

SQL Server में कोई अंतर नहीं है और यह SQL Server में कभी कोई समस्या नहीं रही है। आशावादी जानता है कि वे समान हैं। यदि आप निष्पादन योजनाओं को देखते हैं, तो आप देखेंगे कि वे समान हैं।


1

व्यक्तिगत रूप से मुझे यह बहुत मुश्किल लगता है, यह विश्वास करना बहुत मुश्किल है कि वे एक ही क्वेरी योजना के लिए अनुकूलन नहीं करते हैं। लेकिन आपकी विशेष स्थिति में जानने का एकमात्र तरीका यह परीक्षण करना है। यदि आप करते हैं, तो कृपया वापस रिपोर्ट करें!


-1

कोई वास्तविक अंतर नहीं है, लेकिन बहुत कम प्रदर्शन हो सकता है। अंगूठे के एक नियम के रूप में आपको जरूरत से ज्यादा डेटा नहीं मांगना चाहिए।

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