LIKE, SIMILAR TO या रेगुलर एक्सप्रेशंस के साथ PostgreSQL में पैटर्न का मिलान


94

मुझे एक साधारण प्रश्न लिखना था जहाँ मैं लोगों के नाम की तलाश में हूँ जो B या D से शुरू होता है:

SELECT s.name 
FROM spelers s 
WHERE s.name LIKE 'B%' OR s.name LIKE 'D%'
ORDER BY 1

मैं सोच रहा था कि क्या इस तरह से अधिक प्रदर्शन करने के लिए फिर से लिखने का एक तरीका है। तो मैं बच सकता हूं orऔर / या like?


आप फिर से लिखने की कोशिश क्यों कर रहे हैं? प्रदर्शन? स्वच्छता? है s.nameअनुक्रमित?
मार्टिन स्मिथ

मैं प्रदर्शन के लिए लिखना चाहता हूं, s.name अनुक्रमित नहीं है।
लुकास कॉफमैन

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

जवाबों:


161

आपकी क्वेरी बहुत अधिक इष्टतम है। सिंटैक्स बहुत छोटा नहीं होगा, क्वेरी बहुत तेज़ नहीं होगी:

SELECT name
FROM   spelers
WHERE  name LIKE 'B%' OR name LIKE 'D%'
ORDER  BY 1;

यदि आप वास्तव में वाक्य रचना को छोटा करना चाहते हैं , तो शाखाओं के साथ एक नियमित अभिव्यक्ति का उपयोग करें :

...
WHERE  name ~ '^(B|D).*'

या चरित्र वर्ग के साथ थोड़ा तेज़ :

...
WHERE  name ~ '^[BD].*'

सूचकांक के बिना एक त्वरित परीक्षण SIMILAR TOमेरे लिए किसी भी मामले में की तुलना में तेजी से परिणाम देता है।
जगह में एक उपयुक्त बी-ट्री इंडेक्स के साथ, LIKEपरिमाण के आदेशों द्वारा इस दौड़ को जीतता है।

मैनुअल में पैटर्न मिलान के बारे में मूल बातें पढ़ें ।

बेहतर प्रदर्शन के लिए सूचकांक

यदि आप प्रदर्शन से चिंतित हैं, तो बड़ी तालिकाओं के लिए एक सूचकांक बनाएं:

CREATE INDEX spelers_name_special_idx ON spelers (name text_pattern_ops);

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

इस तरह के एक सूचकांक केवल बाएं-लंगर वाले पैटर्न (स्ट्रिंग की शुरुआत से मिलान) के लिए अच्छा है।

SIMILAR TOया बुनियादी बाईं-एंकर अभिव्यक्तियों के साथ नियमित अभिव्यक्ति इस सूचकांक का उपयोग कर सकते हैं, भी। लेकिन शाखाओं या चरित्र वर्गों के साथ नहीं (कम से कम पोस्टग्रेएसक्यूएल 9.0 पर मेरे परीक्षणों में)।(B|D)[BD]

ट्रिग्राम मैच या टेक्स्ट खोज विशेष GIN या GiST इंडेक्स का उपयोग करते हैं।

पैटर्न मिलान ऑपरेटरों का अवलोकन

  • LIKE( ~~) सरल और तेज है लेकिन अपनी क्षमताओं में सीमित है।
    ILIKE( ~~*) मामला असंवेदनशील रूप।
    pg_trgm दोनों के लिए सूचकांक समर्थन बढ़ाता है।

  • ~ (रेगुलर एक्सप्रेशन मैच) शक्तिशाली है, लेकिन अधिक जटिल है और बुनियादी अभिव्यक्तियों से अधिक कुछ के लिए धीमा हो सकता है।

  • SIMILAR TOबस व्यर्थ है । एक अजीबोगरीब LIKEऔर नियमित भाव। मैं इसका इस्तेमाल कभी नहीं करता। निचे देखो।

  • % "समानता" ऑपरेटर है, जो अतिरिक्त मॉड्यूल द्वारा प्रदान किया गया हैpg_trgm। निचे देखो।

  • @@पाठ खोज ऑपरेटर है। निचे देखो।

pg_trgm - ट्रिग्राम मिलान

PostgreSQL 9.1 के साथ शुरुआत करके आप GIN या GiST इंडेक्स का उपयोग करके किसी भी / पैटर्न (और साथ सरल रीजैक्स पैटर्न ) के pg_trgmलिए सूचकांक समर्थन प्रदान करने के लिए विस्तार की सुविधा दे सकते हैं । LIKEILIKE~

विवरण, उदाहरण और लिंक:

pg_trgmइन ऑपरेटरों को भी प्रदान करता है :

  • % - "समानता" ऑपरेटर
  • <%(कम्यूटेटर %>:) - पोस्टग्रेज 9.6 या बाद में "शब्द_सिमिलिटी" ऑपरेटर
  • <<%(कम्यूटेटर %>>:) - पोस्टग्रेज 11 या बाद में "सख्त_शब्द_सिमिलिटी" ऑपरेटर

पाठ्य खोज

एक विशेष प्रकार का पैटर्न है जो अलग-अलग बुनियादी ढांचे और सूचकांक प्रकारों के साथ मेल खाता है। यह शब्दकोशों और स्टेमिंग का उपयोग करता है और दस्तावेजों में शब्दों को खोजने के लिए एक महान उपकरण है, खासकर प्राकृतिक भाषाओं के लिए।

उपसर्ग मिलान भी समर्थित है:

साथ ही वाक्यांश खोज Postgres 9.6 के बाद से:

मैनुअल में परिचय और ऑपरेटरों और कार्यों के अवलोकन पर विचार करें ।

फजी स्ट्रिंग मिलान के लिए अतिरिक्त उपकरण

अतिरिक्त मॉड्यूल fuzzystrmatch कुछ और विकल्प प्रदान करता है, लेकिन आमतौर पर प्रदर्शन उपरोक्त सभी के लिए अवर है।

विशेष रूप से, levenshtein()फ़ंक्शन के विभिन्न कार्यान्वयन सहायक हो सकते हैं।

नियमित अभिव्यक्तियाँ ( ~) हमेशा से अधिक तेज़ क्यों होती हैं SIMILAR TO?

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

और सरल अभिव्यक्तियाँ जो LIKE( ~~) के साथ की जा सकती हैं वे LIKEवैसे भी तेज़ हैं ।

SIMILAR TOयह केवल PostgreSQL में समर्थित है क्योंकि यह SQL मानक के शुरुआती ड्राफ्ट में समाप्त हो गया है। उन्होंने अभी भी इससे छुटकारा नहीं पाया है। लेकिन इसे हटाने की योजना है और इसके बजाय रेगेक्सपी मैच शामिल हैं - या तो मैंने सुना।

EXPLAIN ANALYZEयह पता चलता है। बस किसी भी तालिका के साथ अपने आप को आज़माएं!

EXPLAIN ANALYZE SELECT * FROM spelers WHERE name SIMILAR TO 'B%';

पता चलता है:

...  
Seq Scan on spelers  (cost= ...  
  Filter: (name ~ '^(?:B.*)$'::text)

SIMILAR TOएक नियमित अभिव्यक्ति ( ~) के साथ फिर से लिखा गया है ।

इस विशेष मामले के लिए अंतिम प्रदर्शन

लेकिन और EXPLAIN ANALYZEखुलासा करता है। जगह-जगह उपरोक्त उल्लिखित सूचकांक के साथ प्रयास करें:

EXPLAIN ANALYZE SELECT * FROM spelers WHERE name ~ '^B.*;

पता चलता है:

...
 ->  Bitmap Heap Scan on spelers  (cost= ...
       Filter: (name ~ '^B.*'::text)
        ->  Bitmap Index Scan on spelers_name_text_pattern_ops_idx (cost= ...
              Index Cond: ((prod ~>=~ 'B'::text) AND (prod ~<~ 'C'::text))

आंतरिक रूप से, एक सूचकांक (जाता है कि स्थान-बारे में पता नहीं के साथ text_pattern_opsया का उपयोग करते हुए स्थान C:) सरल बाएं लंगर डाले भाव इन पाठ पैटर्न ऑपरेटरों के साथ दोबारा लिखे जाने पर ~>=~, ~<=~, ~>~, ~<~। यह मामला है ~, ~~या एक SIMILAR TOजैसे।

varcharसाथ varchar_pattern_opsया charसाथ प्रकार पर अनुक्रमित के लिए भी यही सच है bpchar_pattern_ops

इसलिए, मूल प्रश्न पर लागू किया जाता है, यह सबसे तेज़ संभव तरीका है :

SELECT name
FROM   spelers  
WHERE  name ~>=~ 'B' AND name ~<~ 'C'
    OR name ~>=~ 'D' AND name ~<~ 'E'
ORDER  BY 1;

बेशक, यदि आपको आसन्न आद्याक्षरों की खोज करनी चाहिए, तो आप इसे और सरल बना सकते हैं:

WHERE  name ~>=~ 'B' AND name ~<~ 'D'   -- strings starting with B or C

~या के सादे उपयोग पर लाभ ~~छोटा है। यदि प्रदर्शन आपकी सर्वोपरि आवश्यकता नहीं है, तो आपको मानक ऑपरेटरों के साथ रहना चाहिए - जो आपके पास पहले से ही सवाल है।


ओपी के पास नाम पर एक इंडेक्स नहीं है, लेकिन क्या आपको पता है, अगर उन्होंने ऐसा किया, तो क्या उनकी मूल क्वेरी में 2 रेंज सीक्स और similarएक स्कैन शामिल होगा?
मार्टिन स्मिथ

2
@ मर्टिनस्मिथ: EXPLAIN ANALYZE2 बिटमैप इंडेक्स स्कैन के साथ एक त्वरित परीक्षण । कई बिटमैप इंडेक्स स्कैन को जल्दी से जोड़ा जा सकता है।
इरविन ब्रान्डेसटेटर

धन्यवाद। तो वहाँ की जगह के साथ किसी भी लाभ होगा ORसाथ UNION ALLया जगह name LIKE 'B%'के साथ name >= 'B' AND name <'C'Postgres में?
मार्टिन स्मिथ

1
@MartinSmith: UNIONलेकिन, हाँ, श्रेणियों को एक WHEREखंड में संयोजित करने से क्वेरी को गति मिलेगी। मैंने अपने जवाब में और जोड़ दिया है। बेशक, आपको अपने स्थान को ध्यान में रखना होगा। स्थानीय-जागरूक खोज हमेशा धीमी होती है।
एरविन ब्रान्डसेट्टर

2
@a_horse_with_no_name: मुझे उम्मीद नहीं है। GIN अनुक्रमित के साथ pg_tgrm की नई क्षमताएं जेनेरिक पाठ खोज के लिए एक उपचार हैं। शुरुआत में एंकर की गई खोज पहले से तेज है।
इरविन ब्रांडस्टेटर

11

मेज पर एक स्तंभ जोड़ने के बारे में कैसे। अपनी वास्तविक आवश्यकताओं के आधार पर:

person_name_start_with_B_or_D (Boolean)

person_name_start_with_char CHAR(1)

person_name_start_with VARCHAR(30)

PostgreSQL बेस तालिकाओं में ला एसक्यूएल सर्वर पर गणना किए गए कॉलम का समर्थन नहीं करता है लेकिन नए कॉलम को ट्रिगर के माध्यम से बनाए रखा जा सकता है। जाहिर है, इस नए कॉलम को अनुक्रमित किया जाएगा।

वैकल्पिक रूप से, एक अभिव्यक्ति पर एक सूचकांक आपको एक ही, सस्ता प्रदान करेगा। उदाहरण के लिए:

CREATE INDEX spelers_name_initial_idx ON spelers (left(name, 1)); 

क्वेरी जो उनकी स्थितियों में अभिव्यक्ति से मेल खाती हैं, इस सूचकांक का उपयोग कर सकती हैं।

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


8

आप कोशिश कर सकते हैं

SELECT s.name
FROM   spelers s
WHERE  s.name SIMILAR TO '(B|D)%' 
ORDER  BY s.name

मुझे पता नहीं है कि या तो ऊपर या आपकी मूल अभिव्यक्ति या तो पोस्टग्रेज्स में बहुत ही सस्ती हैं या नहीं।

यदि आप सुझाए गए इंडेक्स बनाते हैं तो यह सुनने में भी दिलचस्पी होगी कि यह अन्य विकल्पों के साथ कैसे तुलना करता है।

SELECT name
FROM   spelers
WHERE  name >= 'B' AND name < 'C'
UNION ALL
SELECT name
FROM   spelers
WHERE  name >= 'D' AND name < 'E'
ORDER  BY name

1
इसने काम किया और मुझे १.१ ९ का खर्चा मिला जहाँ मेरे पास १.२५ था। धन्यवाद !
लुकास कॉफ़मैन

2

मैंने अतीत में जो किया है, एक समान प्रदर्शन के मुद्दे के साथ सामना करना है, आखिरी पत्र के एएससीआईआई चरित्र को बढ़ाना है, और एक बीटा करना है। LIKE कार्यक्षमता के सबसेट के लिए आपको तब सर्वश्रेष्ठ प्रदर्शन मिलता है। बेशक, यह केवल कुछ स्थितियों में काम करता है, लेकिन अल्ट्रा-बड़े डेटासेट्स के लिए जहां आप उदाहरण के लिए एक नाम खोज रहे हैं, यह प्रदर्शन को संक्षिप्त से स्वीकार्य बनाता है।


2

बहुत पुराना सवाल है, लेकिन मुझे इस समस्या का एक और तेज़ समाधान मिल गया है:

SELECT s.name 
FROM spelers s 
WHERE ascii(s.name) in (ascii('B'),ascii('D'))
ORDER BY 1

चूंकि फ़ंक्शन असिसी () केवल स्ट्रिंग के पहले चरित्र को देखता है।


1
क्या यह एक सूचकांक का उपयोग करता है (name)?
ypercube y

2

आदिकाल की जाँच के लिए, मैं अक्सर "char"(दोहरे उद्धरण चिह्नों के साथ) कास्टिंग का उपयोग करता हूं । यह पोर्टेबल नहीं है, लेकिन बहुत तेज है। आंतरिक रूप से, यह केवल पाठ को अलग करता है और पहले वर्ण को लौटाता है, और "चार" तुलनात्मक संचालन बहुत तेज है क्योंकि टाइप 1-बाइट तय लंबाई है:

SELECT s.name 
FROM spelers s 
WHERE s.name::"char" =ANY( ARRAY[ "char" 'B', 'D' ] )
ORDER BY 1

ध्यान दें कि कास्टिंग करने के लिए @ Sole021 द्वारा "char"तेजी से ascii()कम है, लेकिन यह UTF8 संगत नहीं है (या उस मामले के लिए कोई अन्य एन्कोडिंग), बस पहली बाइट लौटा रहा है, इसलिए केवल उन मामलों में उपयोग किया जाना चाहिए जहां तुलना सादे 7 के खिलाफ है -बिट ASCII वर्ण।


1

ऐसे मामलों से निपटने के लिए अभी तक दो तरीकों का उल्लेख नहीं किया गया है:

  1. आंशिक (या विभाजित - यदि पूर्ण श्रेणी के लिए मैन्युअल रूप से बनाया गया है) सूचकांक - सबसे उपयोगी है जब केवल डेटा का सबसेट आवश्यक है (उदाहरण के लिए कुछ रखरखाव के दौरान या कुछ रिपोर्टिंग के लिए अस्थायी):

    CREATE INDEX ON spelers WHERE name LIKE 'B%'
  2. तालिका को स्वयं विभाजित करना (पहले वर्ण को विभाजन की कुंजी के रूप में उपयोग करना) - यह तकनीक विशेष रूप से PostgreSQL 10+ (कम दर्दनाक विभाजन) और 11+ (क्वेरी निष्पादन के दौरान विभाजन की संभावना) पर विचार करने योग्य है।

इसके अलावा, यदि तालिका में डेटा को सॉर्ट किया जाता है, तो कोई BRIN इंडेक्स (पहले वर्ण पर) का उपयोग करके लाभ उठा सकता है ।


-4

संभवतः किसी एक पात्र की तुलना करने में तेज़:

SUBSTR(s.name,1,1)='B' OR SUBSTR(s.name,1,1)='D'

1
ज़रुरी नहीं। column LIKE 'B%'स्तंभ पर फ़ंक्शनिंग फ़ंक्शन का उपयोग करने से अधिक कुशल होगा।
ypercube।
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.