Sql सर्वर सरल बायजेक्शन पर इंडेक्स का उपयोग करने में विफल रहता है


11

यह एक और क्वेरी ऑप्टिमाइज़र है।

हो सकता है कि मैं सिर्फ क्वेरी ऑप्टिमाइज़र का अनुमान लगा रहा हूं, या शायद मुझे कुछ याद आ रहा है - इसलिए मैं इसे बाहर रख रहा हूं।

मेरे पास एक साधारण टेबल है

CREATE TABLE [dbo].[MyEntities](
  [Id] [uniqueidentifier] NOT NULL,
  [Number] [int] NOT NULL,
  CONSTRAINT [PK_dbo.MyEntities] PRIMARY KEY CLUSTERED ([Id])
)

CREATE NONCLUSTERED INDEX [IX_Number] ON [dbo].[MyEntities] ([Number])

एक इंडेक्स और कुछ हजार पंक्तियों के साथ, Number0, 1 और 2 के मानों में समान रूप से वितरित किया जा रहा है।

अब यह प्रश्न:

SELECT * FROM
    (SELECT
        [Extent1].[Number] AS [Number],
        CASE
        WHEN (0 = [Extent1].[Number]) THEN 'one'
        WHEN (1 = [Extent1].[Number]) THEN 'two'
        WHEN (2 = [Extent1].[Number]) THEN 'three'
        ELSE '?'
        END AS [Name]
        FROM [dbo].[MyEntities] AS [Extent1]
        ) P
WHERE P.Number = 0;

क्या कोई इंडेक्स चाहता है IX_Numberकि कोई उम्मीद करे।

अगर क्लॉज कहां है

WHERE P.Name = 'one';

हालाँकि, यह एक स्कैन बन जाता है।

केस-क्लॉज स्पष्ट रूप से एक आक्षेप है, इसलिए सिद्धांत रूप में एक अनुकूलन दूसरी क्वेरी से पहली क्वेरी योजना को निकालना संभव हो सकता है।

यह विशुद्ध रूप से अकादमिक नहीं है: क्वेरी अपने संबंधित अनुकूल नामों में एनम मूल्यों का अनुवाद करने से प्रेरित है।

मैं किसी ऐसे व्यक्ति से सुनना चाहता हूं जो यह जानता है कि क्वेरी ऑप्टिमाइज़र (और विशेष रूप से एसक्यूएल सर्वर में एक से) क्या उम्मीद की जा सकती है: क्या मैं बस बहुत अधिक उम्मीद कर रहा हूं?

मैं पूछ रहा हूं क्योंकि मेरे पास ऐसे मामले थे जहां एक क्वेरी के कुछ मामूली बदलाव अचानक एक अनुकूलन बन जाएंगे।

मैं Sql Server 2016 डेवलपर संस्करण का उपयोग कर रहा हूं।

जवाबों:


18

क्या मैं बहुत ज्यादा उम्मीद कर रहा हूं?

हाँ। कम से कम उत्पाद के वर्तमान संस्करणों में।

SQL सर्वर CASEस्टेटमेंट को अलग नहीं करेगा और इंजीनियर को यह पता लगाने के लिए रिवर्स करेगा कि यदि कंप्यूटेड कॉलम का परिणाम है 'one'तो [Extent1].[Number]होना चाहिए 0

आपको यह सुनिश्चित करने की आवश्यकता है कि आप अपने विधेय को व्यर्थ लिखते हैं। जो लगभग हमेशा इसे फॉर्म में शामिल करता है। basetable_column_name comparison_operator expression

यहां तक ​​कि मामूली विचलन भी बर्बरता को तोड़ते हैं।

WHERE P.Number + 0 = 0;

इंडेक्स की तलाश में या तो उपयोग नहीं करेगा, भले ही यह CASEअभिव्यक्ति की तुलना में सरल करने के लिए और भी अधिक सरल हो ।

यदि आप एक स्ट्रिंग नाम पर खोज करना चाहते हैं और नंबर पर एक तलाश करना चाहते हैं तो आपको नामों और नंबरों के साथ एक मैपिंग टेबल की आवश्यकता होगी और उस पर क्वेरी में शामिल हो सकते हैं, फिर योजना मैपिंग टेबल पर एक सहसंबद्ध की तलाश में हो सकती है। पर [dbo].[MyEntities]नंबर से लौटे के साथ पहले की तलाश है।


6

एक मामले के बयान के रूप में अपनी एनम को प्रोजेक्ट न करें। इसे एक व्युत्पन्न तालिका की तरह प्रोजेक्ट करें:

SELECT * FROM
   (SELECT
      [Extent1].[Number] AS [Number],
      enum.Name
   FROM
      [dbo].[MyEntities] AS [Extent1]
      LEFT JOIN (VALUES
         (0, 'one'),
         (1, 'two'),
         (2, 'three')
      ) enum (Number, Name)
         ON Extent1.Number = enum.Number
   ) P
WHERE
   P.Name = 'one';

मुझे संदेह है कि आपको बेहतर परिणाम मिलेंगे। (मैंने नाम ?गायब होने पर परिवर्तित नहीं किया था क्योंकि यह संभवतः प्रदर्शन लाभ के साथ हस्तक्षेप करेगा। हालाँकि, आप टेबल WHEREपर विधेय लगाने के लिए बाहरी क्वेरी के अंदर खंड को स्थानांतरित कर सकते हैं enum, या आप दो कॉलम वापस कर सकते हैं। आंतरिक क्वेरी, एक विधेय के लिए और एक प्रदर्शन के लिए, जहां विधेय एक है NULLजब कोई मिलान एनम मूल्य नहीं है।)

हालांकि, मैं अनुमान लगा रहा हूं कि [Extent1]वहां होने के कारण , आप एक ORM का उपयोग कर रहे हैं जैसे कि Entity Framework या Linq-To-SQL। मैं आपको मार्गदर्शन नहीं दे सकता कि इस तरह के प्रक्षेपण को कैसे पूरा किया जाए, लेकिन, आप एक अलग तकनीक का उपयोग कर सकते हैं।

मेरा एक प्रोजेक्ट में, मैंने डेटाबेस में वास्तविक तालिकाओं में कोड एनम मानों को प्रतिबिंबित किया, एक कस्टम-बिल्ड क्लास के माध्यम से जिसने डेटाबेस में एनम मूल्यों को मर्ज किया। (आपको उस नियम का सम्मान करना होगा जिसे आपको अपने एनम मूल्यों को स्पष्ट रूप से सूचीबद्ध करना होगा, अपनी तालिकाओं की समीक्षा के बिना कभी भी हटा नहीं सकता है, और कभी भी उन्हें बदल नहीं सकता है, हालांकि आपको पहले से ही अपने वर्तमान सेटअप के साथ कम से कम कुछ का निरीक्षण करना होगा) ।

अब, मैं एक Identifierबेस क्लास के एक एन्यूमरेबल का उपयोग कर रहा था जिसमें कई अलग-अलग कंक्रीट उपवर्ग हैं, लेकिन कोई कारण नहीं है कि यह एक सादे वेनिला एनम के साथ नहीं किया जा सकता है। यहाँ एक उदाहरण का उपयोग करें:

new EnumOrIdentifierProjector<CodeClassOrEnum, PrivateDbDtoObject>(
   _sqlConnector.Connection,
   "dbo.TableName",
   "PrimaryKeyId",
   "NameColumnName",
   dtoObject => dtoObject.PrimaryKeyId,
   dtoObject => dtoObject.NameField,
   EnumerableOfIdentifierOrTypeOfEnum
)
   .Populate();

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

विचार यह है कि एक बार आपके पास एक ऐसी तालिका होती है जो स्टार्टअप पर केवल एक बार पढ़ी / लिखी जाती है, जो कि सभी enum मानों को मज़बूती से देगी, आप बस किसी भी अन्य तालिका की तरह इसमें शामिल होंगे, और प्रदर्शन अच्छा होना चाहिए।

मुझे उम्मीद है कि ये विचार आपके लिए एक सुधार बनाने के लिए पर्याप्त हैं।


हां, मैं EntityFramework का उपयोग करता हूं और वहां है जहां समाधान वास्तव में एक इष्टतम दुनिया में होना चाहिए। ऐसा होने से पहले, आपका सुझाव मुझे विश्वास है कि सबसे अच्छे कार्यदलों में से एक है।
जॉन

5

मैं इस प्रश्न की व्याख्या करता हूं कि आप सामान्य रूप से ऑप्टिमाइज़र में रुचि रखते हैं, लेकिन SQL सर्वर के लिए एक विशेष रुचि के साथ। मैंने db2 LUW V11.1 के साथ आपके परिदृश्य का परीक्षण किया:

]$ db2 "create table myentities ( id int not null, number int not null )"
]$ db2 "create index ix_number on myentities (number)"
]$ db2 "insert into myentities (id, number) with t(n) as ( values 0 union all select n+1 from t where n<10000) select n, mod(n,3) from t"

DB2 में ऑप्टिमाइज़र पहले वाले को दूसरा प्रश्न फिर से लिखता है:

Original Statement:
------------------
SELECT 
  * 
FROM 
  (SELECT 
     number,

   CASE 
   WHEN (0 = Number) 
   THEN 'one' 
   WHEN (1 = Number) 
   THEN 'two' 
   WHEN (2 = Number) 
   THEN 'three' 
   ELSE '?' END AS Name 
   FROM 
     MyEntities
  ) P 
WHERE 
  P.name = 'one'


Optimized Statement:
-------------------
SELECT 
  Q1.NUMBER AS "NUMBER",

CASE 
WHEN (0 = Q1.NUMBER) 
THEN 'one' 
WHEN (1 = Q1.NUMBER) 
THEN 'two' 
WHEN (2 = Q1.NUMBER) 
THEN 'three' 
ELSE '?' END AS "NAME" 
FROM 
  LELLE.MYENTITIES AS Q1 
WHERE 
  (0 = Q1.NUMBER)

योजना इस प्रकार है:

Access Plan:
-----------
        Total Cost:             33.5483
        Query Degree:           1


      Rows 
     RETURN
     (   1)
      Cost 
       I/O 
       |
      3334 
     IXSCAN
     (   2)
     33.1861 
     4.66713 
       |
      10001 
 INDEX: LELLE   
    IX_NUMBER
       Q1

मैं अन्य ऑप्टिमाइज़र के बारे में ज्यादा नहीं जानता, लेकिन मुझे लगता है कि DB2 ऑप्टिमाइज़र प्रतियोगियों के बीच भी बहुत अच्छा माना जाता है।


यह रोमांचक है। क्या आप कुछ प्रकाश डाल सकते हैं जहां "अनुकूलित कथन" आता है? क्या db2 खुद को आपको वापस देता है? - साथ ही, मुझे प्लान पढ़ने में भी परेशानी होती है। मैं इसे लेता हूं "IXSCAN" का मतलब इस मामले में सूचकांक स्कैन नहीं है?
जॉन

1
आप के लिए एक बयान की व्याख्या करने के लिए आप DB2 बता सकते हैं। एकत्र की गई जानकारी तालिकाओं के एक सेट में संग्रहीत की जाती है, और आप या तो दृश्य व्याख्या का उपयोग कर सकते हैं या इस मामले में उपयोगिता db2exfmt (या अपना खुद का उपयोग बना सकते हैं)। इसके अलावा आप एक बयान की निगरानी कर सकते हैं और वास्तविक योजना के साथ योजना में अनुमानित कार्डिनैलिटी की तुलना कर सकते हैं। इस योजना में हम देख सकते हैं कि यह वास्तव में एक इंडेक्सस्कैन (IXSCAN) है और इस ऑपरेटर से अनुमानित आउटपुट 3334 पंक्तियाँ हैं। क्या यह SQL सर्वर में खराब है? यह startkey और stopkey जानता है इसलिए यह केवल DB2 में संबंधित पंक्तियों को स्कैन करता है।
लेनार्ट

इसलिए इसे स्कैन कहते हैं, जिसमें मांग करना शामिल है, और ईमानदार होने के लिए, एसक्यूएल सर्वर के समतुल्य योजना स्पष्टीकरण भी कभी-कभी कुछ ऐसा स्कैन कहते हैं जिसमें मांग शामिल होती है, और दूसरी बार यह एक तलाश है। मुझे हमेशा यह समझने की जरूरत है कि क्या है यह समझने के लिए पंक्ति गणना को देखें। चूंकि db2 के आउटपुट में स्पष्ट रूप से 3334 है, यह सुनिश्चित करता है कि मैं क्या उम्मीद कर रहा था। बहुत ही रोचक।
जॉन

हां, मुझे भी कभी-कभी यह गड़बड़ लगता है। प्रत्येक को प्रत्येक ऑपरेटर को वास्तव में समझने के लिए अधिक विस्तृत जानकारी को देखना होगा कि क्या हो रहा है।
लेनार्ट

0

इस विशेष प्रश्न में, यह बहुत मूर्खतापूर्ण है कि यहां तक ​​कि एक CASEबयान भी है । आप एक विशेष मामले को नीचे फ़िल्टर कर रहे हैं! शायद यह आपके द्वारा दिए गए विशेष उदाहरण क्वेरी का केवल एक विवरण है, लेकिन यदि नहीं, तो आप इस क्वेरी को समकक्ष परिणाम प्राप्त करने के लिए लिख सकते हैं:

SELECT
    [Extent1].[Number] AS [Number],
    'one' AS [Name]
FROM [dbo].[MyEntities] AS [Extent1]
WHERE [Extent1].[Number] = 0;

यह आपको बिल्कुल वही परिणाम सेट देगा, और चूंकि आप पहले से ही एक CASEबयान में कठिन कोडिंग मान रखते हैं , इसलिए आप यहां कोई स्थिरता नहीं खो रहे हैं।


1
मुझे लगता है कि आप इस बिंदु को याद कर रहे हैं - यह एसक्यूएल एक बैक-एंड कोडबेस से उत्पन्न होता है जो अपने स्ट्रिंग अभ्यावेदन के माध्यम से एनम के साथ काम करता है। कोड जो SQL प्रोजेक्ट कर रहा है वह क्वेरी को हिंसा कर रहा है। मुझे यकीन है कि पूछने वाला, अगर वह खुद एसक्यूएल लिख रहा था, तो बेहतर क्वेरी लिख पाएगा। इस प्रकार, यह मूर्खतापूर्ण नहीं है कि एक CASEबयान है, क्योंकि ओआरएम इस तरह की बात करते हैं। मूर्खतापूर्ण यह है कि आपने समस्या के इन सरल पहलुओं को नहीं पहचाना ... (यह कैसे अप्रत्यक्ष रूप से
मस्तिष्कविहीन

@ एरिक अभी भी मूर्खतापूर्ण है, क्योंकि आप केवल सी # मानकर, एनम के संख्यात्मक मान का उपयोग कर सकते हैं । (एक काफी सुरक्षित धारणा है कि हम SQL सर्वर बात कर रहे हैं।)
jpmc26

लेकिन आपके पास कोई विचार नहीं है कि उपयोग का मामला क्या है। शायद यह संख्यात्मक मान पर स्विच करने के लिए एक बड़ा बदलाव होगा। हो सकता है कि किसी मौजूदा विशाल कोड बेस में एनमॉर्फ़ रेट्रोफिट थे। ज्ञान के बिना आलोचना करना हास्यास्पद है।
एरिक

@ एरिक यदि यह हास्यास्पद है, तो आप ऐसा क्यों कर रहे हैं? =) मैंने केवल यह इंगित करने के लिए उत्तर दिया कि यदि उपयोग का मामला प्रश्न में उदाहरण के रूप में सरल है (जो कि मेरे उत्तर की प्रस्तावना में स्पष्ट रूप से निर्दिष्ट है), तो CASEकथन को पूरी तरह से बिना दोष के समाप्त किया जा सकता है। के पाठ्यक्रम वहाँ अज्ञात कारकों हो सकता है, लेकिन वे अनिर्दिष्ट कर रहे हैं।
jpmc26

मुझे आपके उत्तर के तथ्यात्मक भागों पर कोई आपत्ति नहीं है, केवल उन भागों पर जो विषयवस्तु की विशेषता रखते हैं। जैसे कि क्या मैं ज्ञान के बिना आलोचना कर रहा हूं, मैं सभी कानों को किसी भी तरह से समझने के लिए हूं जिसमें मैं स्पष्ट रूप से स्वच्छ तर्क का उपयोग करने में विफल रहा हूं या ऐसी धारणाएं बनाई हैं जो
राक्षसी रूप से
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.