एक स्कैन हो रही है, हालांकि मैं एक तलाश की उम्मीद है


9

मुझे एक SELECTकथन को अनुकूलित करने की आवश्यकता है लेकिन एसक्यूएल सर्वर हमेशा एक शोध के बजाय एक सूचकांक स्कैन करता है। यह वह प्रश्न है, जो निश्चित रूप से एक संग्रहीत प्रक्रिया में है:

CREATE PROCEDURE dbo.something
  @Status INT = NULL,
  @IsUserGotAnActiveDirectoryUser BIT = NULL    
AS

    SELECT [IdNumber], [Code], [Status], [Sex], 
           [FirstName], [LastName], [Profession], 
           [BirthDate], [HireDate], [ActiveDirectoryUser]
    FROM Employee
    WHERE (@Status IS NULL OR [Status] = @Status)
    AND 
    (
      @IsUserGotAnActiveDirectoryUser IS NULL 
      OR 
      (
        @IsUserGotAnActiveDirectoryUser IS NOT NULL AND       
        (
          @IsUserGotAnActiveDirectoryUser = 1 AND ActiveDirectoryUser <> ''
        )
        OR
        (
          @IsUserGotAnActiveDirectoryUser = 0 AND ActiveDirectoryUser = ''
        )
      )
    )

और यह सूचकांक है:

CREATE INDEX not_relevent ON dbo.Employee
(
    [Status] DESC,
    [ActiveDirectoryUser] ASC
)
INCLUDE (...all the other columns in the table...); 

योजना:

योजना चित्र

SQL सर्वर ने स्कैन क्यों चुना? मेरे द्वारा यह कैसे किया जा सकता है?

स्तंभ परिभाषाएँ:

[Status] int NOT NULL
[ActiveDirectoryUser] VARCHAR(50) NOT NULL

स्थिति पैरामीटर हो सकते हैं:

NULL: all status,
1: Status= 1 (Active employees)
2: Status = 2 (Inactive employees)

IsUserGotAnActiveDirectoryUser हो सकता है:

NULL: All employees
0: ActiveDirectoryUser is empty for that employee
1: ActiveDirectoryUser  got a valid value (not null and not empty)

क्या आप वास्तविक निष्पादन योजना को कहीं पोस्ट कर सकते हैं (इसका चित्र नहीं, लेकिन XML फॉर्म में .sqlplan फ़ाइल)? मेरा अनुमान है कि आपने प्रक्रिया बदल दी है लेकिन वास्तव में बयान स्तर पर एक नया संकलन नहीं मिला है। क्या आप क्वेरी के कुछ पाठ को बदल सकते हैं (जैसे तालिका नाम में स्कीमा उपसर्ग जोड़ना ), और फिर के लिए एक मान्य मान में पास करें @Status?
हारून बर्ट्रेंड

1
इसके अलावा इंडेक्स डेफिनेशन भी इस सवाल का जवाब देता है - कुंजी क्यों है Status DESC? इसके लिए कितने मान हैं Status, वे क्या हैं (यदि संख्या छोटी है), और क्या प्रत्येक मान लगभग समान रूप से दर्शाया गया है? हम में से उत्पादन दिखाएँSELECT TOP (20) [Status], c = COUNT(*) FROM dbo.Employee GROUP BY [Status] ORDER BY c DESC;
हारून बर्ट्रेंड

जवाबों:


11

मुझे नहीं लगता कि स्कैन एक रिक्त स्ट्रिंग की खोज के कारण होता है (और जब आप उस मामले के लिए फ़िल्टर किए गए सूचकांक जोड़ सकते हैं, तो यह केवल क्वेरी के बहुत विशिष्ट रूपों में मदद करेगा)। आप अधिक से अधिक पैरामीटर सूँघने का शिकार हो सकते हैं और एक भी योजना सभी मापदंडों (और पैरामीटर मूल्यों) के विभिन्न संयोजनों के लिए अनुकूलित नहीं है जो आप इस क्वेरी को प्रदान करेंगे।

मैं इसे "रसोई सिंक" प्रक्रिया कहता हूं , क्योंकि आप रसोई सिंक सहित सभी चीजों को प्रदान करने के लिए एक क्वेरी की उम्मीद कर रहे हैं।

मैं इस के लिए मेरे समाधान के बारे में वीडियो है यहाँ और यहाँ के साथ ही इसके बारे में एक ब्लॉग पोस्ट , लेकिन अनिवार्य रूप से, सबसे अच्छा अनुभव रहा ऐसी क्वेरी के लिए है:

  • स्टेटमेंट को गतिशील रूप से बनाएं - यह आपको उन कॉलमों का उल्लेख करने से रोक देगा, जिनके लिए कोई पैरामीटर नहीं दिया गया था, और यह सुनिश्चित करता है कि आपके पास एक योजना होगी जो वास्तविक मापदंडों के लिए ठीक से अनुकूलित है जो मानों के साथ पारित किए गए थे।
  • उपयोगOPTION (RECOMPILE) - यह विशिष्ट पैरामीटर मानों को गलत प्रकार की योजना को मजबूर करने से रोकता है, विशेष रूप से तब उपयोगी होता है जब आपके पास डेटा तिरछा, खराब आँकड़े होते हैं, या जब किसी कथन का पहला निष्पादन एक atypical मान का उपयोग करता है जो बाद में और अधिक बार की तुलना में एक अलग योजना का नेतृत्व करेगा फांसी।
  • सर्वर विकल्प का उपयोग करेंoptimize for ad hoc workloads - यह क्वेरी बदलाव को रोकता है जो केवल एक बार आपके प्लान कैश को प्रदूषित करने से उपयोग किया जाता है।

तदर्थ कार्यभार के लिए अनुकूलन सक्षम करें:

EXEC sys.sp_configure 'show advanced options', 1;
GO
RECONFIGURE WITH OVERRIDE;
GO
EXEC sys.sp_configure 'optimize for ad hoc workloads', 1;
GO
RECONFIGURE WITH OVERRIDE;
GO
EXEC sys.sp_configure 'show advanced options', 0;
GO
RECONFIGURE WITH OVERRIDE;

अपनी प्रक्रिया बदलें:

ALTER PROCEDURE dbo.Whatever
  @Status INT = NULL,
  @IsUserGotAnActiveDirectoryUser BIT = NULL
AS
BEGIN 
  SET NOCOUNT ON;
  DECLARE @sql NVARCHAR(MAX) = N'SELECT [IdNumber], [Code], [Status], 
     [Sex], [FirstName], [LastName], [Profession],
     [BirthDate], [HireDate], [ActiveDirectoryUser]
   FROM dbo.Employee -- please, ALWAYS schema prefix
   WHERE 1 = 1';

   IF @Status IS NOT NULL
     SET @sql += N' AND ([Status]=@Status)'

   IF @IsUserGotAnActiveDirectoryUser = 1
     SET @sql += N' AND ActiveDirectoryUser <> ''''';
   IF @IsUserGotAnActiveDirectoryUser = 0
     SET @sql += N' AND ActiveDirectoryUser = ''''';

   SET @sql += N' OPTION (RECOMPILE);';

   EXEC sys.sp_executesql @sql, N'@Status INT, @Status;
END
GO

एक बार आपके पास कार्यभार के आधार पर प्रश्नों का एक सेट होता है जिसे आप मॉनिटर कर सकते हैं, आप निष्पादन का विश्लेषण कर सकते हैं और देख सकते हैं कि किन लोगों को अतिरिक्त या अलग-अलग इंडेक्स से सबसे अधिक लाभ होगा - आप इसे विभिन्न कोणों से कर सकते हैं, सरल से "जो संयोजन मापदंडों को सबसे अधिक बार प्रदान किया जाता है? " "कौन से व्यक्तिगत प्रश्नों में सबसे लंबा रनटाइम है?" हम आपके कोड के आधार पर उन सवालों के जवाब नहीं दे सकते हैं, हम केवल यह सुझाव दे सकते हैं कि कोई भी सूचकांक केवल उन सभी संभावित संयोजनों के उपसमुच्चय के लिए सहायक होगा जो आप समर्थन करने का प्रयास कर रहे हैं। उदाहरण के लिए, यदि@StatusNULL है, तो उस गैर-संकुल सूचकांक के विरुद्ध कोई भी खोज संभव नहीं है। इसलिए उन मामलों के लिए जहां उपयोगकर्ता स्थिति के बारे में परवाह नहीं करते हैं, आप एक स्कैन प्राप्त करने जा रहे हैं, जब तक कि आपके पास एक सूचकांक नहीं है जो अन्य खंडों को पूरा करता है (लेकिन ऐसा कोई सूचकांक या तो उपयोगी नहीं होगा, आपके वर्तमान क्वेरी तर्क को देखते हुए। - या तो खाली स्ट्रिंग या खाली स्ट्रिंग बिल्कुल चयनात्मक नहीं है)।

इस मामले में, संभावित Statusमूल्यों के सेट पर निर्भर करता है और उन मूल्यों को कैसे वितरित किया जाता है, OPTION (RECOMPILE)आवश्यक नहीं हो सकता है। लेकिन अगर आपके पास कुछ मूल्य हैं जो 100 पंक्तियों और कुछ मानों का उत्पादन करेंगे जो सैकड़ों सैकड़ों पैदा करेंगे, तो आप इसे वहां भी (सीपीयू लागत पर, जो इस क्वेरी की जटिलता को देखते हुए सीमांत होना चाहिए), ताकि आप कर सकें अधिक से अधिक मामलों में तलाश करना। यदि मानों की सीमा पर्याप्त रूप से परिमित है, तो आप डायनेमिक SQL के साथ भी कुछ मुश्किल कर सकते हैं, जहाँ आप कहते हैं "मेरे पास इसके लिए बहुत ही चयनात्मक मूल्य है @Status, इसलिए जब यह विशिष्ट मान पास हो जाता है, तो क्वेरी पाठ में यह थोड़ा परिवर्तन करें ताकि इसे एक अलग क्वेरी माना जाता है और उस परम मूल्य के लिए अनुकूलित किया जाता है। "


3
मैंने कई बार इस दृष्टिकोण का उपयोग किया है और यह एक शानदार तरीका है कि ऑप्टिमाइज़र को उन चीजों को करने का तरीका है जो आपको लगता है कि इसे वैसे भी करना चाहिए। किम ट्रिप ने यहां एक समान समाधान के बारे में बात की: sqlskills.com/blogs/kimberly/high-performance-procedures और उसके पास एक सत्र का एक वीडियो है जो उसने कुछ साल पहले PASS में किया था जो वास्तव में पागल हो जाता है जैसे कि यह क्यों काम करता है। वह कहता है, यह वास्तव में श्री बर्ट्रेंड के यहां कहा गया एक टन नहीं जोड़ता है। यह उन उपकरणों में से एक है जिन्हें हर किसी को अपने टूलबेल में रखना चाहिए। यह वास्तव में उन कैच-सभी प्रश्नों के लिए कुछ बड़े दर्द को बचा सकता है।
22

3

डिस्क्लेमर : इस उत्तर में कुछ सामान डीबीए फ्लिंच बना सकते हैं। मैं इसे एक शुद्ध प्रदर्शन के दृष्टिकोण से ले जा रहा हूं - जब आप हमेशा इंडेक्स स्कैन्स प्राप्त करते हैं तो इंडेक्स सॉक्स कैसे प्राप्त करें।

उस रास्ते से, यहाँ चला जाता है।

आपकी क्वेरी को "किचन सिंक क्वेरी" के रूप में जाना जाता है - संभव खोज स्थितियों की एक श्रृंखला को पूरा करने के लिए एक एकल क्वेरी। यदि उपयोगकर्ता @statusकिसी मान पर सेट होता है, तो आप उस स्थिति को फ़िल्टर करना चाहते हैं। यदि @statusहै NULL, तो सभी स्थितियों को वापस करें, और इसी तरह।

यह अनुक्रमण के साथ समस्याओं का परिचय देता है, लेकिन वे sargability से संबंधित नहीं हैं, क्योंकि आपकी सभी खोज स्थितियां "मानदंड के बराबर" हैं।

यह सारगर्भित है:

WHERE [status]=@status

यह व्यर्थ नहीं है क्योंकि एसक्यूएल सर्वर ISNULL([status], 0)को इंडेक्स में एकल मान देखने के बजाय हर पंक्ति के लिए मूल्यांकन करने की आवश्यकता होती है:

WHERE ISNULL([status], 0)=@status

मैंने सरल रूप में रसोई सिंक-समस्या को फिर से बनाया है:

CREATE TABLE #work (
    A    int NOT NULL,
    B    int NOT NULL
);

CREATE UNIQUE INDEX #work_ix1 ON #work (A, B);

INSERT INTO #work (A, B)
VALUES (1,  1), (2,  1),
       (3,  1), (4,  1),
       (5,  2), (6,  2),
       (7,  2), (8,  3),
       (9,  3), (10, 3);

यदि आप निम्नलिखित प्रयास करते हैं, तो आपको एक इंडेक्स स्कैन मिलेगा, भले ही ए इंडेक्स का पहला कॉलम हो:

DECLARE @a int=4, @b int=NULL;

SELECT *
FROM #work
WHERE (@a IS NULL OR @a=A) AND
      (@b IS NULL OR @b=B);

यह, हालांकि, एक इंडेक्स सीक का उत्पादन करता है:

DECLARE @a int=4, @b int=NULL;

SELECT *
FROM #work
WHERE @a=A AND
      @b IS NULL;

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

DECLARE @a int=4, @b int=NULL;

SELECT *
FROM #work
WHERE @a=A AND
      @b IS NULL
UNION ALL
SELECT *
FROM #work
WHERE @a=A AND
      @b=B
UNION ALL
SELECT *
FROM #work
WHERE @a IS NULL AND
      @b=B
UNION ALL
SELECT *
FROM #work
WHERE @a IS NULL AND
      @b IS NULL;

इंडेक्स सीक का उपयोग करने के लिए उन चार में से एक के लिए, आपको दूसरे इंडेक्स की आवश्यकता होगी (B, A), हालांकि। यहां बताया गया है कि आपकी क्वेरी इन परिवर्तनों के साथ कैसे दिखाई दे सकती है (इसे और अधिक पठनीय बनाने के लिए क्वेरी को फिर से भरना सहित)।

DECLARE @Status int = NULL,
        @IsUserGotAnActiveDirectoryUser bit = NULL;

SELECT [IdNumber], [Code], [Status], [Sex], [FirstName], [LastName],
       [Profession], [BirthDate], [HireDate], [ActiveDirectoryUser]
FROM Employee
WHERE [Status]=@Status AND
      @IsUserGotAnActiveDirectoryUser IS NULL

UNION ALL

SELECT [IdNumber], [Code], [Status], [Sex], [FirstName], [LastName],
       [Profession], [BirthDate], [HireDate], [ActiveDirectoryUser]
FROM Employee
WHERE [Status]=@Status AND
      @IsUserGotAnActiveDirectoryUser=1 AND ActiveDirectoryUser<>''

UNION ALL

SELECT [IdNumber], [Code], [Status], [Sex], [FirstName], [LastName],
       [Profession], [BirthDate], [HireDate], [ActiveDirectoryUser]
FROM Employee
WHERE [Status]=@Status AND
      @IsUserGotAnActiveDirectoryUser=0 AND (ActiveDirectoryUser IS NULL OR ActiveDirectoryUser='')

UNION ALL

SELECT [IdNumber], [Code], [Status], [Sex], [FirstName], [LastName],
       [Profession], [BirthDate], [HireDate], [ActiveDirectoryUser]
FROM Employee
WHERE @Status IS NULL AND
      @IsUserGotAnActiveDirectoryUser IS NULL

UNION ALL

SELECT [IdNumber], [Code], [Status], [Sex], [FirstName], [LastName],
       [Profession], [BirthDate], [HireDate], [ActiveDirectoryUser]
FROM Employee
WHERE @Status IS NULL AND
      @IsUserGotAnActiveDirectoryUser=1 AND ActiveDirectoryUser<>''

UNION ALL

SELECT [IdNumber], [Code], [Status], [Sex], [FirstName], [LastName],
       [Profession], [BirthDate], [HireDate], [ActiveDirectoryUser]
FROM Employee
WHERE @Status IS NULL AND
      @IsUserGotAnActiveDirectoryUser=0 AND (ActiveDirectoryUser IS NULL OR ActiveDirectoryUser='');

... इसके अलावा आपको Employeeदो अनुक्रमणिका स्तंभों के साथ एक अतिरिक्त अनुक्रमणिका की आवश्यकता होगी ।

पूर्णता के लिए, मुझे इस बात का उल्लेख करना चाहिए कि x=@xइसका मतलब यह है कि xऐसा नहीं किया जा सकता है NULLक्योंकि NULLकभी भी इसके बराबर नहीं है NULL। यह क्वेरी को थोड़ा सरल करता है।

और, हाँ, हारून बर्ट्रेंड के गतिशील एसक्यूएल जवाब ज्यादातर मामलों में एक बेहतर विकल्प है (यानी जब भी आप रीकॉम्पायर के साथ रह सकते हैं)।


3

आपका मूल प्रश्न "क्यों" प्रतीत होता है और मुझे लगता है कि आपको कुछ साल पहले TechEd पर एडम मैकहानिक द्वारा इस शानदार प्रस्तुति के बारे में 55 या तो जवाब मिल सकता है ।

मैं 55 मिनट पर 5 मिनट का उल्लेख करता हूं लेकिन पूरी प्रस्तुति समय के लायक है। यदि आप अपनी क्वेरी के लिए क्वेरी प्लान को देखते हैं तो मुझे यकीन है कि आप पाएंगे कि यह खोज के लिए अवशिष्ट विधेयकों है। मूल रूप से एसक्यूएल सूचकांक के सभी हिस्सों को "नहीं" देख सकता है क्योंकि उनमें से कुछ असमानताओं और अन्य स्थितियों से छिपे हुए हैं। परिणाम, Predicate के आधार पर एक सुपर सेट के लिए एक इंडेक्स स्कैन है। यह परिणाम स्पूल किया जाता है और फिर अवशिष्ट विधेय का उपयोग करके पुन: स्कैन किया जाता है।

स्कैन ऑपरेटर (F4) के गुणों की जाँच करें और देखें कि क्या आपके पास संपत्ति सूची में "सीक प्रेडिकेट" और "प्रेडिकेट" दोनों हैं।

जैसा कि दूसरों ने संकेत दिया है, क्वेरी को अनुक्रमित करना मुश्किल है। मैं हाल ही में कई समानों पर काम कर रहा हूं और प्रत्येक को एक अलग समाधान की आवश्यकता है। :(


0

इससे पहले कि हम यह सवाल करें कि क्या सूचकांक की तलाश सूचकांक स्कैन पर पसंद की जाती है, अंगूठे का एक नियम यह जांचना है कि अंतर्निहित तालिका की कुल पंक्तियों के कितने पंक्तियों को लौटाया गया है। उदाहरण के लिए यदि आप अपनी क्वेरी को 1 मिलियन पंक्तियों में से 10 पंक्तियों को वापस करने की अपेक्षा करते हैं, तो इंडेक्स की तलाश इंडेक्स स्कैन की तुलना में अत्यधिक पसंद की जाती है। हालाँकि, यदि कुछ हज़ार पंक्तियों (या अधिक) को क्वेरी से लौटाया जाना है, तो सूचकांक की तलाश आवश्यक नहीं है।

आपकी क्वेरी जटिल नहीं है, इसलिए यदि आप निष्पादन योजना पोस्ट कर सकते हैं, तो आपकी सहायता के लिए हमारे पास बेहतर विचार हो सकते हैं।


1 मिलियन की मेज से कुछ हजार पंक्तियों को छानते हुए, मैं अभी भी एक तलाश करना चाहूंगा - यह अभी भी पूरी तालिका को स्कैन करने में एक विशाल प्रदर्शन सुधार है।
डैनियल हटमाचर

-6

यह सिर्फ मूल स्वरूपित है

DECLARE @Status INT = NULL,
        @IsUserGotAnActiveDirectoryUser BIT = NULL    

SELECT [IdNumber], [Code], [Status], [Sex], [FirstName], [LastName], [Profession],
       [BirthDate], [HireDate], [ActiveDirectoryUser]
FROM Employee
WHERE (@Status IS NULL OR [Status]=@Status)  
AND (            @IsUserGotAnActiveDirectoryUser IS NULL 
      OR (       @IsUserGotAnActiveDirectoryUser IS NOT NULL 
           AND (     @IsUserGotAnActiveDirectoryUser = 1 
                 AND ActiveDirectoryUser <> '') 
           OR  (     @IsUserGotAnActiveDirectoryUser = 0 
                 AND ActiveDirectoryUser =  '')
         )
    )

यह संशोधन है - इसके बारे में 100% निश्चित नहीं है लेकिन (शायद) इसे एक बार
भी आज़माएं या शायद एक समस्या बनने जा रही है
जो ActiveDirectoryUser नल पर टूट जाएगी

  WHERE isnull(@Status, [Status]) = [Status]
    AND (      (     isnull(@IsUserGotAnActiveDirectoryUser, 1) = 1 
                 AND ActiveDirectoryUser <> '' ) 
           OR  (     isnull(@IsUserGotAnActiveDirectoryUser, 0) = 0 
                 AND ActiveDirectoryUser =  '' )
        )

3
यह मेरे लिए स्पष्ट नहीं है कि यह उत्तर ओपी के प्रश्न को कैसे हल करता है।
एरिक

@ क्या हम चाह सकते हैं कि ओपी इसे आजमा दे? दो ओर चले गए। क्या आपको पता है कि यह क्वेरी प्रदर्शन में मदद नहीं कर सकता है?
पापराज्जो

@ ypercube y IsUserGotAnActiveDirectoryUser IS NULL नहीं निकाला गया है। उन दो अनावश्यक एक OR को हटाते हैं और IsUserGotAnActiveDirectoryUser IS NULL को हटा देते हैं। क्या आप सुनिश्चित हैं कि यह क्वेरी तेज़ी से नहीं चलेगी और फिर ओपी?
पापाराज़ो

@ ypercube y बहुत सारी चीजें कर सकता था। मैं सरल नहीं दिख रहा हूं। दो या गए। या क्वेरी योजनाओं के लिए आमतौर पर खराब है। मुझे लगता है कि यहां एक तरह का क्लब है और मैं क्लब का हिस्सा नहीं हूं। लेकिन मैं एक जीवित और पोस्ट के लिए ऐसा करता हूं जो मुझे पता है कि मैंने काम किया है। मेरे जवाब वोटों से प्रभावित नहीं हैं।
पापाराज़ो
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.