तारीख की तुलना के साथ घटिया प्रदर्शन करने वाला गरीब


15

एक मैचिंग फ़ील्ड के साथ सभी पूर्व रिकॉर्ड की कुल संख्या का पता लगाने के लिए एक सबक्वेरी का उपयोग करते समय, प्रदर्शन एक मेज पर भयानक होता है जिसमें 50k रिकॉर्ड होते हैं। सबक्वेरी के बिना, क्वेरी कुछ मिलीसेकंड में निष्पादित होती है। उपकुंजी के साथ, निष्पादन का समय एक मिनट से ऊपर है।

इस प्रश्न के लिए, परिणाम:

  • किसी दिए गए दिनांक सीमा के भीतर केवल उन रिकॉर्ड्स को शामिल करें।
  • तिथि सीमा की परवाह किए बिना, वर्तमान रिकॉर्ड सहित सभी पूर्व रिकॉर्ड्स की एक संख्या शामिल करें।

बेसिक टेबल स्कीमा

Activity
======================
Id int Identifier
Address varchar(25)
ActionDate datetime2
Process varchar(50)
-- 7 other columns

उदाहरण डेटा

Id  Address     ActionDate (Time part excluded for simplicity)
===========================
99  000         2017-05-30
98  111         2017-05-30
97  000         2017-05-29
96  000         2017-05-28
95  111         2017-05-19
94  222         2017-05-30

अपेक्षित परिणाम

की तिथि सीमा के 2017-05-29लिए2017-05-30

Id  Address     ActionDate    PriorCount
=========================================
99  000         2017-05-30    2  (3 total, 2 prior to ActionDate)
98  111         2017-05-30    1  (2 total, 1 prior to ActionDate)
94  222         2017-05-30    0  (1 total, 0 prior to ActionDate)
97  000         2017-05-29    1  (3 total, 1 prior to ActionDate)

रिकॉर्ड 96 और 95 को परिणाम से बाहर रखा गया है, लेकिन PriorCountउपनगर में शामिल किया गया है

वर्तमान क्वेरी

select 
    *.a
    , ( select count(*) 
        from Activity
        where 
            Activity.Address = a.Address
            and Activity.ActionDate < a.ActionDate
    ) as PriorCount
from Activity a
where a.ActionDate between '2017-05-29' and '2017-05-30'
order by a.ActionDate desc

वर्तमान सूचकांक

CREATE NONCLUSTERED INDEX [IDX_my_nme] ON [dbo].[Activity]
(
    [ActionDate] ASC
)
INCLUDE ([Address]) WITH (
    PAD_INDEX = OFF, 
    STATISTICS_NORECOMPUTE = OFF, 
    SORT_IN_TEMPDB = OFF, 
    DROP_EXISTING = OFF, 
    ONLINE = OFF, 
    ALLOW_ROW_LOCKS = ON, 
    ALLOW_PAGE_LOCKS = ON
)

सवाल

  • इस क्वेरी के प्रदर्शन को बेहतर बनाने के लिए किन रणनीतियों का उपयोग किया जा सकता है?

संपादित करें 1
इस सवाल के जवाब में कि मैं DB पर क्या संशोधित कर सकता हूं: मैं अनुक्रमणिका को संशोधित कर सकता हूं, केवल तालिका संरचना नहीं।

संपादित करें 2
मैंने अब Addressकॉलम पर एक मूल सूचकांक जोड़ा है , लेकिन इसमें बहुत सुधार नहीं हुआ। मैं वर्तमान में एक अस्थायी तालिका बनाने PriorCountऔर उसके बिना मूल्यों को सम्मिलित करने और फिर प्रत्येक पंक्ति को उनके विशिष्ट गणनाओं के साथ अपडेट करने के साथ बहुत बेहतर प्रदर्शन पा रहा हूं ।

3 संपादित करें
सूचकांक स्पूल जो ऑब्बिश (स्वीकृत उत्तर) पाया गया मुद्दा था। एक बार जब मैंने एक नया जोड़ा nonclustered index [xyz] on [Activity] (Address) include (ActionDate), क्वेरी समय एक मिनट से ऊपर एक सेकंड से भी कम समय के लिए एक अस्थायी तालिका (2 संपादित देखें) का उपयोग किए बिना नीचे चला गया।

जवाबों:


17

आपके पास जो इंडेक्स परिभाषा है IDX_my_nme, उसके साथ SQL सर्वर ActionDateकॉलम का उपयोग करने में सक्षम होगा, लेकिन कॉलम के साथ नहीं Address। अनुक्रमणिका में सबकुछ को कवर करने के लिए आवश्यक सभी कॉलम होते हैं, लेकिन यह संभवत: उस उपश्रेणी के लिए बहुत चयनात्मक नहीं होता है। मान लीजिए कि तालिका में लगभग सभी डेटा ActionDateकी तुलना में पहले का मान है '2017-05-30'ActionDate < '2017-05-30'सूचकांक से लगभग सभी पंक्तियों की तलाश वापस आ जाएगी, जिन्हें सूचकांक से पंक्ति में लाने के बाद आगे फ़िल्टर किया जाता है। यदि आपकी क्वेरी 200 पंक्तियाँ लौटाती है, तो आप संभवतः लगभग 200 पूर्ण सूचकांक स्कैन कर रहे हैं IDX_my_nme, जिसका अर्थ है कि आप सूचकांक से लगभग 50000 * 200 = 10 मिलियन पंक्तियाँ पढ़ेंगे।

यह संभावना है कि Addressआपके उप-वर्ग के लिए मांग करना कहीं अधिक चयनात्मक होगा, हालांकि आपने हमें क्वेरी के बारे में पूर्ण सांख्यिकीय जानकारी नहीं दी है, इसलिए यह मेरी ओर से एक धारणा है। हालाँकि, मान लीजिए कि आप सिर्फ एक इंडेक्स बनाते हैं Addressऔर आपकी तालिका में 10k अद्वितीय मान हैं Address। नए इंडेक्स के साथ, एसक्यूएल सर्वर को सबक्वेरी के प्रत्येक निष्पादन के लिए इंडेक्स से केवल 5 पंक्तियों की आवश्यकता होगी, इसलिए आप इंडेक्स से लगभग 200 * 5 = 1000 पंक्तियों को पढ़ेंगे।

मैं SQL सर्वर 2016 के खिलाफ परीक्षण कर रहा हूं, इसलिए कुछ मामूली वाक्यविन्यास अंतर हो सकते हैं। नीचे कुछ नमूना डेटा है जिसमें मैंने डेटा वितरण के लिए उपरोक्त के समान धारणा बनाई है:

CREATE TABLE #Activity (
    Id int NOT NULL,
    [Address] varchar(25) NULL,
    ActionDate datetime2 NULL,
    FILLER varchar(100),
    PRIMARY KEY (Id)
);

INSERT INTO #Activity WITH (TABLOCK)
SELECT TOP (50000) -- 50k total rows
x.RN
, x.RN % 10000 -- 10k unique addresses
, DATEADD(DAY, x.RN / 100, '20160201') -- 100 rows per day
, REPLICATE('Z', 100)
FROM
(
    SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) RN
    FROM master..spt_values t1
    CROSS JOIN master..spt_values t2
) x;

CREATE NONCLUSTERED INDEX [IDX_my_nme] ON #Activity
([ActionDate] ASC) INCLUDE ([Address]);

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

select 
    a.*
    , ( select count(*) 
        from #Activity Activity
        where 
            Activity.[Address] = a.[Address]
            and Activity.ActionDate < a.ActionDate
    ) as PriorCount
from #Activity a
where a.ActionDate between '2017-05-29' and '2017-05-30'
order by a.ActionDate desc;

मुझे एक इंडेक्स स्पूल मिलता है। बुनियादी स्तर पर इसका मतलब यह है कि क्वेरी ऑप्टिमाइज़र एक अस्थायी इंडेक्स का निर्माण करता है, क्योंकि टेबल के खिलाफ कोई भी मौजूदा इंडेक्स उपयुक्त नहीं था।

सूचकांक स्पूल

क्वेरी अभी भी मेरे लिए जल्दी खत्म हो गई है। शायद आपको अपने सिस्टम पर इंडेक्स स्पूल ऑप्टिमाइज़ेशन नहीं मिल रहा है या टेबल की परिभाषा या क्वेरी के बारे में कुछ अलग है। शैक्षिक उद्देश्यों के लिए मैं OPTION (QUERYRULEOFF BuildSpool)इंडेक्स स्पूल को निष्क्रिय करने के लिए एक अनिर्दिष्ट सुविधा का उपयोग कर सकता हूं । यहाँ योजना क्या दिखती है:

ख़राब सूचकांक की तलाश

एक सरल सूचकांक की तलाश में मूर्ख मत बनो। SQL सर्वर सूचकांक से लगभग 10 मिलियन पंक्तियाँ पढ़ता है:

सूचकांक से 10M पंक्तियों

अगर मैं क्वेरी को एक से अधिक बार चलाने जा रहा हूं, तो संभवतः क्वेरी ऑप्टिमाइज़र के लिए यह इंडेक्स बनाने के लिए मायने नहीं रखता है कि यह हर बार कैसे चलता है। मैं एक इंडेक्स अपफ्रंट बना सकता हूं जो इस क्वेरी के लिए अधिक चयनात्मक होगा:

CREATE NONCLUSTERED INDEX [IDX_my_nme_2] ON #Activity
([Address] ASC) INCLUDE (ActionDate);

योजना पहले की तरह है:

सूचकांक की तलाश

हालाँकि, नए इंडेक्स SQL ​​सर्वर के साथ केवल इंडेक्स से 1000 पंक्तियाँ पढ़ता है। पंक्तियों में से 800 पंक्तियों को गिना जाता है। सूचकांक को अधिक चयनात्मक माना जा सकता है लेकिन यह आपके डेटा वितरण के आधार पर काफी अच्छा हो सकता है।

अच्छा है

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

SELECT t.*
FROM
(
    select 
        a.*
        , -1 + ROW_NUMBER() OVER (PARTITION BY [Address] ORDER BY ActionDate) PriorCount
    from #Activity a
) t
where t.ActionDate between '2017-05-29' and '2017-05-30'
order by t.ActionDate desc;

वह क्वेरी डेटा का एक सिंगल स्कैन करता है, लेकिन एक महंगा क्रमबद्ध करता है और ROW_NUMBER()तालिका में प्रत्येक पंक्ति के लिए फ़ंक्शन की गणना करता है , इसलिए ऐसा महसूस होता है कि यहां कुछ अतिरिक्त काम किया गया है:

बुरी तरह

हालाँकि, यदि आप वास्तव में उस कोड पैटर्न को पसंद करते हैं, तो आप इसे और अधिक कुशल बनाने के लिए एक सूचकांक को परिभाषित कर सकते हैं:

CREATE NONCLUSTERED INDEX [IDX_my_nme] ON #Activity
([Address], [ActionDate]) INCLUDE (FILLER);

यह उस छोर की ओर बढ़ता है जो बहुत कम खर्चीला होगा:

अच्छी तरह

यदि इसमें से कोई भी मदद नहीं करता है, तो आपको प्रश्न पर अधिक जानकारी जोड़ने की आवश्यकता होगी, अधिमानतः वास्तविक निष्पादन योजनाओं सहित।


1
इंडेक्स स्पूल जो आपको मिला वह मुद्दा था। एक बार जब मैं एक नए में जुड़ता हूं nonclustered index [xyz] on [Activity] (Address) include (ActionDate), तो क्वेरी का समय एक मिनट से ऊपर एक सेकंड से भी कम हो जाता है। +10 अगर मैं कर सका। धन्यवाद!
मेट्रो स्मर्फ
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.