TSQL प्रदर्शन - मूल्य के आधार पर शामिल हों न्यूनतम और अधिकतम


10

मेरे पास दो टेबल हैं जिनमें मैं स्टोर करता हूं:

  • एक आईपी रेंज - देश लुकअप टेबल
  • विभिन्न आईपी से आने वाले अनुरोधों की एक सूची

IP bigintलुकअप प्रदर्शन को बेहतर बनाने के लिए संग्रहीत किए गए थे ।

यह तालिका संरचना है:

create table [dbo].[ip2country](
    [begin_ip] [varchar](15) NOT NULL,
    [end_ip] [varchar](15) NOT NULL,
    [begin_num] [bigint] NOT NULL,
    [end_num] [bigint] NOT NULL,
    [IDCountry] [int] NULL,
    constraint [PK_ip2country] PRIMARY KEY CLUSTERED 
    (
        [begin_num] ASC,
        [end_num] ASC
    )
)

create table Request(
    Id int identity primary key, 
    [Date] datetime, 
    IP bigint, 
    CategoryId int
)

मैं प्रति देश अनुरोध विराम प्राप्त करना चाहता हूं, इसलिए मैं निम्नलिखित प्रश्न करता हूं:

select 
    ic.IDCountry,
    count(r.Id) as CountryCount
from Request r
left join ip2country ic 
  on r.IP between ic.begin_num and ic.end_num
where r.CategoryId = 1
group by ic.IDCountry

मेरे पास तालिकाओं में बहुत सारे रिकॉर्ड हैं: लगभग 200,000 IP2Countryऔर कुछ लाखों में Request, इसलिए क्वेरी में कुछ समय लगता है।

निष्पादन योजना को देखते हुए, सबसे महंगा हिस्सा सूचकांक PK_IP2Country पर एक क्लस्टर इंडेक्स सीक है, जिसे कई बार निष्पादित किया जाता है (अनुरोध में पंक्तियों की संख्या)।

इसके अलावा, कुछ ऐसा है जो मुझे थोड़ा अजीब लगता है, वह left join ip2country ic on r.IP between ic.begin_num and ic.end_numहिस्सा है (पता नहीं है कि लुकअप करने का कोई बेहतर तरीका है)।

तालिका संरचना, कुछ नमूना डेटा और क्वेरी SQLFiddle में उपलब्ध हैं: http://www.sqlfiddle.com/#/3/a463e/3 (दुर्भाग्य से मुझे नहीं लगता कि मैं समस्या को पुन: उत्पन्न करने के लिए कई रिकॉर्ड सम्मिलित कर सकता हूं, लेकिन यह उम्मीद है कि एक विचार देता है)।

मैं (स्पष्ट रूप से) एसक्यूएल प्रदर्शन / अनुकूलन में विशेषज्ञ नहीं हूं, इसलिए मेरा सवाल है: क्या कोई स्पष्ट तरीके हैं जिससे इस संरचना / क्वेरी को प्रदर्शन-वार बेहतर बनाया जा सकता है कि मैं गायब हूं?


2
क्या एक आईपी पता कई देशों को मैप कर सकता है? यदि नहीं, तो आप अपने पीके को सीमित कर सकते हैं begin_num। मुझे भी A BETWEEN B AND Cअक्सर इसमें शामिल होना पड़ता है , और मुझे यह जानने की उत्सुकता है कि क्या थकाऊ आरबीएआर जॉइन के बिना इसे प्राप्त करने का कोई तरीका है।
जॉन ऑफ ऑल ट्रेड्स

1
यह आपके प्रश्न के लिए एक छोटा सा विषय है, लेकिन मैं पाठ और संख्याओं को किसी तरह से बाहर निकलने की संभावना को रोकने के लिए गणना किए गए कॉलम बनाने begin_ipऔर end_ipबनाए रखने पर विचार करूंगा ।
जॉन ऑफ ऑल ट्रेड्स

@ w0lf: क्या ओवरलैपिंग रेंज में हैं ip2country (begin_num, end_num)?
ypercube y

@JonofAllTrades आम तौर पर एक आईपी एक ही देश से संबंधित होना चाहिए, इसलिए मुझे लगता है कि आपके प्रश्न का विचार जैसे give me the first record that has a begin_num < ip in asc order of begin_num(मुझे सही है तो गलत है) मान्य हो सकता है और प्रदर्शन में सुधार कर सकता है।
क्रिस्टियान लुपस्कू

1
@ w0lf: मेरा इंप्रेशन यह है कि मूल रूप से सर्वर इस तरह से क्या कर रहा है, क्योंकि यह पहले स्कैन करता है begin_num, फिर end_numउस सेट के भीतर स्कैन करता है और केवल एक रिकॉर्ड पाता है।
जॉन ऑफ ऑल ट्रेड्स

जवाबों:


3

आपको एक अतिरिक्त सूचकांक की आवश्यकता है। आपके फिडेल उदाहरण में मैंने जोड़ा:

CREATE UNIQUE INDEX ix_IP ON Request(CategoryID, IP)

जो आपको अनुरोध तालिका के लिए कवर करता है और एक क्लस्टर इंडेक्स स्कैन के बजाय एक इंडेक्स की तलाश करता है।

देखें कि यह कैसे सुधार करता है और मुझे बताएं। मुझे लग रहा है कि यह उस सूचकांक पर स्कैन के बाद से काफी मदद करेगा, मुझे यकीन है कि सस्ता नहीं है।


मुझे पता नहीं क्यों, लेकिन परिणाम अलग-अलग (SQLFiddle में) प्रतीत होते हैं
क्रिस्टियन

@ w0lf: वे अलग हैं (प्रोब्लेबी) क्योंकि आप दोनों टेबल्स में रैंडम डेटा डाल रहे हैं।
ypercube y

@ypercube निश्चित रूप से इसका कारण है। मैंने हाल ही में इतनी सारी चीजें की हैं कि मैं भूल गया कि डेटा यादृच्छिक था। माफ़ करना।
क्रिस्टियन लुपस्कू

2

हमेशा ब्रूट-फोर्स दृष्टिकोण होता है: आप अपने आईपी मैप को विस्फोट कर सकते हैं। प्रति आईपी पते पर एक रिकॉर्ड बनाने के लिए अपने मौजूदा नक्शे के खिलाफ एक संख्या तालिका में शामिल हों। यह आपके फिडेल डेटा के आधार पर केवल 267K रिकॉर्ड है, कोई समस्या नहीं है।

CREATE TABLE IPLookup
  (
  IP  BIGINT PRIMARY KEY,
  CountryID  INT
  )
INSERT INTO IPLookup (IP, CountryID)
  SELECT
    N.Number, Existing.IDCountry
  FROM
    ip2country AS Existing
    INNER JOIN Numbers AS N ON N.Number BETWEEN Existing.begin_num AND Existing.end_num

यह तलाश को सरल बना देगा, और तेजी से उम्मीद करेगा। यह केवल तभी समझ में आता है जब आप अपेक्षाकृत कम अपडेट करते हैं ip2country

मुझे आशा है कि किसी और के पास एक बेहतर समाधान है!


पूरा डेटा सेट 5 बिलियन से अधिक रिकॉर्ड का उत्पादन करेगा, इसलिए मुझे नहीं लगता कि मैं इसे करूँगा। लेकिन यह एक अच्छा विचार है; मुझे यकीन है कि यह कई समान मामलों में संभव है। +1
क्रिस्टियन लुपस्कू

0

इसे इस्तेमाल करे:

SELECT ic.IDCountry,
        COUNT(r.Id) AS CountryCount
FROM Request r
INNER JOIN (SELECT begin_num+NUMS.N [IP], IDCountry 
            FROM ip2country
            CROSS JOIN (SELECT TOP(SELECT ABS(MAX(end_num-begin_num)) FROM ip2country) ROW_NUMBER() OVER(ORDER BY sc.name)-1 [N]
                        FROM sys.columns sc) NUMS
            WHERE begin_num+NUMS.N <= end_num) ic
ON r.IP = ic.IP
WHERE r.CategoryId = 1
GROUP BY ic.IDCountry

धन्यवाद, मैंने आपके दृष्टिकोण की कोशिश की है, लेकिन यह प्रारंभिक क्वेरी की तुलना में अधिक महंगा लगता है
क्रिस्टियन लुपस्कु

आपके पास प्रत्येक तालिका में कितनी पंक्तियाँ हैं? मैं अपने DB पर आपकी समस्या के पैमाने को पुन: प्रस्तुत करना चाहूंगा और एक इंडेक्स जोड़े बिना हल करने की कोशिश
करूंगा

IP2Country में लगभग 200,000 और अनुरोध में कुछ लाखों (संभवतः निकट भविष्य में करोड़ों)। मुझे लगता है कि यदि आप इसे इंडेक्स के बिना हल करते हैं तो आप "डीबीए ऑफ द ईयर" शीर्षक के हकदार हैं :)
क्रिस्टियन लुपस्कू
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.