SQL सर्वर में न्यूमेरिक रेंज (अंतराल) खोज को ऑप्टिमाइज़ करना


18

यह प्रश्न ऑप्टिमाइज़िंग आईपी रेंज सर्च के समान है ? लेकिन यह SQL Server 2000 के लिए प्रतिबंधित है।

मान लीजिए कि मेरे पास 10 मिलियन श्रेणियां हैं जो अस्थायी रूप से एक तालिका में संग्रहीत हैं और नीचे की तरह आबाद हैं।

CREATE TABLE MyTable
(
Id        INT IDENTITY PRIMARY KEY,
RangeFrom INT NOT NULL,
RangeTo   INT NOT NULL,
CHECK (RangeTo > RangeFrom),
INDEX IX1 (RangeFrom,RangeTo),
INDEX IX2 (RangeTo,RangeFrom)
);

WITH RandomNumbers
     AS (SELECT TOP 10000000 ABS(CRYPT_GEN_RANDOM(4)%100000000) AS Num
         FROM   sys.all_objects o1,
                sys.all_objects o2,
                sys.all_objects o3,
                sys.all_objects o4)
INSERT INTO MyTable
            (RangeFrom,
             RangeTo)
SELECT Num,
       Num + 1 + CRYPT_GEN_RANDOM(1)
FROM   RandomNumbers 

मुझे सभी रेंजों को जानना होगा, जिसमें मूल्य हो 50,000,000। मैं निम्नलिखित प्रश्न का प्रयास करता हूं

SELECT *
FROM MyTable
WHERE 50000000 BETWEEN RangeFrom AND RangeTo

SQL सर्वर से पता चलता है कि 10,951 तार्किक रीड थे और 12 मिलान वाले को वापस करने के लिए लगभग 5 मिलियन पंक्तियों को पढ़ा गया था।

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

क्या मैं इस प्रदर्शन में सुधार कर सकता हूं? तालिका या अतिरिक्त अनुक्रमित का कोई भी पुनर्गठन ठीक है।


यदि मैं तालिका के सेट को सही ढंग से समझ रहा हूं, तो आप अपनी सीमाओं को बनाने के लिए समान रूप से यादृच्छिक संख्या चुन रहे हैं, प्रत्येक सीमा के "आकार" पर कोई अड़चन नहीं है। और आपकी जांच समग्र रेंज 1..100M के मध्य के लिए है। उस मामले में - समान यादृच्छिकता के कारण कोई स्पष्ट झंझट नहीं है - मुझे नहीं पता कि क्यों कम या ऊपरी बाउंड पर एक सूचकांक सहायक होगा। क्या आप उसे समझा सकते हैं?
दविदबक

इस तालिका पर @davidbak पारंपरिक सूचकांक वास्तव में सबसे खराब स्थिति में बहुत मददगार नहीं हैं क्योंकि इसमें आधी सीमा को स्कैन करना पड़ता है इसलिए इस पर संभावित सुधार के लिए कहा जाता है। SQL सर्वर 2000 के लिए "ग्रेन्युल" की शुरुआत के साथ जुड़े प्रश्न में एक अच्छा सुधार है। मैं उम्मीद कर रहा था कि स्थानिक सूचकांक यहाँ मदद कर सकते हैं क्योंकि वे containsप्रश्नों का समर्थन करते हैं और साथ ही वे डेटा की मात्रा को कम करने में अच्छी तरह से पढ़ते हैं जो वे दूसरे को जोड़ते हैं। ओवरहेड जो इस का प्रतिकार करता है।
मार्टिन स्मिथ

मेरे पास इसे आज़माने की सुविधा नहीं है - लेकिन मुझे आश्चर्य है कि अगर दो अनुक्रमणिकाएँ - एक निचली सीमा पर, एक ऊपरी पर - और फिर एक आंतरिक जुड़ाव - क्वेरी ऑप्टिमाइज़र को कुछ काम करने देगा।
davidbak

जवाबों:


11

एक गैर-अनुक्रमित सूचकांक की तुलना में स्तंभकार यहां बहुत अधिक है जो आधा टेबल स्कैन करता है। एक अस्पष्टीकृत कॉलमस्टोर इंडेक्स अधिकांश लाभ प्रदान करता है लेकिन एक क्लस्टर किए गए कॉलमस्टोर इंडेक्स में ऑर्डर किए गए डेटा को सम्मिलित करना और भी बेहतर है।

DROP TABLE IF EXISTS dbo.MyTableCCI;

CREATE TABLE dbo.MyTableCCI
(
Id        INT PRIMARY KEY,
RangeFrom INT NOT NULL,
RangeTo   INT NOT NULL,
CHECK (RangeTo > RangeFrom),
INDEX CCI CLUSTERED COLUMNSTORE
);

INSERT INTO dbo.MyTableCCI
SELECT TOP (987654321) *
FROM dbo.MyTable
ORDER BY RangeFrom ASC
OPTION (MAXDOP 1);

डिज़ाइन के द्वारा मैं RangeFromकॉलम पर पंक्तिसमूह उन्मूलन प्राप्त कर सकता हूं जो मेरे पंक्ति समूह के आधे हिस्से को समाप्त कर देगा। लेकिन डेटा की प्रकृति के कारण मुझे RangeToकॉलम पर भी पंक्तिग्रुप उन्मूलन मिलता है :

Table 'MyTableCCI'. Segment reads 1, segment skipped 9.

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


हां निश्चित रूप से 2000 प्रतिबंध के बिना विचार करने के लिए अन्य तरीकों की तलाश कर रहे हैं। ऐसा लगता है कि पीटा जाएगा।
मार्टिन स्मिथ

9

पॉल व्हाइट ने इटज़िक बेन गण के एक दिलचस्प लेख के लिंक वाले एक समान प्रश्न का उत्तर दिया । यह "स्टेटिक रिलेशनल इंटरवल ट्री" मॉडल का वर्णन करता है जो इसे कुशलता से करने की अनुमति देता है।

सारांश में इस दृष्टिकोण में पंक्ति में अंतराल मूल्यों के आधार पर एक संगणित ("forknode") मान संग्रहीत करना शामिल है। जब एक और सीमा को काटते हुए पर्वतमाला की खोज की जाती है, तो संभव है कि संभव थकनोड मानों को पहले से निर्धारित कर दिया जाए, जो कि पंक्तियों के मिलान के लिए होने चाहिए और अधिकतम ३१ ऑपरेशनों के साथ परिणाम खोजने के लिए इसका उपयोग करें (नीचे ० से अधिकतम ० की सीमा तक पूर्णांक का समर्थन करता है) बिट इंट)

इसके आधार पर मैंने नीचे दी गई तालिका का पुनर्गठन किया।

CREATE TABLE dbo.MyTable3
(
  Id        INT IDENTITY PRIMARY KEY,
  RangeFrom INT NOT NULL,
  RangeTo   INT NOT NULL,   
  node  AS RangeTo - RangeTo % POWER(2, FLOOR(LOG((RangeFrom - 1) ^ RangeTo, 2))) PERSISTED NOT NULL,
  CHECK (RangeTo > RangeFrom)
);

CREATE INDEX ix1 ON dbo.MyTable3 (node, RangeFrom) INCLUDE (RangeTo);
CREATE INDEX ix2 ON dbo.MyTable3 (node, RangeTo) INCLUDE (RangeFrom);

SET IDENTITY_INSERT MyTable3 ON

INSERT INTO MyTable3
            (Id,
             RangeFrom,
             RangeTo)
SELECT Id,
       RangeFrom,
       RangeTo
FROM   MyTable

SET IDENTITY_INSERT MyTable3 OFF 

और फिर निम्नलिखित क्वेरी का उपयोग किया (लेख अंतराल को इंटरसेप्ट करने के लिए देख रहा है, इसलिए एक अंतराल को खोजने के लिए एक बिंदु युक्त यह एक पतित मामला है)

DECLARE @value INT = 50000000;

;WITH N AS
(
SELECT 30 AS Level, 
       CASE WHEN @value > POWER(2,30) THEN POWER(2,30) END AS selected_left_node, 
       CASE WHEN @value < POWER(2,30) THEN POWER(2,30) END AS selected_right_node, 
       (SIGN(@value - POWER(2,30)) * POWER(2,29)) + POWER(2,30)  AS node
UNION ALL
SELECT N.Level-1,   
       CASE WHEN @value > node THEN node END AS selected_left_node,  
       CASE WHEN @value < node THEN node END AS selected_right_node,
       (SIGN(@value - node) * POWER(2,N.Level-2)) + node  AS node
FROM N 
WHERE N.Level > 0
)
SELECT I.id, I.RangeFrom, I.RangeTo
FROM dbo.MyTable3 AS I
  JOIN N AS L
    ON I.node = L.selected_left_node
    AND I.RangeTo >= @value
    AND L.selected_left_node IS NOT NULL
UNION ALL
SELECT I.id, I.RangeFrom, I.RangeTo
FROM dbo.MyTable3 AS I
  JOIN N AS R
    ON I.node = R.selected_right_node
    AND I.RangeFrom <= @value
    AND R.selected_right_node IS NOT NULL
UNION ALL
SELECT I.id, I.RangeFrom, I.RangeTo
FROM dbo.MyTable3 AS I
WHERE node = @value;

यह आमतौर 1msपर मेरी मशीन पर निष्पादित होता है जब सभी पृष्ठ कैश में होते हैं - IO आँकड़ों के साथ।

Table 'MyTable3'. Scan count 24, logical reads 72, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Worktable'. Scan count 4, logical reads 374, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

और योजना

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

NB: स्रोत नोड्स प्राप्त करने के लिए एक पुनरावर्ती CTE के बजाय मल्टीस्टैटमेंट TVFs का उपयोग करता है, लेकिन मेरे उत्तर को शामिल करने के लिए चुने गए मेरे स्वयं को शामिल करने के हितों में। उत्पादन के उपयोग के लिए मैं शायद TVFs का उपयोग करूँगा।


9

मैं एक पंक्ति मोड दृष्टिकोण खोजने में सक्षम था जो एन / सीसीआई दृष्टिकोण के साथ प्रतिस्पर्धी है, लेकिन आपको अपने डेटा के बारे में कुछ जानने की आवश्यकता है। मान लीजिए कि आप एक स्तंभ के अंतर निहित था RangeFromऔर RangeToऔर अपने साथ यह अनुक्रमित RangeFrom:

ALTER TABLE dbo.MyTableWithDiff ADD DiffOfColumns AS RangeTo-RangeFrom;

CREATE INDEX IXDIFF ON dbo.MyTableWithDiff (DiffOfColumns,RangeFrom) INCLUDE (RangeTo);

यदि आप सभी के विभिन्न मूल्यों को जानते थे, DiffOfColumnsतो आप सभी प्रासंगिक डेटा प्राप्त करने के लिए DiffOfColumnsरेंज फिल्टर के साथ हर मूल्य के लिए एक खोज कर सकते हैं RangeTo। उदाहरण के लिए, यदि हम जानते हैं कि DiffOfColumns= 2 तो केवल RangeFrom49999998, 49999999 और 50000000 के लिए अनुमत मान हैं। पुनरावृत्ति का उपयोग सभी के विभिन्न मूल्यों को प्राप्त करने के लिए किया जा सकता है DiffOfColumnsऔर यह आपके डेटा सेट के लिए अच्छी तरह से काम करता है क्योंकि इनमें से केवल 256 हैं। नीचे दी गई क्वेरी मेरी मशीन पर लगभग 6 ms लेती है:

WITH RecursiveCTE
AS
(
    -- Anchor
    SELECT TOP (1)
        DiffOfColumns
    FROM dbo.MyTableWithDiff AS T
    ORDER BY
        T.DiffOfColumns

    UNION ALL

    -- Recursive
    SELECT R.DiffOfColumns
    FROM
    (
        -- Number the rows
        SELECT 
            T.DiffOfColumns,
            rn = ROW_NUMBER() OVER (
                ORDER BY T.DiffOfColumns)
        FROM dbo.MyTableWithDiff AS T
        JOIN RecursiveCTE AS R
            ON R.DiffOfColumns < T.DiffOfColumns
    ) AS R
    WHERE
        -- Only the row that sorts lowest
        R.rn = 1
)
SELECT ca.*
FROM RecursiveCTE rcte
CROSS APPLY (
    SELECT mt.Id, mt.RangeFrom, mt.RangeTo
    FROM dbo.MyTableWithDiff mt
    WHERE mt.DiffOfColumns = rcte.DiffOfColumns
    AND mt.RangeFrom >= 50000000 - rcte.DiffOfColumns AND mt.RangeFrom <= 50000000
) ca
OPTION (MAXRECURSION 0);

आप हर अलग मूल्य के लिए अनुक्रमणिका की तलाश के साथ सामान्य पुनरावर्ती भाग देख सकते हैं:

क्वेरी प्लान 1

इस दृष्टिकोण के साथ दोष यह है कि यह धीमा होने लगता है जब इसके लिए कई अलग-अलग मूल्य होते हैं DiffOfColumns। चलो एक ही परीक्षण करते हैं, लेकिन CRYPT_GEN_RANDOM(2)इसके बजाय का उपयोग करें CRYPT_GEN_RANDOM(1)

DROP TABLE IF EXISTS dbo.MyTableBigDiff;

CREATE TABLE dbo.MyTableBigDiff
(
Id        INT IDENTITY PRIMARY KEY,
RangeFrom INT NOT NULL,
RangeTo   INT NOT NULL,
CHECK (RangeTo > RangeFrom)
);

WITH RandomNumbers
     AS (SELECT TOP 10000000 ABS(CRYPT_GEN_RANDOM(4)%100000000) AS Num
         FROM   sys.all_objects o1,
                sys.all_objects o2,
                sys.all_objects o3,
                sys.all_objects o4)
INSERT INTO dbo.MyTableBigDiff
            (RangeFrom,
             RangeTo)
SELECT Num,
       Num + 1 + CRYPT_GEN_RANDOM(2) -- note the 2
FROM   RandomNumbers;


ALTER TABLE dbo.MyTableBigDiff ADD DiffOfColumns AS RangeTo-RangeFrom;

CREATE INDEX IXDIFF ON dbo.MyTableBigDiff (DiffOfColumns,RangeFrom) INCLUDE (RangeTo);

वही क्वेरी अब पुनरावर्ती भाग से 65536 पंक्तियों को ढूंढती है और मेरी मशीन पर 823 एमएसपी सीपीयू लेती है। PAGELATCH_SH प्रतीक्षा और अन्य बुरी चीजें चल रही हैं। मैं विशिष्ट मानों की संख्या को नियंत्रण में रखने और बकेटिंग के लिए समायोजित करने के लिए अलग-अलग मानों को बाल्टी द्वारा प्रदर्शन में सुधार कर सकता हूं CROSS APPLY। इस डेटा सेट के लिए मैं २५६ बाल्टियाँ आज़माऊंगा:

ALTER TABLE dbo.MyTableBigDiff ADD DiffOfColumns_bucket256 AS CAST(CEILING((RangeTo-RangeFrom) / 256.) AS INT);

CREATE INDEX [IXDIFF😎] ON dbo.MyTableBigDiff (DiffOfColumns_bucket256, RangeFrom) INCLUDE (RangeTo);

अतिरिक्त पंक्तियों को प्राप्त करने से बचने का एक तरीका (अब मैं सच्चे मूल्य के बजाय एक गोल मूल्य की तुलना कर रहा हूं) पर फ़िल्टर करके है RangeTo:

CROSS APPLY (
    SELECT mt.Id, mt.RangeFrom, mt.RangeTo
    FROM dbo.MyTableBigDiff mt
    WHERE mt.DiffOfColumns_bucket256 = rcte.DiffOfColumns_bucket256
    AND mt.RangeFrom >= 50000000 - (256 * rcte.DiffOfColumns_bucket256)
    AND mt.RangeFrom <= 50000000
    AND mt.RangeTo >= 50000000
) ca

पूर्ण क्वेरी अब मेरी मशीन पर 6 एमएस लेती है।


8

रेंज का प्रतिनिधित्व करने का एक वैकल्पिक तरीका एक लाइन पर बिंदुओं के रूप में होगा।

नीचे एक geometryडेटाटाइप के रूप में दर्शाई गई सीमा के साथ सभी डेटा को एक नई तालिका में माइग्रेट करता है ।

CREATE TABLE MyTable2
(
Id INT IDENTITY PRIMARY KEY,
Range GEOMETRY NOT NULL,
RangeFrom AS Range.STPointN(1).STX,
RangeTo   AS Range.STPointN(2).STX,
CHECK (Range.STNumPoints() = 2 AND Range.STPointN(1).STY = 0 AND Range.STPointN(2).STY = 0)
);

SET IDENTITY_INSERT MyTable2 ON

INSERT INTO MyTable2
            (Id,
             Range)
SELECT ID,
       geometry::STLineFromText(CONCAT('LINESTRING(', RangeFrom, ' 0, ', RangeTo, ' 0)'), 0)
FROM   MyTable

SET IDENTITY_INSERT MyTable2 OFF 


CREATE SPATIAL INDEX index_name   
ON MyTable2 ( Range )  
USING GEOMETRY_GRID  
WITH (  
BOUNDING_BOX = ( xmin=0, ymin=0, xmax=110000000, ymax=1 ),  
GRIDS = (HIGH, HIGH, HIGH, HIGH),  
CELLS_PER_OBJECT = 16); 

मूल्य वाले श्रेणियों को खोजने के लिए बराबर क्वेरी 50,000,000नीचे है।

SELECT Id,
       RangeFrom,
       RangeTo
FROM   MyTable2
WHERE  Range.STContains(geometry::STPointFromText ('POINT (50000000 0)', 0)) = 1 

10,951मूल क्वेरी से इस शो के सुधार को पढ़ता है ।

Table 'MyTable2'. Scan count 0, logical reads 505, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Workfile'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'extended_index_1797581442_384000'. Scan count 4, logical reads 17, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

हालांकि बीते हुए समय के संदर्भ में मूल पर कोई उल्लेखनीय सुधार नहीं हुआ है । विशिष्ट निष्पादन परिणाम 250 एमएस बनाम 252 एमएस हैं।

निष्पादन योजना नीचे के रूप में अधिक जटिल है

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

एक ही मामला है जहाँ फिर से लिखना मेरे लिए बेहतर प्रदर्शन करता है एक ठंडा कैश के साथ है।

इसलिए इस मामले में निराशाजनक और इस पुनर्लेखन की सिफारिश करना मुश्किल है लेकिन नकारात्मक परिणामों का प्रकाशन भी उपयोगी हो सकता है।


5

हमारे नए रोबोट के अधिपति के लिए एक श्रद्धांजलि के रूप में, मैंने यह देखने का फैसला किया कि क्या नए-ईश आर और पायथन कार्यक्षमता में से कोई भी हमारी मदद कर सकता है। इसका उत्तर नहीं है, कम से कम उन लिपियों के लिए जिन्हें मैं काम कर सकता था और सही परिणाम लौटा सकता था। अगर बेहतर ज्ञान वाला कोई भी व्यक्ति साथ आता है, तो, बेझिझक मुझसे संपर्क करें। मेरी दरें वाजिब हैं।

ऐसा करने के लिए, मैंने 4 कोर और 16 जीबी रैम के साथ एक वीएम सेट किया, यह सोचकर कि ~ 200 एमबी डेटा सेट से निपटने के लिए यह पर्याप्त होगा।

आइए बोस्टन में मौजूद भाषा से शुरू करें!

आर

EXEC sp_execute_external_script 
@language = N'R', 
@script = N'
tweener = 50000000
MO = data.frame(MartinIn)
MartinOut <- subset(MO, RangeFrom <= tweener & RangeTo >= tweener, select = c("Id","RangeFrom","RangeTo"))
', 
@input_data_1_name = N'MartinIn',
@input_data_1 = N'SELECT Id, RangeFrom, RangeTo FROM dbo.MyTable',
@output_data_1_name = N'MartinOut',
@parallel = 1
WITH RESULT SETS ((ID INT, RangeFrom INT, RangeTo INT));

यह एक बुरा समय था।

Table 'MyTable'. Scan count 1, logical reads 22400

 SQL Server Execution Times:
   CPU time = 3219 ms,  elapsed time = 5349 ms.

कार्य योजना लागू करके बहुत नीरस है, हालांकि मैं क्यों बीच ऑपरेटर हमें नाम कॉल करने के लिए है पता नहीं है।

पागल

अगला, crayons के साथ कोडिंग!

अजगर

EXEC sp_execute_external_script 
@language = N'Python', 
@script = N'
import pandas as pd
MO = pd.DataFrame(MartinIn)
tweener = 50000000
MartinOut = MO[(MO.RangeFrom <= tweener) & (MO.RangeTo >= tweener)]
', 
@input_data_1_name = N'MartinIn',
@input_data_1 = N'SELECT Id, RangeFrom, RangeTo FROM dbo.MyTable',
@output_data_1_name = N'MartinOut',
@parallel = 1
WITH RESULT SETS ((ID INT, RangeFrom INT, RangeTo INT));

जब आप सोचते हैं कि यह आर से भी बदतर नहीं हो सकता है:

Table 'MyTable'. Scan count 1, logical reads 22400

 SQL Server Execution Times:
   CPU time = 3797 ms,  elapsed time = 10146 ms.

एक और फाउल-माउथ निष्पादन योजना :

पागल

हम्म और हम्मर

अब तक, मैं प्रभावित नहीं हूं। मैं इस VM को हटाने का इंतजार नहीं कर सकता।


1
आप मापदंडों में भी पास कर सकते हैं, उदाहरण के लिए, DECLARE @input INT = 50000001; EXEC dbo.sp_execute_external_script @language = N'R', @script = N'OutputDataSet <- InputDataSet[which(x >= InputDataSet$RangeFrom & x <= InputDataSet$RangeTo) , ]', @parallel = 1, @input_data_1 = N'SELECT Id, RangeFrom, RangeTo FROM dbo.MyTable;', @params = N'@x INT', @x = 50000001 WITH RESULT SETS ( ( Id INT NOT NULL, RangeFrom INT NOT NULL, RangeTo INT NOT NULL ));लेकिन हाँ प्रदर्शन बहुत अच्छा नहीं है। यदि आप कुछ भविष्यवाणी करना चाहते हैं, तो मैं आपको एसक्यूएल में नहीं कर सकता सामान के लिए आर का उपयोग करता हूं।
wcc

4

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

अपने दिए गए नमूने से शुरुआत करें, फिर तालिका को संशोधित करें:

ALTER TABLE dbo.MyTable
    ADD curtis_jackson 
        AS CONVERT(BIT, CASE 
                            WHEN RangeTo >= 50000000
                            AND RangeFrom < 50000000
                            THEN 1 
                            ELSE 0 
                        END);

CREATE INDEX IX1_redo 
    ON dbo.MyTable (curtis_jackson) 
        INCLUDE (RangeFrom, RangeTo);

क्वेरी बस बन जाती है:

SELECT *
FROM MyTable
WHERE curtis_jackson = 1;

जो आपके आरंभिक प्रश्न के समान परिणाम देता है। निष्पादन योजनाओं के बंद होने के साथ, यहाँ आँकड़े हैं (संक्षिप्तता के लिए संक्षिप्त):

Table 'MyTable'. Scan count 1, logical reads 3...

SQL Server Execution Times:
  CPU time = 0 ms,  elapsed time = 0 ms.

और यहाँ क्वेरी योजना है :

पागल


क्या आप एक सूचकांक के साथ गणना किए गए कॉलम / फ़िल्टर किए गए सूचकांक की नकल पर काबू नहीं पा सकते हैं WHERE (50000000 BETWEEN RangeFrom AND RangeTo) INCLUDE (..)?
ypercube y

3
@ yper-madhat-cubeᵀᴹ - हाँ। CREATE INDEX IX1_redo ON dbo.MyTable (curtis_jackson) INCLUDE (RangeFrom, RangeTo) WHERE RangeTo >= 50000000 AND RangeFrom <= 50000000काम करेगा। और क्वेरी SELECT * FROM MyTable WHERE RangeTo >= 50000000 AND RangeFrom <= 50000000;इसका उपयोग करती है - इसलिए गरीब कर्टिस की ज्यादा जरूरत नहीं है
मार्टिन स्मिथ

3

मेरा समाधान अवलोकन पर आधारित है कि अंतराल में एक ज्ञात अधिकतम चौड़ाई डब्ल्यू है । नमूना डेटा के लिए यह एक बाइट या 256 पूर्णांक है। इसलिए दिए गए खोज पैरामीटर मान P के लिए हम सबसे छोटी रेंजफ्रेम को जानते हैं जो परिणाम सेट में हो सकता है पी - डब्ल्यू । इसे विधेय में जोड़ना

declare @P int = 50000000;
declare @W int = 256;

select
    *
from MyTable
where @P between RangeFrom and RangeTo
and RangeFrom >= (@P - @W);

मूल सेट-अप और मेरी मशीन को क्वेरी करते हुए (64 बिट विंडोज 10, 4-कोर हाइपरथ्रेडेड i7, 2.8GHz, 16GB RAM) में 13 पंक्तियाँ हैं। वह क्वेरी (RangeFrom, RangeTo) इंडेक्स के समानांतर इंडेक्स की तलाश करती है। संशोधित क्वेरी समान अनुक्रमणिका पर समान अनुक्रमणिका की तलाश करती है।

मूल और संशोधित प्रश्नों के लिए माप हैं

                          Original  Revised
                          --------  -------
Stats IO Scan count              9        6
Stats IO logical reads       11547        6

Estimated number of rows   1643170  1216080
Number of rows read        5109666       29
QueryTimeStats CPU             344        2
QueryTimeStats Elapsed          53        0

मूल क्वेरी के लिए पढ़ी गई पंक्तियों की संख्या उन पंक्तियों की संख्या के बराबर है जो @P से कम या बराबर हैं। क्वेरी ऑप्टिमाइज़र (QO) के पास कोई विकल्प नहीं है, लेकिन उन सभी को पढ़ें क्योंकि यह पहले से निर्धारित नहीं कर सकता है कि क्या ये पंक्तियाँ विधेय को संतुष्ट करेंगी। बहु-स्तंभ अनुक्रमणिका (RangeFrom, RangeTo) उन पंक्तियों को समाप्त करने में उपयोगी नहीं है, जो RangeTo से मेल नहीं खाती हैं, क्योंकि पहली अनुक्रमणिका कुंजी और दूसरा जिसे लागू किया जा सकता है, के बीच कोई संबंध नहीं है। उदाहरण के लिए, पहली पंक्ति में एक छोटा अंतराल हो सकता है और इसे समाप्त किया जा सकता है जबकि दूसरी पंक्ति में एक बड़ा अंतराल होता है और इसे वापस कर दिया जाता है, या इसके विपरीत।

एक असफल प्रयास में मैंने एक चेक बाधा के माध्यम से उस निश्चितता को प्रदान करने की कोशिश की:

alter table MyTable with check
add constraint CK_MyTable_Interval
check
(
    RangeTo <= RangeFrom + 256
);

इससे कोई फर्क नहीं पड़ा।

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

दर्पण तर्क में रेंजटो की ऊपरी सीमा P + W है । हालाँकि, यह उपयोगी नहीं है, क्योंकि RangeFrom और RangeTo के बीच कोई सहसंबंध नहीं है जो पंक्तियों को खत्म करने के लिए एक बहु-स्तंभ सूचकांक के अनुगामी स्तंभ की अनुमति देगा। इसलिए इस खंड को क्वेरी में जोड़ने से कोई लाभ नहीं है।

यह दृष्टिकोण छोटे अंतराल आकार से इसका अधिकांश लाभ प्राप्त करता है। जैसा कि संभव अंतराल आकार में कम-मूल्यवान पंक्तियों की संख्या बढ़ जाती है, हालांकि कुछ अभी भी छोड़ दिए जाएंगे। सीमित मामले में, अंतराल के साथ डेटा रेंज जितनी बड़ी है, यह दृष्टिकोण मूल क्वेरी (जो ठंडा आराम मैं मानता है) से भी बदतर नहीं है।

इस उत्तर में जो भी ऑफ-बाय-वन त्रुटियाँ हो सकती हैं, उसके लिए मैं क्षमा चाहता हूँ।

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