- SQL anti-patterns क्वेरी और डेटा पाइपलाइन की maintainability को कठिन बना देते हैं, और अपेक्षा से धीमा performance पैदा करते हैं
- CASE WHEN का अत्यधिक उपयोग, indexed columns पर function apply करना, SELECT *, DISTINCT का अत्यधिक उपयोग, nested views और subqueries, गहरी dependency structure आदि इसके प्रतिनिधि उदाहरण हैं
- ज़्यादातर समस्याएँ speed और deadline pressure की वजह से अपनाए गए अस्थायी workaround से पैदा होती हैं, और लंबे समय में data reliability और development speed—दोनों को नुकसान पहुँचाती हैं
- समाधान के तौर पर स्पष्ट join definitions, dimension tables का उपयोग, अनावश्यक nesting हटाना, और periodic view cleanup की ज़रूरत होती है
- SQL को सिर्फ एक simple script नहीं बल्कि team level पर managed production code की तरह handle करना चाहिए, और readability को ध्यान में रखकर किया गया शुरुआती design rework को कम करता है
परिचय
- आज हम SQL anti-patterns के कुछ ऐसे उदाहरणों पर बात करेंगे जो अक्सर दिखाई देते हैं और जिनका प्रभाव बड़ा होता है
- ये समस्याएँ data reliability में गिरावट, query development speed में कमी जैसी बुरी श्रृंखला पैदा करती हैं
- नीचे दी गई सूची सभी मामलों को शामिल नहीं करती; अगर आप गहरी समझ चाहते हैं, तो Bill Karwin की पुस्तक की सिफारिश की जाती है
जरूरत से ज़्यादा जटिल CASE WHEN statements
- बड़े सिस्टम में state codes (जैसे: 1=out of stock) को इंसानों के पढ़ने लायक बनाने के लिए CASE WHEN statements का अक्सर उपयोग किया जाता है
- dashboard या report को जल्दी बनाने के लिए किसी एक View में ही उस CASE WHEN logic को जोड़ देना लंबे समय में एक anti-pattern बन जाता है
- इससे duplicate logic की copy/paste, interpretation mismatch जैसी समस्याएँ पैदा होती हैं और पूरा query environment बेतरतीब हो जाता है
- इसका समाधान यह है कि state code conversion के लिए अलग से dimension table या shared view बनाया जाए, ताकि reusability मिल सके
indexed columns पर function का उपयोग
WHERE UPPER(name) = 'ABC' की तरह indexed column पर function apply करने से index की efficiency खत्म हो जाती है
- SQL Server आदि में इसकी वजह से अनावश्यक full table scan हो सकता है
- इसका समाधान है function-applied column को अलग से index करना या input value को convert करके query condition को सरल बनाना
views में SELECT * का उपयोग
- view बनाते समय SELECT * सुविधाजनक लग सकता है, लेकिन structure (schema) बदलते ही view आसानी से टूट सकता है
- इसमें अनावश्यक columns भी शामिल हो जाते हैं, जिससे अनचाही dependencies और performance issues पैदा होते हैं; इसलिए columns को explicitly select करना चाहिए
DISTINCT का अत्यधिक उपयोग करके duplicate “हल” करना
- जब गलत join की वजह से duplicate results आते हैं, तो SELECT DISTINCT से अस्थायी समाधान करना data integrity problem को छिपा देता है
- असली कारण join condition का अधूरापन या relationship definition (1:1, 1:N आदि) में गलती होता है
- सही समाधान है join logic को मजबूत करके relationship definition को स्पष्ट करना, और aggregation या reporting से पहले relationship consistency सुनिश्चित करना
view nesting (Excessive View Layer Stacking)
- जब कई टीमें existing views को reuse करते हुए लगातार नए views जोड़ती रहती हैं, तो dependency chain जटिल हो जाती है और performance तेज़ी से गिरता है
- debugging कठिन हो जाती है, और query को expand करना “पुरातात्विक खुदाई” जितना मुश्किल हो सकता है
- समय-समय पर transformation logic को flatten करना चाहिए, और complex operations को स्पष्ट base view या table में materialize करने की strategy अपनानी चाहिए
बहुत गहरी subqueries
- 3~4 स्तर से अधिक deeply nested subqueries readability को घटाती हैं और debugging को कठिन बनाती हैं
- 5000 से अधिक पंक्तियों वाली subqueries के उदाहरण भी मौजूद हैं
- CTE(Common Table Expression) का उपयोग करने से logical stages को अलग करना आसान होता है और query की clarity(readability) बढ़ती है
निष्कर्ष
- SQL ऊपर से सरल दिख सकता है, लेकिन सिस्टम बड़ा होने पर इसकी complexity बढ़ती जाती है
- ज़्यादातर anti-patterns बुरी मंशा से नहीं, बल्कि ‘तेज़ परिणाम’ पाने के लिए किए गए समझौते (speed, deadline, workaround) की वजह से पैदा होते हैं
- SQL को code की तरह manage करना (version control, code review, स्पष्ट design) लंबे समय में productivity और reliability—दोनों को सुनिश्चित कर सकता है
- शुरुआत में कुछ मिनट लगाकर clarity और consistency पर ध्यान देने से आगे चलकर rework और confusion काफ़ी कम हो जाते हैं
5 टिप्पणियां
अभी समय नहीं है, इसलिए सोचा था कि फिलहाल बस तात्कालिक समस्या सुलझा लें और बाद में फिर से लिखेंगे, लेकिन ऐसी चीज़ें जमा होती-जाती हैं और आखिरकार भयानक query hell बन जाती हैं। मैंने भी ऐसी कई चीज़ें बनाई हैं। यह जानते हुए भी कि उन्हें फिर से लिखने वाला वह "बाद में" कभी नहीं आने वाला।
कह...
"ज़्यादातर समस्याएँ गति और deadline के दबाव की वजह से निकाले गए अस्थायी जुगाड़ू समाधानों से पैदा होती हैं"
हाय..
Hacker News की राय
addresses_citiesजैसी table बनाना ज़रूरी नहीं लगताLIMIT 1इसलिए जोड़ा था क्योंकि मुझे लगा था कि ज़्यादा से ज़्यादा एक ही result आएगा, और इस पर भी इसी तरह का नकारात्मक feedback मिला। लेकिन बड़ी tables में (sqlite,mysql,postgresqlसभी में) अक्सर ऐसा लगता है कि DB मनचाहा record मिल जाने के बाद भी पूरी table स्कैन करता रहता हैSELECT x FROM tquery में DISTINCT को सुरक्षित रूप से कब छोड़ा जा सकता है। मान लीजिए किtके schema मेंxपरPRIMARYयाUNIQUEconstraint है, फिर भी कोई कभी भीUNIQUEconstraint हटा सकता है। तब duplicate आने लगेंगे और आप कारण ढूँढते रह जाएँगे। SQL set भाषा नहीं बल्कि bag भाषा है। runtime परrel tऔर attributexमिलते ही वह बस values लौटाता है। duplicate आ सकते हैं, type भी बदल सकता है। अगर आपको Set चाहिए, तो DISTINCT साफ़-साफ़ लिखना चाहिए। query planner runtime परUNIQUEयाPRIMARYहोने पर deduplication नहीं करेगाneo4jमें complex data के साथ काम करते समय duplicate nodes बहुत आसानी से result में आ जाते हैं, इसलिए DISTINCT ज़रूरी हो जाता है। खासकर variable-length relationships में DISTINCT न हो तो query धीमी भी हो जाती है और duplicates भी बहुत बढ़ जाते हैंhttps://kb.databasedesignbook.com/posts/systematic-design-of-join-queries/
!=याNOT IN (...)का इस्तेमाल ज़्यादातर मामलों में inefficient होता है (हालाँकि जब दूसरी conditions result set को काफ़ी छोटा कर दें, तब ठीक हो सकता है)। और यह समझना ज़रूरी है कि DBnullको कैसे handle करता है।nullऔर empty string एक ही हैं या नहीं,null == nullहै या नहीं, यह DB के अनुसार बदल सकता हैnullhandling और indexing के संदर्भ में, जिन DBs का मैंने उपयोग किया है वेnullvalues को index नहीं करते, इसलिए"WHERE col IS NULL"query,colपर index होने के बावजूद, inefficient चलती है। अगर सचमुच ज़रूरत हो, तोcolकेnullहोने को दिखाने वालाchar(1)याbitcolumn बनाकर उस field पर index करना बेहतर रहेगा!=याNOT IN (...)लगभग हमेशा inefficient होते हैं, तो क्यों? अगर दाहिनी ओर का मान constant हो, तो hash table lookup हो सकता है और सामान्यतः efficient होना चाहिए। क्या इसका कोई बेहतर विकल्प है?SQL databaseकहना थोड़ा अस्पष्ट है। SQL सिर्फ़ relational ही नहीं, non-relational DBs में भी लागू हुआ है। विशेषज्ञ SQL की समस्याओं को बहुत पहले से पहचानते आए हैं, और Chris Date तथा Hugh Darwen का Tutorial D जैसे विकल्प भी मौजूद रहे हैं। फिर भी दशकों से जमा SQL code और tools की वजह से विकल्प टिक नहीं पाए। मुझे तो SQL की बदौलत दशकों तक job security और steady income मिली है, इसलिए बेहतर भाषा की ज़रूरत मानते हुए भी मैं इस स्थिति को कुछ सकारात्मक नज़र से देखता हूँhttps://en.wikipedia.org/wiki/Sargable
https://www.brentozar.com/blitzcache/non-sargable-predicates/
sargableशब्द वास्तव में किन communities में इस्तेमाल होता है। 20 साल से ज़्यादा SQL के साथ काम किया है, लेकिन manuals, Stack Overflow, या HN में इसे शायद ही कभी देखा है। क्या यह किसी खास DB, कंपनी, या open source community में ज़्यादा प्रचलित है?sargableकी उत्पत्ति खोजते समय यह StackOverflow जवाब मददगार लगा https://dba.stackexchange.com/a/217983sargableशब्दSearch ARGument ABLEका portmanteau हैCASE WHENके ज़रूरत से ज़्यादा इस्तेमाल की समस्या को UDF (User Defined Function) में logic को एक जगह समेटकर काफी हद तक हल किया जा सकता हैindexed column पर function लगाना इस बात का संकेत है कि query sargable नहीं है
DISTINCT के अति-उपयोग की जगह, join से निकले fanout में table grain के हिसाब से de-dupe करने के लिए जैसी तकनीक उपयोगी होती है। कुछ DBs में
QUALIFYका support होता है, जिससे query काफ़ी साफ़ हो जाती हैsargable की व्याख्या
Redshift में QUALIFY
sqliteमें तो मेरा यही मानना हैwhereclauses लिखने पड़ते, और void/return/cancel जैसी तमाम conditions हर जगह संभालनी पड़तीं। फिर कोई बदलाव आए तो दर्जनों views/procedures बदलने पड़ते। हमारे use case में nested views कहीं ज़्यादा व्यावहारिक हैंUPPER(name)column पर index जोड़ना) कम से कम MS SQL Server में सबसे अच्छा तरीका नहीं है। दूसरे DBs में support कैसा है, यह नहीं जानता, लेकिन बेहतर समाधान यह है कि सीधेCOLLATEके साथ case-insensitive computed column बनाया जाए (अपनी पसंद के अनुसार समायोजित करें)मैंने यह खुद झेलकर सीखा हैशायद SQL developers का motto ही है। जैसे भी हो, SQL लंबे समय से काफ़ी स्थिर रूप से विकसित हुआ है, इसलिए ऐसे traps पहले से जान लेना बहुत लंबे समय तक काम आता हैसबसे अहम चीज़ छूट गई है।