इंडेक्स का उपयोग `= किसी भी ()` के साथ नहीं बल्कि `इन` के साथ किया जाता है


15

तालिका tके दो सूचकांक हैं:

create table t (a int, b int);
create type int_pair as (a int, b int);
create index t_row_idx on t (((a,b)::int_pair));
create index t_a_b_idx on t (a,b);

insert into t (a,b)
select i, i
from generate_series(1, 100000) g(i)
;

anyऑपरेटर के साथ किसी भी इंडेक्स का उपयोग नहीं किया जाता है :

explain analyze
select *
from t
where (a,b) = any(array[(1,1),(1,2)])
;
                                            QUERY PLAN                                             
---------------------------------------------------------------------------------------------------
 Seq Scan on t  (cost=0.00..1693.00 rows=1000 width=8) (actual time=0.042..126.789 rows=1 loops=1)
   Filter: (ROW(a, b) = ANY (ARRAY[ROW(1, 1), ROW(1, 2)]))
   Rows Removed by Filter: 99999
 Planning time: 0.122 ms
 Execution time: 126.836 ms

लेकिन उनमें से एक inऑपरेटर के साथ प्रयोग किया जाता है :

explain analyze
select *
from t
where (a,b) in ((1,1),(1,2))
;
                                                    QUERY PLAN                                                    
------------------------------------------------------------------------------------------------------------------
 Index Only Scan using t_a_b_idx on t  (cost=0.29..8.32 rows=1 width=8) (actual time=0.028..0.029 rows=1 loops=1)
   Index Cond: (a = 1)
   Filter: ((b = 1) OR (b = 2))
   Heap Fetches: 1
 Planning time: 0.161 ms
 Execution time: 0.066 ms

यदि रिकॉर्ड सही प्रकार से डाला जाता है तो यह रिकॉर्ड इंडेक्स का उपयोग करता है:

explain analyze
select *
from t
where (a,b)::int_pair = any(array[row(1,1),row(1,2)])
;
                                                  QUERY PLAN                                                  
--------------------------------------------------------------------------------------------------------------
 Index Scan using t_row_idx on t  (cost=0.42..12.87 rows=2 width=8) (actual time=0.106..0.126 rows=1 loops=1)
   Index Cond: (ROW(a, b)::int_pair = ANY (ARRAY[ROW(1, 1), ROW(1, 2)]))
 Planning time: 0.208 ms
 Execution time: 0.203 ms

anyऑपरेटर ऑपरेटर के लिए गैर रिकॉर्ड इंडेक्स का उपयोग क्यों नहीं करता है क्योंकि यह ऑपरेटर के लिए इसका उपयोग करता है in?


यह दिलचस्प सवाल SO पर संबंधित चर्चा से उभरा: stackoverflow.com/a/34601242/939860
इरविन ब्रान्डेसटेटर

जवाबों:


13

आंतरिक रूप से IN, दो अलग-अलग रूप हैं , साथ ही साथ ANYनिर्माण के लिए भी।

प्रत्येक में से एक, एक सेट ले रहा है , दूसरे के बराबर है और expr IN (<set>)एक ही क्वेरी योजना की ओर जाता है क्योंकि expr = ANY(<set>)यह एक सादे सूचकांक का उपयोग कर सकता है। विवरण:

नतीजतन, निम्नलिखित दो प्रश्न समतुल्य हैं और दोनों सादे इंडेक्स का उपयोग कर सकते हैं t_a_b_idx( यदि आप इंडेक्स का उपयोग करने के लिए अपनी क्वेरी प्राप्त करने की कोशिश कर रहे हैं तो यह भी समाधान हो सकता है ):

EXPLAIN ANALYZE
SELECT *
FROM t
WHERE (a,b) = ANY(VALUES (1,1),(1,2));

या:

...
WHERE (a,b) IN (VALUES (1,1),(1,2));

दोनों के लिए समान:

                                                        QUERY PLAN
--------------------------------------------------------------------------------------------------------------------------
 Nested Loop  (cost=0.33..16.71 rows=1 width=8) (actual time=0.101..0.101 rows=0 loops=1)
   ->  Unique  (cost=0.04..0.05 rows=2 width=8) (actual time=0.068..0.070 rows=2 loops=1)
         ->  Sort  (cost=0.04..0.04 rows=2 width=8) (actual time=0.067..0.068 rows=2 loops=1)
               Sort Key: "*VALUES*".column1, "*VALUES*".column2
               Sort Method: quicksort  Memory: 25kB
               ->  Values Scan on "*VALUES*"  (cost=0.00..0.03 rows=2 width=8) (actual time=0.005..0.005 rows=2 loops=1)
   ->  Index Only Scan using t_plain_idx on t  (cost=0.29..8.32 rows=1 width=8) (actual time=0.009..0.009 rows=0 loops=2)
         Index Cond: ((a = "*VALUES*".column1) AND (b = "*VALUES*".column2))
         Heap Fetches: 0
 Planning time: 4.080 ms
 Execution time: 0.202 ms

हालाँकि , यह आसानी से एक फ़ंक्शन को पारित नहीं किया जा सकता है, क्योंकि पोस्टग्रेज़ में कोई "टेबल चर" नहीं हैं। जो इस विषय को शुरू करने वाली समस्या की ओर ले जाता है:

उस समस्या के लिए विभिन्न समाधान हैं। एक वैकल्पिक जवाब मैं वहाँ जोड़ा जा रहा है। कुछ दुसरे:


प्रत्येक का दूसरा रूप अलग है: ANYवास्तविक सरणीIN लेता है , जबकि मानों की अल्पविराम से अलग सूची लेता है

इनपुट टाइप करने के लिए इसके अलग-अलग परिणाम हैं । जैसा कि हम EXPLAINप्रश्न के आउटपुट में देख सकते हैं , यह प्रपत्र:

WHERE (a,b) = ANY(ARRAY[(1,1),(1,2)]);

के लिए आशुलिपि के रूप में देखा जाता है:

ROW(a, b) = ANY (ARRAY[ROW(1, 1), ROW(1, 2)])

और वास्तविक ROW मानों की तुलना की जाती है। यह देखने के लिए पोस्टग्रेज वर्तमान में पर्याप्त स्मार्ट नहीं है कि कंपोजिट प्रकार पर सूचकांक t_row_idxलागू है। न ही यह महसूस करता है कि सरल सूचकांकt_a_b_idx भी लागू होना चाहिए।

एक स्पष्ट कलाकार स्मार्ट की इस कमी को दूर करने में मदद करता है:

WHERE (a,b)::int_pair = ANY(ARRAY[(1,1),(1,2)]::int_pair[]);

सही ऑपरेंड ( ::int_pair[]) कास्टिंग वैकल्पिक है (हालांकि प्रदर्शन के लिए बेहतर और अस्पष्टता से बचने के लिए)। एक बार बाएं ऑपरेंड के पास एक जाना-पहचाना प्रकार होता है, दाएं ऑपरेंड को "अनाम रिकॉर्ड" से एक मिलान प्रकार के लिए बाध्य किया जाता है। तभी, ऑपरेटर को स्पष्ट रूप से परिभाषित किया गया है। और ऑपरेटर और बाएं ऑपरेंड के आधार पर लागू इंडेक्स को पोस्टग्रेज करता है । कई ऑपरेटरों के लिए जो परिभाषित करते हैं COMMUTATOR, क्वेरी प्लानर अनुक्रमित अभिव्यक्ति को बाईं ओर लाने के लिए ऑपरेंड फ्लिप कर सकते हैं। लेकिन ANYनिर्माण के साथ यह संभव नहीं है ।

सम्बंधित:

.. मानों को तत्वों के रूप में लिया जाता है और Postgres व्यक्तिगत पूर्णांक मानों की तुलना करने में सक्षम होता है जैसा कि हम EXPLAINआउटपुट में एक बार फिर देख सकते हैं :

Filter: ((b = 1) OR (b = 2))

इसलिए पोस्टग्रैज पाता है कि सरल सूचकांक t_a_b_idxका उपयोग किया जा सकता है।


नतीजतन, उदाहरण में विशेष मामले के लिए एक और समाधान होगा : चूंकि उदाहरण में कस्टम मिश्रित प्रकार स्वयं int_pairतालिका के पंक्ति प्रकार के बराबर होता tहै, इसलिए हम सरल कर सकते हैं:

CREATE INDEX t_row_idx2 ON t ((t));

फिर यह क्वेरी बिना अधिक स्पष्ट कास्टिंग के सूचकांक का उपयोग करेगी:

EXPLAIN ANALYZE
SELECT *
FROM   t
WHERE  t = ANY(ARRAY[(1,1),(1,2)]);
                                                      QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------
 Bitmap Heap Scan on t  (cost=40.59..496.08 rows=1000 width=8) (actual time=0.19
1..0.191 rows=0 loops=1)
   Recheck Cond: (t.* = ANY (ARRAY[ROW(1, 1), ROW(1, 2)]))
   ->  Bitmap Index Scan on t_row_idx2  (cost=0.00..40.34 rows=1000 width=0) (actual time=0.188..0.188 rows=0 loops=1)
         Index Cond: (t.* = ANY (ARRAY[ROW(1, 1), ROW(1, 2)]))
 Planning time: 2.575 ms
 Execution time: 0.267 ms

लेकिन विशिष्ट उपयोग के मामले अंतर्निहित मौजूदा प्रकार की तालिका पंक्ति का उपयोग करने में सक्षम नहीं होंगे।


1
एक मामूली जोड़: जबकि एक छोटी IN(...)सूची का अनुवाद (योजनाकार द्वारा) ... OR ...उपरोक्त मामले में एक अभिव्यक्ति में किया जा सकता है, यह आमतौर पर सिर्फ ANY('{...}')एक सरणी का उपयोग करके, अर्थात् में अनुवादित किया जाता है। तो, ज्यादातर मामलों में, INमूल्यों की एक सूची और ANYएक सरणी के साथ एक ही बात है।
डेज़ो

1
@ जेड्सो: अधिकांश सरल मामलों के लिए, हाँ। प्रश्न एक ऐसे मामले को प्रदर्शित करता है, जिसका अनुवाद IN(...) नहीं किया जा सकता है = ANY('{...}')
इरविन ब्रान्डेसटेटर 14
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.