SQL टिप्स और ट्रिक्स
(github.com/ben-n93)- डेटा विश्लेषण के काम में अक्सर इस्तेमाल होने वाली SQL लिखने की आदतों और query patterns की सूची है, इस आधार पर कि ये सभी RDBMS पर समान रूप से लागू नहीं भी हो सकते
- readability के लिहाज़ से leading commas,
WHERE 1=1, indentation, CTE, comments,USINGके ज़रिए queries को पढ़ने और modify करने में आसान बनाने के तरीकों की सिफारिश की गई है - डेटा processing में anti-join,
QUALIFY,GROUP BY ROLLUP,EXCEPTजैसे constructs को practical examples के रूप में लिया गया है, जिनका इस्तेमाल results filter करने, totals बनाने और tables के differences check करने में होता है - performance और correctness के लिहाज़ से
NULLके साथ मिला हुआNOT IN, implicit type conversion, और calculated field alias collisions query results या speed को प्रभावित कर सकते हैं - complex queries में execution order, documentation check करना, columns का source स्पष्ट करना, और saved queries के नाम जैसे basic habits debugging और reusability बढ़ाने में महत्वपूर्ण हैं
SQL लिखने की readability बढ़ाने वाली आदतें
- यह repository कई वर्षों में सीखे गए SQL tips and tricks की curated list है, जिसमें data analyst के रोज़मर्रा के काम में उपयोगी बातें और वे चीज़ें शामिल हैं जो पहली बार SQL लिखते समय पता होतीं तो अच्छा रहता
- कुछ tips सभी RDBMS के लिए उपयुक्त नहीं हो सकते
-
Leading comma और leading
ANDSELECTclause में fields अलग करने के लिए trailing comma की बजाय leading comma इस्तेमाल करने की सलाह दी गई है- इससे यह ज़्यादा साफ़ दिखता है कि कोई नया column है या line-broken code
- line length अलग-अलग होने पर भी comma missing है या नहीं, यह ढूंढना आसान होता है
- इसी वजह से
WHEREclause की conditions के पहले भी leadingANDरखा जा सकता है
-
WHERE 1=1से conditions test करना आसान बनानाWHEREclause में dummy condition1=1डालने से testing के दौरान conditions को comment out करने पर भी query टूटती नहीं- सभी conditions comment out करने पर भी
1=1बचा रहता है, इसलिए query चलती रह सकती है
-
Indentation और formatter
-
Complex queries में CTE पर विचार करें
- inline view को 2–3 levels या उससे अधिक nest करने पर कुछ हफ्तों बाद query को फिर से देखने पर समझना मुश्किल हो सकता है
- CTE को लंबी queries को अधिक व्यवस्थित बनाने, reusability और debugging में मदद करने के तरीके के रूप में पेश किया गया है
-
Comments “क्यों” समझाएं
- समय बीतने के बाद यह याद रखना मुश्किल हो सकता है कि कोई खास processing क्यों की गई थी
- comments आम तौर पर code “कैसे” काम करता है, इसकी बजाय क्यों ऐसा किया गया है, यह समझाने के लिए बेहतर होते हैं
- example में नए CMS के archive video format handle न कर पाने के कारण archive content exclude करने वाली condition पर comment जोड़ा गया है
-
समान नाम वाले columns पर join के लिए
USING- जब दो tables में समान नाम के column से join करना हो, तो
USINGका इस्तेमालONकी तुलना में join को सरल तरीके से व्यक्त कर सकता है USINGcommon column को result में deduplicate करके केवल एक बार return करता हैONइस्तेमाल करते समय common column explicitly न बताने परambiguous column nameerror आ सकता है
- जब दो tables में समान नाम के column से join करना हो, तो
डेटा processing के लिए उपयोगी constructs
-
anti-join से दूसरे table में न मौजूद rows ढूंढना
- anti-join का इस्तेमाल उन rows को return करने के लिए होता है जो एक table में हैं लेकिन दूसरे table में match नहीं होतीं
- example में unarchived content के सिर्फ
video_idलाने की स्थिति ली गई है - इसे implement करने के कई तरीके हैं
LEFT JOINके बाद matching table की keyNULLवाली rows ही filter करनाNOT INऔर subquery का इस्तेमालNOT EXISTSऔर correlated subquery का इस्तेमालNOT INNULLvalues के कारण expected तरीके से काम नहीं कर सकता, इसलिए इसका उपयोग recommend नहीं किया गया है
-
QUALIFYसे window function results filter करनाQUALIFYwindow function के results के आधार पर query results filter करने देता है- inline view के बिना filtering कर पाने से code की lines कम हो सकती हैं
- example में product-wise top 10 markets को
DENSE_RANK()से चुनकरQUALIFYसे filter किया गया है QUALIFYके साथ यह limitation है कि यह Snowflake, Amazon Redshift, Google BigQuery जैसे बड़े data warehouses में ही available दिखता है
-
Column position आधारित
GROUP BYऔरORDER BY- column names की बजाय column position से
GROUP BY 1,ORDER BY 2जैसे लिख सकते हैं - temporary या one-off queries के लिए यह useful हो सकता है
- production code में हमेशा column names को directly refer करने की सिफारिश की गई है
- column names की बजाय column position से
-
GROUP BY ROLLUPसे totals बनानाGROUP BY ROLLUPका इस्तेमाल subtotals और grand totals बनाने के लिए किया जा सकता है- example में department-wise salary total निकालते हुए overall salary total row भी बनाई गई है
- Transact-SQL documentation बताती है कि
ROLLUPcolumn expression combinations के हिसाब से groups बनाता है और right to left groups की संख्या घटाते हुए subtotals और grand totals बनाता है COALESCEapply करने पर total row कोTotalजैसे label से दिखाया जा सकता है- total row result के bottom में आए, इसके लिए sorting column पर ध्यान देना चाहिए
-
EXCEPTसे दो result sets का difference ढूंढनाEXCEPTपहली query के result में मौजूद लेकिन दूसरी query के result में न मौजूद rows return करता हैEXCEPTऔरUNION ALLको साथ इस्तेमाल करके verify किया जा सकता है कि दो tables में same data है या नहीं- अगर कोई row return नहीं होती, तो दोनों tables identical हैं
- अगर rows return होती हैं, तो वही rows difference की वजह हैं
Performance और correctness को नुकसान पहुंचाने वाले patterns
-
NULLallow करने वाले columns मेंNOT EXISTS,NOT INसे बेहतर है- अगर comparison target column
NULLallow करता है, तोNOT INआम तौर परNOT EXISTSसे धीमा हो सकता है - Snowflake में यह behavior देखा गया, और PostgreSQL Wiki के Don’t Do This में लिखा है कि
NOT IN (SELECT ...)अच्छी तरह optimize नहीं होता - अगर comparison target values में
NULLहो, तोNOT INexpected तरीके से काम नहीं करता - column
NULLallow करता है, इसका मतलब यह नहीं कि actualNULLvalues मौजूद हैं, लेकिन ऐसी tables से काम करते समय जिन्हें modify नहीं कर सकते,NOT EXISTSspeed improvement में मदद कर सकता है
- अगर comparison target column
-
Implicit type conversion धीमा कर सकता है या fail हो सकता है
- column से अलग data type की value condition में डालने पर database implicit type conversion की कोशिश कर सकता है
- example में string type
video_idcolumn की integer200050से तुलना की स्थिति ली गई है - implicit type conversion पर निर्भर रहने से problems हो सकती हैं
- अगर कोई value convert नहीं हो सकती तो error आ सकता है
- हर value को specified type में convert करने के extra work के कारण query धीमी हो सकती है
- column के समान data type का इस्तेमाल करें, या errors से बचने के लिए Snowflake के
TRY_TO_NUMBERजैसे functions इस्तेमाल किए जा सकते हैं - speed पर impact processed dataset के size पर निर्भर करता है
आम गलतियां
-
NOT INऔरNULL- comparison target values में
NULLहो तोNOT INकाम नहीं करता NULLUnknown को दर्शाता है, इसलिए SQL engine verify नहीं कर सकता कि checked value list में नहीं है- इस case में
NOT EXISTSइस्तेमाल करना alternative है
- comparison target values में
-
Calculated field alias collision
- calculated field का नाम मौजूदा column जैसा रखने पर unexpected behavior हो सकता है
- Snowflake की
GROUP BYdocumentation कहती है कि अगरGROUP BYclause में दिया गया नाम column name और alias दोनों से match करता है, तो column name इस्तेमाल होता है - example में
LEFT(product, 1) AS productसे alias बनाकरGROUP BY productइस्तेमाल करने पर first letter की बजाय originalproductcolumn से grouping होती है और 3 rows return होती हैं - समाधान दो हैं
product_letterजैसा unique alias इस्तेमाल करेंGROUP BY LEFT(product, 1)जैसे expression explicitly लिखें- window functions में भी alias problem हो सकती है
- example में
CASEसेRobotका revenue 0 किया जाता है, लेकिन यह window function run होने के बाद apply होता है, जिससे ranking expected से अलग आती है - जहां संभव हो unique alias इस्तेमाल करें, या window function के
ORDER BYके अंदर calculation expression directly डालने की जरूरत होती है
-
Column किस table से है, यह स्पष्ट करें
- कई joins वाली complex query में value issue को source table तक trace कर पाना चाहिए
- जब दो tables समान column names share करती हैं और column source explicitly नहीं बताया जाता, तो RDBMS error दे सकता है
- example में
vc.video_id,metadata.seasonजैसे table alias लगाकर column source स्पष्ट किया गया है
Execution order, documentation, saved names
-
SQL execution order समझना
- SQL सीखने वालों के लिए सबसे महत्वपूर्ण सलाहों में से एक clauses के execution order को समझना माना गया है
- execution order जानने से query लिखने का तरीका काफी बदल सकता है
- reference के रूप में A beginner’s guide to the true order of SQL operations दिया गया है
-
Documentation अंत तक पढ़ें
- Snowflake में कई date columns में से latest date return करने के लिए
GREATEST()इस्तेमाल करने का case है GREATEST()arguments में से कोई एकNULLहो तोNULLreturn करता है- documentation आगे पढ़ी होती तो
COALESCE(GREATEST(...), ...)की बजायGREATEST_IGNORE_NULLS()इस्तेमाल किया जा सकता था - कई cases में documentation skim करने में 1 minute से कम ही लगता है, और यह expected से अलग behavior की वजह ढूंढने की मेहनत कम कर सकता है
- Snowflake में कई date columns में से latest date return करने के लिए
-
Saved queries के लिए descriptive names इस्तेमाल करें
- ऐसी query जिसे फिर से run या refer करना पड़े, उसे ढूंढ न पाने की स्थिति से बचने के लिए descriptive name से save करना बेहतर है
- saved name में आम तौर पर query topic, run month, और requester name होता है
- example format है
Lapsed users analysis - 2023-09-01 - Olivia Roberts
2 टिप्पणियां
लीडिंग comma इस पोस्ट में हर जगह trailing comma के रूप में लिखे गए हैं। मूल लेख में वे लीडिंग रूप में दर्ज हैं।
Hacker News की राय
मेरी तरफ़ से जोड़ने वाली टिप यह है: DB सर्वर को ठीक से समझें और execution plan बार-बार देखें. अप्रत्याशित नतीजे आ सकते हैं, इसलिए ट्यून करें और फिर से जांचें
आम तौर पर
EXISTS,INसे तेज़ होता है, औरNOT EXISTS,NULLhandling मेंEXCEPTसे अलग तरीके से काम करता है. टेबल join करने के बादDISTINCTवगैरह से rows filter करने की बजाय,SELECTlist में subquery columns इस्तेमाल करना कई बार कहीं ज़्यादा तेज़ होता है. एक ही टेबल से 10 से ज़्यादा values लानी हों तब भी, और DB सर्वर lateral join support करता हो तब भी ऐसा हो सकता है. बस subquery को अधिकतम एक ही row लौटानी चाहिएजो queries one-off नहीं हैं, उन्हें पूरी table scan नहीं करनी चाहिए. आज की table scan कल की outage बन सकती है, इसलिए index जोड़ना चाहिए. यह भी याद रखें कि
GROUP BYclause आम तौर पर index usage तय करता हैअगर expression से filtering करनी हो—मसलन यह जांचना कि कोई substring किसी खास value के बराबर है या नहीं—तो computed column जोड़कर उस पर index लगाया जा सकता है. कुछ DB सीधे expression indexes support करते हैं.
ORकी जगहUNION ALLइस्तेमाल करने से complex queries या कईORconditions में अक्सर बहुत तेजी मिल सकती हैजब DB filtering order समझदारी से तय नहीं कर पाता, तो subquery को
JOINकरके order force करना भी उपयोगी होता हैPostgres में जो दिलचस्प लगा—और संभव है दूसरे DB में भी ऐसा हो—वह यह कि
INSERT (SELECT ...)operation को CPU cores की संख्या के हिसाब से manually shard करने पर यह लगभग linearly तेज़ हो सकता है. करीब 10 joins हों तब भी यह संभव था. पहलेEXPLAINदेखें, सबसे inner या outer join खोजें, फिर हर row range के लिए अलग parallel query (id >= start AND id < end) चलाएं. 6 साल पहले एक काम में अजीब वजहों से मैंने यह तरीका बहुत इस्तेमाल किया था. Postgres 10+ में parallelism जोड़ा गया है, लेकिन मेरी जानकारी में वह अभी इतना advanced नहीं हैSELECTlist में subquery “column” इस्तेमाल करने का ठीक-ठीक मतलब मुझे समझ नहीं आयाउदाहरण के लिए
SELECT column1, (SELECT column2, column3, ... FROM table_b WHERE table_a.id = table_b.a_id) FROM table_aचलाने पर उम्मीद के मुताबिक “subquery must return only one column” आता है. क्या मतलब कई columns को record/composite type के रूप में लौटाने से है?GROUP BYclause आम तौर पर index usage तय करता है, इसकी वजह तुरंत समझ में नहीं आई; जिन्हें जिज्ञासा हो, उनके लिए यह लेख step-by-step अच्छी तरह समझाता है: https://www.brentozar.com/archive/2015/06/indexing-for-group...EXPLAINइस्तेमाल करें, और अपने पसंदीदा tool से उसे समझना सीखें. queries को monitor भी करना चाहिएपिछले startup में हमने PgHero install किया था, और performance optimization तथा priority तय करने में वह सचमुच बहुत मददगार रहा
कुछ queries में full table scan ही सबसे efficient access strategy होती है. आम तौर पर पूरी table पढ़ने वाली analytics/aggregation queries ऐसी होती हैं, और कभी-कभी पूरी rows के सिर्फ 50% लाने पर भी table scan बेहतर होता है.
read-only table scan outage में कैसे बदलता है, यह भी मुझे साफ नहीं है. क्योंकि यह concurrent access को block नहीं करता. नुकसान बस इतना है कि I/O load बढ़ता है; अगर server इतना भी नहीं संभाल सकता तो वह शुरू से ही गंभीर रूप से under-provisioned है
“readability” section के 3 examples अजीब हैं. पहले 2 तो लिखना आसान बनाने के लिए सचमुच readability की बलि दे रहे हैं, और आखिरी वाला indentation से भी मुश्किल से बचने वाला, पढ़ने में कठिन monster जैसा है
मुझे लगता है developers commit history भी लगभग उतनी ही देखते हैं जितना actual source code
इतना अक्सर देखा है कि अब खास चुभता नहीं
कोई problem भी नहीं दिखती
और कुछ गलत भी नहीं लगता
SELECTblock में columns को अलग-अलग lines में बांटते हुए भी 150-character की line जस की तस छोड़ने वाला आखिर कौन है? यह readability की टूटी हुई definition है. comma वाली बात तो शुरू भी नहीं कर सकताcode review में लंबी lines को कोई ठीक से नहीं देखता. AngularJS की सबसे बड़ी problem यही थी. merge गलत handle हुआ और सब टूट गया, क्योंकि 90वें column के आसपास आंखें धुंधली होने लगती हैं. code review वाली आधा dozen से ज़्यादा teams में रहा हूं, और हर जगह यही देखा. इस problem को बहुत अच्छी तरह जानते हुए और बचने की कोशिश करते हुए भी, मैं खुद भी दूसरों की करीब आधी frequency से अब भी गलती कर देता हूं
थोड़ा तोड़कर लिखें. खासकर जब दूसरों को example दिखा रहे हों, तब तो और भी ज़रूरी है
जटिल stored procedures से निपटने के लिए सुझाव ये हैं
WHEREclause चलने की उम्मीद कर सकते हैं.JOINया complex operations की कोशिश करेंगे तो timeout होने की संभावना बड़ी हैअगर #6 के बिना #5 किया, तो हो सकता है आपको दिखे ही नहीं कि आप suboptimal काम कर रहे हैं. मेरी सलाह है premature optimization से बचें, पहले सबसे intuitive तरीके से लिखें और सिर्फ ज़रूरत पड़ने पर optimize करें. सबसे अहम बात यह है कि SQL को procedural तरीके से न लिखें. आप जो data चाहते हैं उसे describe कर रहे हैं, engine को यह command नहीं दे रहे कि उसे कैसे लाना है
दूसरी ओर Microsoft लगातार warning लगाता रहता है कि इसे tune करने की कोशिश न करें, मानो query planner ही सबसे बेहतर जानता हो
DB version भी असर डाल सकता है
मुझे “बस शायद जरूरत पड़े” वाली development पसंद नहीं. interfaces के साथ भी यही है, और
where 1=1जैसे placeholders के साथ भीजब जरूरत हो तब करें. सिर्फ इसलिए न करें कि भविष्य में कभी जरूरत पड़ सकती है. production code development helpers छोड़ने की जगह नहीं है. development के दौरान जो चाहें करें, लेकिन production code में readability और clear intent कहीं ज़्यादा अहम हैं
“anti join” पर एक बात और. अगर आपको सिर्फ यह check करना है कि किसी दूसरी बड़ी table या subquery में condition से matching rows मौजूद हैं या नहीं, तो
INयाLEFT JOINकी बजाय EXISTS इस्तेमाल करना बेहतर हैEXISTSmatching item मिलते ही true return कर देता है.LEFT JOINऔरINके मामले में engine evaluation से पहले सारे results collect करता है(NOT) EXISTSने(LEFT) JOINया(NOT) INसे बेहतर execution plan बनाया या वही plan बनायाऊपर से intent भी ज्यादा clear है
“code में comments डालें” के संबंध में, कम-से-कम MSSQL में comments के लिए
--की बजाय/**/इस्तेमाल करने की अक्सर सलाह दी जाती है. ऐसा इसलिए क्योंकि Query Store जैसी features अक्सर queries को line breaks के बिना store करती हैं, और वहाँ से query निकालने पर IDE formatter तुरंत इस्तेमाल करने के बजाय सब कुछ manually ठीक करना पड़ता हैOBJECT_DEFINITIONमें यह इस्तेमाल करता हूँselect name,cast((select OBJECT_DEFINITION(object_id) for xml path('')) as xml) from sys.proceduresline breaks preserve होते हैं, इसलिए cleanup शायद आसान हो. हालांकि दूसरे XML characters टूट जाते हैं, जैसे
>का>में बदलना. एक और optionVARBINARYऔर उसे वापस unpack करने वाली कोई चीज़ इस्तेमाल करना हैहर कोई comma suggestion पर इतना हंगामा कर रहा है, लेकिन क्या
WHEREclause में1=1को अच्छा idea माना जा रहा है? code review में यह दिखे तो मुझे समझ नहीं आता कि लेखक के बारे में क्या सोचूँWHEREstatement में बदलाव दूसरी lines को affect नहीं करते, इसलिए code review आसान हो जाता हैलेकिन अगर वजह dynamic conditions जोड़ना है, जैसा इस case में है, तो जहाँ मैं काम करता हूँ वहाँ तो निश्चित रूप से नौकरी चली जाएगी
क्या कोई सामान्य guideline शेयर कर सकता है कि DB settings से speed बढ़ाने—जो लगभग “खरीदने” जैसा तरीका है—और असल में manually implement करने वाले “बनाने” के बीच लाइन कहाँ खींचनी चाहिए? मेरे सीमित अनुभव में, काबिल DBA कहीं और काम करते हैं और उन्हें काफी ज्यादा pay मिलता है, इसलिए यह काम अक्सर app developer पर आ गिरता है। जैसा ऊपर कहा गया, DB समझना जरूरी है
एक typical उदाहरण वह data है जो समय के साथ bulk में जमा होता जाता है और जिसमें latest data सबसे ज्यादा access होता है। DBA partitioning या partial index से access तेज रख सकता है, लेकिन app developer records को background में अलग archive table में move करते हुए पूरे dataset पर eventual search जैसी functionality भी support कर सकता है। ऐसा भी लगता है कि सही समय पर एक table को कई tables में बाँटने का शुरुआती काम tools काफी हद तक automate कर सकते हैं, जैसे cloud DB की feature कमी के कारण limitations हों
एक और management choice यह है कि बड़े blobs/files को किसी अलग database में, या file system में store किया जाए ताकि अलग storage settings इस्तेमाल हो सकें। इसे भी DB support कर सकता है और manually भी handle किया जा सकता है
extreme case में बात index को खुद implement करने तक जा सकती है। एक huge table हो जिसमें auto-increment primary key और ढेरों columns हों, और अलग से एक table बनाया जाए जिसमें वह ID और कुछ searchable columns हों। full-text search या vector तक भी जा सकते हैं
MSSQL 2016+ में materialized view pattern को manually implement करते समय उपयोगी tip है कि partition switching भी साथ में इस्तेमाल करें। https://github.com/cajuncoding/SqlBulkHelpers?tab=readme-ov-... में इसे अच्छी तरह explain और implement किया गया है। यह संयोग से मिली एक छोटी library थी, जो commercially सबसे उपयोगी निकली, लेकिन search rankings में पीछे थी और stars कम थे; इसका focus .NET से MSSQL में bulk insert पर है। partition switching automation के जरिए buy/build की लाइन सही तरह खींचने का यह अच्छा उदाहरण लगता है
छूटी हुई बात:
SELECT *का इस्तेमाल बंद करना चाहिए। लगभग तय है कि आपको table की पूरी width की जरूरत नहीं है, और ऐसा करने से filter और transfer होने वाला data बढ़ता है, साथ ही semi join जैसी शानदार capability भी block हो जाती हैअगर आप developer हैं, तो सही है।
SELECT *में pitfalls हैं, और लगभग हमेशा columns explicit करने चाहिए या ऐसा करने वाला query builder इस्तेमाल करना चाहिएलेकिन अगर आप analyst हैं, तो जिंदगी छोटी है, और कभी-कभी आप सभी columns type नहीं करना चाहेंगे।
SELECT *भी ठीक हैथोड़ा off-topic हो सकता है, लेकिन क्या maintainer का pull request को बिना किसी comment या discussion के बस close कर देना acceptable तरीका है?
यह मैं उस व्यक्ति के तौर पर पूछ रहा हूँ जिसने repository में कभी-कभी contribute किया है या करने की कोशिश की है
उदाहरण: https://github.com/ben-n93/SQL-tips-and-tricks/pulls?q=is%3A...