एक मनमाना सबस्ट्रिंग मैच के लिए स्ट्रिंग डेटा खोजना SQL सर्वर में एक महंगा ऑपरेशन हो सकता है। फॉर्म की क्वेरी Column LIKE '%match%'
बी-ट्री इंडेक्स की मांग क्षमताओं का उपयोग नहीं कर सकता है, इसलिए क्वेरी प्रोसेसर को प्रत्येक पंक्ति में व्यक्तिगत रूप से भविष्यवाणी लागू करनी होगी। इसके अलावा, प्रत्येक परीक्षण को जटिल संयोजन नियमों के पूर्ण सेट को सही ढंग से लागू करना चाहिए। इन सभी कारकों को एक साथ मिलाकर, इसमें कोई आश्चर्य की बात नहीं है कि इस प्रकार की खोजें संसाधन-गहन और धीमी हो सकती हैं।
पूर्ण-पाठ खोज भाषाई मिलान के लिए एक शक्तिशाली उपकरण है, और नई सांख्यिकीय अर्थपूर्ण खोज समान अर्थ वाले दस्तावेज़ खोजने के लिए बहुत बढ़िया है। लेकिन कभी-कभी, आपको वास्तव में केवल उन स्ट्रिंग्स को खोजने की ज़रूरत होती है जिनमें एक विशेष सबस्ट्रिंग होता है - एक सबस्ट्रिंग जो किसी भी भाषा में एक शब्द भी नहीं हो सकता है।
यदि खोजा गया डेटा बड़ा नहीं है, या प्रतिक्रिया समय की आवश्यकताएं महत्वपूर्ण नहीं हैं, तो LIKE '%match%'
का उपयोग करके एक उपयुक्त समाधान हो सकता है। लेकिन, उस विषम अवसर पर जहां सुपर-फास्ट खोज की आवश्यकता अन्य सभी विचारों (भंडारण स्थान सहित) को मात देती है, आप n-grams का उपयोग करके एक कस्टम समाधान पर विचार कर सकते हैं। इस लेख में खोजी गई विशिष्ट विविधता एक तीन वर्णों वाला ट्रिग्राम है।
ट्रिग्राम का उपयोग करके वाइल्डकार्ड खोज
ट्रिग्राम खोज का मूल विचार काफी सरल है:
- लक्षित डेटा के तीन-वर्ण वाले सबस्ट्रिंग (ट्रिग्राम) बनाए रखें।
- खोज शब्द(शब्दों) को त्रिकोण में विभाजित करें।
- संग्रहीत ट्रिगर (समानता खोज) के विरुद्ध खोज ट्रिगर का मिलान करें
- सभी ट्रिग्राम से मेल खाने वाली स्ट्रिंग्स को खोजने के लिए योग्य पंक्तियों को प्रतिच्छेद करें
- मूल खोज फ़िल्टर को बहुत कम किए गए चौराहे पर लागू करें
हम यह देखने के लिए एक उदाहरण के माध्यम से काम करेंगे कि यह सब कैसे काम करता है, और ट्रेड-ऑफ़ क्या हैं।
नमूना तालिका और डेटा
नीचे दी गई स्क्रिप्ट एक उदाहरण तालिका बनाती है और इसे स्ट्रिंग डेटा की एक लाख पंक्तियों के साथ भरती है। प्रत्येक स्ट्रिंग 20 वर्ण लंबी होती है, जिसमें पहले 10 वर्ण संख्यात्मक होते हैं। शेष 10 वर्ण ए से एफ तक की संख्याओं और अक्षरों का मिश्रण हैं, जो NEWID()
का उपयोग करके उत्पन्न होते हैं। . इस नमूना डेटा के बारे में कुछ खास नहीं है; ट्रिग्राम तकनीक काफी सामान्य है।
-- टेस्ट टेबलक्रिएट टेबल dbo.Example (आईडी इंटीजर आइडेंटिटी नॉट न्यूल, स्ट्रिंग चार(20) नॉट न्यूल, कॉन्स्ट्रेंट [पीके डीबीओ.उदाहरण (आईडी)] प्राथमिक कुंजी क्लस्टर (आईडी));जाओ-- 1 मिलियन RowINSERT dbo.Example with (TABLOCKX) (स्ट्रिंग) सेलेक्ट टॉप (1 * 1000 * 1000) - 10 न्यूमेरिक कैरेक्टर REPLACE (STR (RAND (CHECKSUM (NEWID ())) * 1e10, 10), स्पेस (1), ' 0') + -- प्लस 10 मिश्रित अंक + [ए-एफ] अक्षर RIGHT(NEWID(), 10) Master.dbo.spt_values से SV1CROSS JOIN Master.dbo.spt_values AS SV2OPTION (MAXDOP 1);
इसमें लगभग 3 सेकंड लगता है मेरे मामूली लैपटॉप पर डेटा बनाने और पॉप्युलेट करने के लिए। डेटा छद्म यादृच्छिक है, लेकिन एक संकेत के रूप में यह कुछ इस तरह दिखेगा:
डेटा नमूना
ट्रिग्राम जनरेट करना
निम्नलिखित इनलाइन फ़ंक्शन किसी दिए गए इनपुट स्ट्रिंग से अलग अल्फ़ान्यूमेरिक ट्रिग्राम उत्पन्न करता है:
--- एक स्ट्रिंग से ट्रिग्राम जेनरेट करें फंक्शन dbo.GenerateTrigrams (@string varchar(255))RETURNs tablewith SCHEMABINDINGAS RETURN with N16 AS ( VALUES (0),(0),(0),(0 से V.v चुनें) ),(0),(0),(0),(0), (0),(0),(0),(0),(0),(0),(0),(0) ) एएस वी (वी)), - संख्या तालिका (256) अंक एएस (चुनें n =ROW_NUMBER() ओवर (ए.वी द्वारा ऑर्डर) एन 16 से क्रॉस जॉइन एन 16 एएस बी के रूप में), ट्रिग्राम एएस (- प्रत्येक 3-वर्ण सबस्ट्रिंग टॉप चुनें (केस जब LEN(@string)> 2 तब LEN(@string) - 2 ELSE 0 END) ट्रिग्राम =सबस्ट्रिंग(@string, N.n, 3) N.n द्वारा N ORDER के रूप में संख्याओं से) - डुप्लिकेट निकालें और सभी सुनिश्चित करें तीन वर्ण अल्फ़ान्यूमेरिक हैं, ट्रिग्राम से डिस्टिंट टी.ट्रिग्राम का चयन करें जहां टी - बाइनरी कॉलेशन तुलना इसलिए आर एंजेस उम्मीद के मुताबिक काम करता है T.trigram COLLATE लैटिन1_General_BIN2 '%[^A-Z0-9a-z]%' पसंद नहीं है;
इसके उपयोग के उदाहरण के रूप में, निम्न कॉल:
dbo.GenerateTrigrams('SQLperformance.com') AS GT से GT.trigram चुनें;
निम्नलिखित ट्रिगर उत्पन्न करता है:
SQLperformance.com ट्रिगर
निष्पादन योजना इस मामले में टी-एसक्यूएल का काफी सीधा अनुवाद है:
- पंक्तियाँ बनाना (लगातार स्कैन के क्रॉस जॉइन)
- पंक्ति क्रमांकन (खंड और अनुक्रम परियोजना)
- स्ट्रिंग की लंबाई (शीर्ष) के आधार पर आवश्यक संख्याओं को सीमित करना
- गैर-अल्फ़ान्यूमेरिक वर्णों वाले ट्रिग्राम निकालें (फ़िल्टर)
- डुप्लीकेट हटाएं (अलग क्रम में)
ट्रिग्राम जनरेट करने की योजना
ट्रिग्राम लोड हो रहा है
अगला कदम उदाहरण डेटा के लिए ट्रिगर जारी रखना है। ट्रिगर एक नई तालिका में आयोजित किए जाएंगे, जो हमारे द्वारा अभी बनाए गए इनलाइन फ़ंक्शन का उपयोग करके पॉप्युलेट किया जाएगा:
- उदाहरण तालिका के लिए ट्रिग्राम टेबल डीबीओ बनाएं। उदाहरण ट्रिगर्स (आईडी पूर्णांक न्यूल नहीं, ट्रिग्राम चार (3) न्यूल नहीं); जाओ-- ट्रिगर्स उत्पन्न करें INSERT dbo.ExampleTrigrams with (TABLOCKX) (आईडी, ट्रिग्राम) ई.आईडी चुनें, GT.trigramFROM dbo.उदाहरण के रूप में लागू करें dbo.GenerateTrigrams(E.string) AS GT;
इसमें लगभग 20 सेकंड लगते हैं मेरे SQL सर्वर 2016 लैपटॉप इंस्टेंस पर निष्पादित करने के लिए। इस विशेष रन ने 17,937,972 पंक्तियों का उत्पादन किया 20-वर्ण परीक्षण डेटा की 1 मिलियन पंक्तियों के लिए ट्रिग्राम। निष्पादन योजना अनिवार्य रूप से उदाहरण तालिका की प्रत्येक पंक्ति के लिए मूल्यांकन की जा रही कार्य योजना को दर्शाती है:
ट्रिग्राम टेबल को पॉप्युलेट करना
चूंकि यह परीक्षण SQL सर्वर 2016 पर किया गया था (एक हीप तालिका लोड करना, डेटाबेस संगतता स्तर 130 के तहत, और एक TABLOCK
के साथ) संकेत), योजना समानांतर डालने से लाभान्वित होती है। उदाहरण तालिका के समानांतर स्कैन द्वारा थ्रेड्स के बीच पंक्तियों को वितरित किया जाता है, और उसके बाद उसी थ्रेड पर बने रहते हैं (कोई पुनर्विभाजन एक्सचेंज नहीं)।
सॉर्ट ऑपरेटर थोड़ा प्रभावशाली लग सकता है, लेकिन संख्याएं नेस्टेड लूप जॉइन के सभी पुनरावृत्तियों पर क्रमबद्ध पंक्तियों की कुल संख्या दिखाती हैं। वास्तव में, एक लाख अलग-अलग प्रकार हैं, प्रत्येक में 18 पंक्तियाँ हैं। चार (मेरे मामले में दो कोर हाइपरथ्रेडेड) की समानता की डिग्री पर, किसी भी समय अधिकतम चार छोटे प्रकार चल रहे हैं, और प्रत्येक प्रकार का उदाहरण स्मृति का पुन:उपयोग कर सकता है। यह बताता है कि इस निष्पादन योजना का अधिकतम स्मृति उपयोग मात्र 136KB . क्यों है (हालांकि 2,152 केबी दी गई थी)।
ट्रिग्राम तालिका में प्रत्येक स्रोत तालिका पंक्ति में प्रत्येक विशिष्ट ट्रिग्राम के लिए एक पंक्ति होती है (id
द्वारा पहचानी जाती है) ):
ट्रिग्राम टेबल का नमूना
अब हम ट्रिग्राम मिलानों की खोज में सहायता के लिए एक संकुल बी-ट्री इंडेक्स बनाते हैं:
-- Trigram search indexCREATE UNIQUE CLUSTERED INDEX [CUQ dbo.ExampleTrigrams (trigram, id)]ON dbo.ExampleTrigrams (trigram, id)with (DATA_COMPRESSION =ROW);
इसमें लगभग 45 सेकंड लगते हैं , हालांकि उनमें से कुछ सॉर्ट स्पिलिंग के कारण है (मेरा उदाहरण 4GB मेमोरी तक सीमित है)। अधिक मेमोरी वाला एक इंस्टेंस संभवत:पूरा कर सकता है कि कम से कम लॉग किए गए समानांतर इंडेक्स का निर्माण काफी तेज हो।
सूचकांक निर्माण योजना
ध्यान दें कि अनुक्रमणिका को अद्वितीय के रूप में निर्दिष्ट किया गया है (कुंजी में दोनों स्तंभों का उपयोग करके)। हम अकेले ट्रिग्राम पर एक गैर-अद्वितीय क्लस्टर इंडेक्स बना सकते थे, लेकिन SQL सर्वर ने वैसे भी लगभग सभी पंक्तियों में 4-बाइट यूनीक्विफायर जोड़े होंगे। एक बार जब हम उस uniquifiers को पंक्ति के चर-लंबाई वाले हिस्से (संबंधित ओवरहेड के साथ) में संग्रहीत कर लेते हैं, तो यह केवल id
को शामिल करने के लिए अधिक समझ में आता है कुंजी में और इसके साथ किया जाना है।
पंक्ति संपीड़न निर्दिष्ट किया गया है क्योंकि यह उपयोगी रूप से ट्रिग्राम तालिका के आकार को 277MB से 190MB तक कम कर देता है (तुलना के लिए, उदाहरण तालिका 32MB है)। यदि आप कम से कम SQL Server 2016 SP1 का उपयोग नहीं कर रहे हैं (जहां सभी संस्करणों के लिए डेटा संपीड़न उपलब्ध हो गया है) तो आप यदि आवश्यक हो तो संपीड़न खंड को छोड़ सकते हैं।
अंतिम अनुकूलन के रूप में, हम डेटा में सबसे अधिक और कम से कम सामान्य ट्रिगर खोजने के लिए इसे तेज़ और आसान बनाने के लिए ट्रिगर टेबल पर एक अनुक्रमित दृश्य भी बनाएंगे। यह चरण छोड़ा जा सकता है, लेकिन प्रदर्शन के लिए अनुशंसित है।
-- प्रत्येक ट्रिग्राम की चयनात्मकता (प्रदर्शन ऑप्टिमाइज़ेशन) क्रिएट व्यू dbo.ExampleTrigramCountswith SCHEMABINDINGASSELECT ET.trigram, cnt =COUNT_BIG(*)FROM dbo.ExampleTrigrams AS ETGROUP BY ET.trigram;GO-- व्यू को पूरा करें CUQ dbo.ExampleTrigramCounts (trigram)]ON dbo.ExampleTrigramCounts (trigram);
अनुक्रमित दृश्य निर्माण योजना
इसे पूरा होने में केवल कुछ सेकंड लगते हैं। भौतिक दृश्य का आकार छोटा है, बस 104KB ।
ट्रिग्राम सर्च
एक खोज स्ट्रिंग को देखते हुए (उदा. '%find%this%'
), हमारा दृष्टिकोण होगा:
- ट्रिग्राम का पूरा सेट तैयार करें खोज स्ट्रिंग के लिए
- अनुक्रमित दृश्य का उपयोग करके तीन सबसे चुनिंदा ट्रिग्राम खोजें
- सभी उपलब्ध ट्रिगर से मेल खाने वाली आईडी खोजें
- आईडी द्वारा स्ट्रिंग्स को पुनः प्राप्त करें
- ट्रिग्राम-योग्य पंक्तियों पर पूरा फ़िल्टर लागू करें
चुनिंदा ट्रिग्राम ढूँढना
पहले दो चरण बहुत सीधे हैं। हमारे पास पहले से ही एक मनमाना स्ट्रिंग के लिए ट्रिगर उत्पन्न करने के लिए एक फ़ंक्शन है। अनुक्रमित दृश्य में शामिल होकर उन ट्रिगर्स में से सबसे अधिक चयनात्मक ढूँढना प्राप्त किया जा सकता है। निम्नलिखित कोड एक अन्य इनलाइन फ़ंक्शन में हमारी उदाहरण तालिका के कार्यान्वयन को लपेटता है। यह बाद में उपयोग में आसानी के लिए तीन सबसे चुनिंदा ट्रिग्राम को एक पंक्ति में पिवट करता है:
-- एक खोज स्ट्रिंग के लिए सबसे चुनिंदा ट्रिग्राम-- हमेशा एक पंक्ति देता है (यदि कोई ट्रिगर नहीं मिला तो नल) फ़ंक्शन बनाएं dbo.Example_GetBestTrigrams (@string varchar (255)) SCHEMABINDING ASRETURN चयन के साथ तालिका लौटाएं - पिवट ट्रिगर 1 =MAX ( केस जब BT.rn =1 तब BT.trigram END), trigram2 =MAX (केस जब BT.rn =2 तब BT.trigram END), trigram3 =MAX (केस जब BT.rn =3 तब BT.trigram END) से (- खोज स्ट्रिंग के लिए ट्रिगर उत्पन्न करें - और सबसे चुनिंदा तीन चुनें टॉप (3) आरएन =ROW_NUMBER() ओवर (ईटीसी.सीएनटी एएससी द्वारा ऑर्डर), जीटी.ट्रिग्राम डीबीओ से। जेनरेटट्रिग्राम्स(@स्ट्रिंग) एएस जीटी dbo.ExampleTrigram में शामिल हों ETC के साथ ETC.trigram =GT.trigram ORDER BY ETC.cnt ASC ) AS BT;
उदाहरण के तौर पर:
ईजीबीटी के रूप में ईजीबीटी.ट्रिग्राम1, ईजीबीटी.ट्रिग्राम2, ईजीबीटी.ट्रिग्राम3 को डीबीओ से चुनें।Example_GetBestTrigrams('%1234%5678%') ASBT;
रिटर्न (मेरे नमूना डेटा के लिए):
चुने गए ट्रिग्राम
निष्पादन योजना है:
GetBestTrigrams निष्पादन योजना
यह पहले से परिचित ट्रिग्राम-जनरेटिंग प्लान है, इसके बाद प्रत्येक ट्रिग्राम के लिए अनुक्रमित दृश्य में एक लुकअप, मैचों की संख्या के आधार पर छाँटना, पंक्तियों की संख्या (सीक्वेंस प्रोजेक्ट), सेट को तीन पंक्तियों (शीर्ष) तक सीमित करना, फिर पिवट करना परिणाम (स्ट्रीम एग्रीगेट)।
सभी ट्रिगर से मेल खाने वाली आईडी ढूंढना
अगला चरण उदाहरण तालिका पंक्ति आईडी ढूंढना है जो पिछले चरण द्वारा पुनर्प्राप्त सभी गैर-शून्य ट्रिग्राम से मेल खाते हैं। यहाँ झुर्रियाँ यह है कि हमारे पास शून्य, एक, दो या तीन ट्रिगर उपलब्ध हो सकते हैं। निम्नलिखित कार्यान्वयन एक बहु-कथन फ़ंक्शन में आवश्यक तर्क को लपेटता है, एक तालिका चर में योग्यता आईडी लौटाता है:
-- सभी प्रदान किए गए (गैर-शून्य) ट्रिगर से मेल खाने वाली उदाहरण आईडी देता है फ़ंक्शन dbo बनाएं।Example_GetTrigramMatchIDs(@Trigram1 char(3), @Trigram2 char(3), @Trigram3 char(3))RETURNS @IDs तालिका (आईडी पूर्णांक) प्राथमिक कुंजी) SCHEMABINDING ASBEGIN के साथ यदि @ Trigram1 पूर्ण प्रारंभ नहीं है यदि @ Trigram2 पूर्ण प्रारंभ नहीं है यदि @ Trigram3 पूर्ण प्रारंभ नहीं है -- 3 ट्रिगर उपलब्ध INSERT @IDs (id) dbo.ExampleTrigrams ASET1 से ET1.id चुनें .trigram =@ Trigram1 इंटरसेक्ट सेलेक्ट ET2.id से dbo.ExampleTrigrams AS ET2 जहां ET2.trigram =@ Trigram2 इंटरसेक्ट सेलेक्ट ET3.id से dbo.ExampleTrigrams AS ET3 जहां ET3.trigram =@Trigram3 OPTION); अंत; ELSE BEGIN - 2 ट्रिगर उपलब्ध INSERT @IDs (id) dbo से ET1.id चुनें। ET1 के रूप में उदाहरण ट्राइग्राम जहां ET1.trigram =@ Trigram1 dbo.ExampleTrigrams AS ET2 से चुनें। मर्ज जॉइन); अंत; अंत; ELSE BEGIN -- 1 ट्रिगर उपलब्ध INSERT @IDs (id) dbo से ET1.id चुनें। ET1 के रूप में उदाहरण Trigrams जहां ET1.trigram =@ Trigram1; अंत; अंत; वापसी;अंत;
इस फ़ंक्शन के लिए अनुमानित निष्पादन योजना रणनीति दिखाती है:
ट्रिग्राम मैच आईडी प्लान
यदि एक ट्रिग्राम उपलब्ध है, तो ट्रिग्राम टेबल में एक ही खोज की जाती है। अन्यथा, दो या तीन तलाशी की जाती हैं और कुशल एक-से-कई मर्ज का उपयोग करके पाए जाने वाले आईडी का प्रतिच्छेदन। इस योजना में कोई मेमोरी लेने वाला ऑपरेटर नहीं है, इसलिए हैश या सॉर्ट स्पिल की कोई संभावना नहीं है।
उदाहरण खोज को जारी रखते हुए, हम नए फ़ंक्शन को लागू करके उपलब्ध ट्रिगर से मेल खाने वाली आईडी ढूंढ सकते हैं:
dbo से EGTMID.id चुनें।यह निम्न जैसा सेट देता है:
मिलान आईडी
नए फ़ंक्शन के लिए वास्तविक (निष्पादन के बाद) योजना तीन ट्रिग्राम इनपुट के साथ योजना का आकार दिखाती है:
वास्तविक आईडी मिलान योजना
यह काफी अच्छी तरह से मिलान करने वाले ट्रिग्राम की शक्ति को दर्शाता है। हालांकि तीनों त्रिकोण प्रत्येक उदाहरण तालिका में लगभग 11,000 पंक्तियों की पहचान करते हैं, पहला चौराहा इस सेट को 1,004 पंक्तियों तक कम कर देता है, और दूसरा चौराहा इसे सिर्फ 7 तक कम कर देता है ।
ट्रिग्राम खोज का पूर्ण कार्यान्वयन
अब जब हमारे पास ट्रिग्राम से मेल खाने वाली आईडी हैं, तो हम उदाहरण तालिका में मेल खाने वाली पंक्तियों को देख सकते हैं। हमें अभी भी मूल खोज शर्त को अंतिम जांच के रूप में लागू करने की आवश्यकता है, क्योंकि ट्रिगर झूठी सकारात्मक (लेकिन झूठी नकारात्मक नहीं) उत्पन्न कर सकते हैं। संबोधित करने के लिए अंतिम मुद्दा यह है कि पिछले चरणों में कोई ट्रिगर नहीं मिला है। यह हो सकता है, उदाहरण के लिए, क्योंकि खोज स्ट्रिंग में बहुत कम जानकारी है।
'%FF%'
. की खोज स्ट्रिंग ट्रिग्राम खोज का उपयोग नहीं कर सकते क्योंकि दो वर्ण एक भी ट्रिगर उत्पन्न करने के लिए पर्याप्त नहीं हैं। इस परिदृश्य को शानदार ढंग से संभालने के लिए, हमारी खोज इस स्थिति का पता लगाएगी और एक गैर-ट्रिग्राम खोज पर वापस आ जाएगी।निम्नलिखित अंतिम इनलाइन फ़ंक्शन आवश्यक तर्क को लागू करता है:
-- सर्च इंप्लीमेंटेशनक्रिएट फंक्शन dbo.Example_TrigramSearch(@Search varchar(255))SCHEMABINDINGASRETURN सेलेक्ट रिजल्ट के साथ रिटर्न टेबल। dbo.Example_GetBestTrigrams(@Search) AS GBT क्रॉस एप्लाई ई। स्ट्रिंग FROM dbo.Example_GetTrigramMatchIDs (GBT.trigram1, GBT.trigram2, GBT.trigram3) जैसे कि MID JOIN dbo.उदाहरण के रूप में E ON E.id =MID.id WHERE -- कम से कम एक ट्रिग्राम मिला GBT.trigram1 NULL और E नहीं है .string LIKE @Search Union ALL - नॉन-ट्रिग्राम सर्च सेलेक्ट E.id, E.string FROM dbo.Example as E WHERE - कोई ट्रिग्राम नहीं मिला GBT.trigram1 IS NULL और E.string LIKE @Search ) AS परिणाम;मुख्य विशेषता
GBT.trigram1
. का बाहरी संदर्भ हैUNION ALL
. के दोनों ओर . ये निष्पादन योजना में स्टार्ट-अप अभिव्यक्तियों के साथ फ़िल्टर में अनुवाद करते हैं। एक स्टार्ट-अप फ़िल्टर केवल अपने सबट्री को तभी निष्पादित करता है जब उसकी स्थिति का मूल्यांकन सही होता है। शुद्ध प्रभाव यह है कि संघ का केवल एक हिस्सा सभी को निष्पादित किया जाएगा, इस पर निर्भर करता है कि हमें एक ट्रिग्राम मिला या नहीं। निष्पादन योजना का प्रासंगिक हिस्सा है:स्टार्ट-अप फ़िल्टर प्रभाव
या तो
Example_GetTrigramMatchIDs
फ़ंक्शन निष्पादित किया जाएगा (और परिणाम आईडी पर एक खोज का उपयोग करके उदाहरण में देखे गए), या उदाहरण के क्लस्टर इंडेक्स स्कैन अवशिष्टLIKE
के साथ विधेय चलेगा, लेकिन दोनों नहीं।प्रदर्शन
निम्नलिखित कोड समतुल्य
LIKE
. के विरुद्ध ट्रिग्राम खोज के प्रदर्शन का परीक्षण करता है :सेट सांख्यिकी एक्सएमएल ऑफडेक्लेयर @S डेटाटाइम2 =SYSUTCDATETIME(); F2.stringFROM dbo.Example के रूप में F2WHERE F2.string जैसे '%1234%5678%' विकल्प (MAXDOP 1) का चयन करें; ElapsedMS =DATEDIFF (मिलीसेकंड, @S, SYSUTCDATETIME ()) का चयन करें; GOSET सांख्यिकी एक्सएमएल OFFDECLARE @S डेटाटाइम 2 =SYSUTCDATETIME (); ETS.stringFROM dbo.Example_TrigramSearch('%1234%5678%') AS ETS से चुनें; ElapsedMS =DATEDIFF(MILLISECOND, @S, SYSUTCDATETIME ()) का चयन करें;ये दोनों एक ही परिणाम पंक्ति (पंक्तियों) का उत्पादन करते हैं लेकिन
LIKE
क्वेरी 2100ms . तक चलती है , जबकि ट्रिग्राम खोज में 15ms . लगता है ।इससे भी बेहतर प्रदर्शन संभव है। आम तौर पर, प्रदर्शन में सुधार होता है क्योंकि ट्रिगर अधिक चयनात्मक हो जाते हैं, और संख्या में कम (इस कार्यान्वयन में अधिकतम तीन से नीचे)। उदाहरण के लिए:
सेट सांख्यिकी एक्सएमएल ऑफडेक्लेयर @S डेटाटाइम2 =SYSUTCDATETIME(); ETS.stringFROM dbo.Example_TrigramSearch('%BEEF%') AS ETS से चुनें; ElapsedMS =DATEDIFF(MILLISECOND, @S, SYSUTCDATETIME ()) का चयन करें;उस खोज ने SSMS ग्रिड में 4ms . में 111 पंक्तियाँ लौटा दीं .
LIKE
समकक्ष 1950ms . के लिए चला ।ट्रिग्राम बनाए रखना
यदि लक्ष्य तालिका स्थिर है, तो स्पष्ट रूप से आधार तालिका और संबंधित ट्रिग्राम तालिका को सिंक्रनाइज़ रखने में कोई समस्या नहीं है। इसी तरह, यदि खोज परिणामों को हर समय पूरी तरह से अद्यतन करने की आवश्यकता नहीं है, तो ट्रिग्राम तालिका (तालिकाओं) का एक शेड्यूल्ड रिफ्रेश अच्छा काम कर सकता है।
अन्यथा, हम ट्रिग्राम खोज डेटा को अंतर्निहित स्ट्रिंग्स के साथ सिंक्रनाइज़ रखने के लिए कुछ काफी सरल ट्रिगर्स का उपयोग कर सकते हैं। सामान्य विचार हटाए गए और सम्मिलित पंक्तियों के लिए ट्रिग्राम उत्पन्न करना है, फिर उन्हें ट्रिग्राम तालिका में उपयुक्त के रूप में जोड़ना या हटाना है। नीचे दिए गए ट्रिगर डालें, अपडेट करें और हटाएं इस विचार को व्यवहार में दिखाते हैं:
-- उदाहरण डालने के बाद ट्रिग्राम बनाए रखें, ट्रिगर बनाए रखेंTrigrams_AION dbo.ExampleAFTER INSERTASBEGIN IF @@ROWCOUNT =0 RETURN; IF TRIGGER_NESTLEVEL(@@PROCID, 'AFTER', 'DML')> 1 RETURN; खाता चालू करें; पंक्ति 0 सेट करें; -- संबंधित ट्रिग्राम डालें INSERT dbo.ExampleTrigrams (id, trigram) INS.id चुनें, GT.trigram से INS क्रॉस लागू करें dbo.GenerateTrigrams(INS.string) AS GT;END;-- उदाहरण के हटाए जाने के बाद ट्रिगर बनाए रखें, TRIGGER बनाए रखेंTrigrams_ADON dbo.ExampleAFTER DELETEASBEGIN IF @@ROWCOUNT =0 RETURN; IF TRIGGER_NESTLEVEL(@@PROCID, 'AFTER', 'DML')> 1 RETURN; खाता चालू करें; पंक्ति 0 सेट करें; - हटाए गए संबंधित ट्रिगर्स DELETE ET के साथ (SRIALIZABLE) से DEL CROSS APPLY dbo.GenerateTrigrams(DEL.string) AS GT JOIN dbo.ExampleTrigrams AS ET ON ET.trigram =GT.trigram DEL और ET.id; END;-- उदाहरण अपडेट के बाद ट्रिगर बनाए रखें, TRIGGER बनाए रखेंTrigrams_AUON dbo.ExampleAFTER UPDATEASBEGIN IF @@ROWCOUNT =0 RETURN; IF TRIGGER_NESTLEVEL(@@PROCID, 'AFTER', 'DML')> 1 RETURN; खाता चालू करें; पंक्ति 0 सेट करें; - हटाए गए संबंधित ट्रिगर्स DELETE ET के साथ (SRIALIZABLE) से DEL CROSS APPLY dbo.GenerateTrigrams(DEL.string) AS GT JOIN dbo.ExampleTrigrams AS ET ON ET.trigram =GT.trigram DEL और ET.id; -- संबंधित ट्रिग्राम डालें INSERT dbo.ExampleTrigrams (id, trigram) INS.id चुनें, GT.trigram से INS क्रॉस लागू करें dbo.GenerateTrigrams(INS.string) AS GT;END;ट्रिगर काफी कुशल हैं, और एकल और बहु-पंक्ति दोनों परिवर्तनों को संभाल लेंगे (एक
MERGE
का उपयोग करते समय उपलब्ध कई क्रियाओं सहित) बयान)। हमें किसी भी ट्रिगर कोड को लिखने की आवश्यकता के बिना, ट्रिग्राम टेबल पर अनुक्रमित दृश्य स्वचालित रूप से SQL सर्वर द्वारा बनाए रखा जाएगा।ट्रिगर ऑपरेशन
उदाहरण के तौर पर, उदाहरण तालिका से एक मनमाना पंक्ति को हटाने के लिए एक कथन चलाएँ:
-- सिंगल रो डिलीटडिलीट टॉप (1) dbo.Example;निष्पादन के बाद (वास्तविक) निष्पादन योजना में ट्रिगर हटाने के बाद के लिए एक प्रविष्टि शामिल है:
ट्रिगर निष्पादन योजना हटाएं
योजना का पीला भाग हटाए गए . से पंक्तियों को पढ़ता है पेसुडो-टेबल, प्रत्येक हटाए गए उदाहरण स्ट्रिंग (हरे रंग में हाइलाइट की गई परिचित योजना का उपयोग करके) के लिए ट्रिगर उत्पन्न करता है, फिर संबंधित ट्रिग्राम तालिका प्रविष्टियों को ढूंढता है और हटा देता है। योजना का अंतिम भाग, लाल रंग में दिखाया गया है, अनुक्रमित दृश्य को अद्यतित रखने के लिए SQL सर्वर द्वारा स्वचालित रूप से जोड़ा जाता है।
इंसर्ट ट्रिगर की योजना बेहद समान है। अपडेट को डिलीट करने के बाद इंसर्ट करके हैंडल किया जाता है। इन योजनाओं को देखने के लिए निम्न स्क्रिप्ट चलाएँ और पुष्टि करें कि नई और अद्यतन पंक्तियों को ट्रिग्राम खोज फ़ंक्शन का उपयोग करके खोजा जा सकता है:
-- सिंगल रो इंसर्ट INSERT dbo.Example (स्ट्रिंग) VALUES ('SQLPerformance.com'); -- ETS के रूप में dbo.Example_TrigramSearch('%perf%') से नई पंक्ति चुनें ETS.stringFROM; - सिंगल रो अपडेटअपडेट टॉप (1) dbo.Example SET string ='12345678901234567890'; -- बहु-पंक्ति सम्मिलित करेंINSERT dbo.Example with (TABLOCKX) (स्ट्रिंग) सेलेक्ट टॉप (1000) REPLACE(STR(RAND(CHECKSUM(NEWID())) * 1e10, 10), SPACE(1), '0') + राइट (नया (), 10) Master.dbo.spt_values से SV1 के रूप में; -- बहु-पंक्ति अद्यतन अद्यतन शीर्ष (1000) dbo.Example SET string ='12345678901234567890'; - अपडेट की गई पंक्तियों के लिए खोजें, dbo से ETS.string चुनें।Example_TrigramSearch('12345678901234567890') AS ETS;मर्ज उदाहरण
अगली स्क्रिप्ट एक
MERGE
दिखाती है उदाहरण तालिका को एक साथ सम्मिलित करने, हटाने और अद्यतन करने के लिए उपयोग किया जा रहा कथन:-- MERGE डेमोडेक्लेयर @MergeData टेबल (आईडी इंटीजर UNIQUE CLUSTERED NULL, ऑपरेशन char(3) NOT NULL, string char(20) NULL); INSERT @MergeData (id, ऑपरेशन, स्ट्रिंग) VALUES (NULL, 'INS', '11223344556677889900'), -- इन्सर्ट (1001, 'DEL', NULL), -- Delete (2002, 'UPD', '00000000000000000000'); - अद्यतन घोषणा @ क्रियाएँ तालिका (कार्रवाई $ nvarchar(10) NULL नहीं, old_id पूर्णांक NULL, old_string char(20) NULL, new_id पूर्णांक NULL, new_string char(20) NULL); MERGE dbo.Example के रूप में EUSING @MergeData MD पर MD.id =E.idWHEN MATCHED और MD.operation ='DEL' फिर DELETEWHEN MATCHED और MD.operation ='UPD' फिर UPD SET E.string =MD.stringWHEN MATCHED नहीं और MD.ऑपरेशन ='INS' तब INSERT (स्ट्रिंग) VALUES (MD.string) OUTPUT $action, Deleted.id, Deleted.string, Inserted.id, Inserted.stringINTO @Actions (कार्रवाई $, old_id, old_string, new_id, न्यू_स्ट्रिंग); चुनें * @Actions AS A से;आउटपुट कुछ इस तरह दिखाएगा:
एक्शन आउटपुट
अंतिम विचार
ट्रिगर उत्पन्न करने के बजाय सीधे आईडी को संदर्भित करके बड़े डिलीट और अपडेट ऑपरेशंस को तेज करने की कुछ गुंजाइश है। इसे यहां लागू नहीं किया गया है क्योंकि इसके लिए उपयोग किए गए (पहले से ही महत्वपूर्ण) भंडारण स्थान को दोगुना करने के लिए ट्रिगर टेबल पर एक नए गैर-संकुल सूचकांक की आवश्यकता होगी। ट्रिग्राम तालिका में एक पूर्णांक और एक
char(3)
होता है प्रति पंक्ति; पूर्णांक कॉलम पर एक गैर-संकुल सूचकांकchar(3)
. प्राप्त करेगा सभी स्तरों पर कॉलम (क्लस्टर इंडेक्स के सौजन्य से, और इंडेक्स कीज़ को हर स्तर पर अद्वितीय होने की आवश्यकता)। विचार करने के लिए मेमोरी स्पेस भी है, क्योंकि जब सभी रीड कैश से होते हैं तो ट्रिग्राम सर्च सबसे अच्छा काम करता है।अतिरिक्त इंडेक्स कैस्केडिंग रेफ़रेंशियल अखंडता को एक विकल्प बना देगा, लेकिन यह अक्सर इसके लायक होने की तुलना में अधिक परेशानी वाला होता है।
ट्रिग्राम सर्च कोई रामबाण इलाज नहीं है। अतिरिक्त भंडारण आवश्यकताएं, कार्यान्वयन जटिलता, और अद्यतन प्रदर्शन पर प्रभाव सभी इसके खिलाफ भारी हैं। यह तकनीक उन खोजों के लिए भी बेकार है जो कोई ट्रिगर उत्पन्न नहीं करती हैं (न्यूनतम 3 वर्ण)। यद्यपि यहां दिखाया गया मूल कार्यान्वयन कई प्रकार की खोज को संभाल सकता है (एकाधिक वाइल्डकार्ड के साथ शुरू होता है, होता है, समाप्त होता है) यह हर संभव खोज अभिव्यक्ति को कवर नहीं करता है जिसे
LIKE
के साथ काम करने के लिए बनाया जा सकता है। . यह उन खोज स्ट्रिंग्स के लिए अच्छा काम करता है जो AND-प्रकार के ट्रिगर उत्पन्न करते हैं; खोज स्ट्रिंग को संभालने के लिए और अधिक काम करने की आवश्यकता है जिसके लिए OR-टाइप हैंडलिंग की आवश्यकता होती है, या अधिक उन्नत विकल्प जैसे रेगुलर एक्सप्रेशन।जो कुछ भी कहा गया है, यदि आपका आवेदन वास्तव में जरूरी . है तेज़ वाइल्डकार्ड स्ट्रिंग खोजें, n-grams गंभीरता से विचार करने के लिए कुछ हैं।
संबंधित सामग्री:हारून बर्ट्रेंड द्वारा एक प्रमुख %वाइल्डकार्ड के लिए अनुक्रमणिका प्राप्त करने का एक तरीका।