ऐसा करने से दर्द होता है, इसलिए ऐसा न करें।
Oracle में, कर्सर को प्रोग्रामिंग 101 के एक भाग के रूप में पढ़ाया जाता है। कई (यदि अधिकतर नहीं) मामलों में, कर्सर पहली चीज है जो Oracle डेवलपर सीखता है। प्रथम श्रेणी आमतौर पर शुरू होती है:"13 तार्किक संरचनाएं हैं, जिनमें से पहला लूप है, जो इस तरह जाता है..."
दूसरी ओर, PostgreSQL कर्सर पर बहुत अधिक निर्भर नहीं करता है। हाँ, वे मौजूद हैं। उनका उपयोग करने के तरीके के लिए कई वाक्यविन्यास स्वाद हैं। मैं इस आलेख श्रृंखला में किसी बिंदु पर सभी प्रमुख डिज़ाइनों को कवर करूंगा। लेकिन PostgreSQL कर्सर में पहला सबक यह है कि PostgreSQL में कर्सर का उपयोग करने के लिए कुछ (और बहुत बेहतर) एल्गोरिथम विकल्प हैं। वास्तव में, PostgreSQL के साथ 23 साल के करियर में, मुझे वास्तव में केवल दो बार कर्सर का उपयोग करने की आवश्यकता मिली है। और मुझे उनमें से एक के लिए खेद है।
कर्सर एक महंगी आदत है।
लूपिंग की तुलना में इटरेटिंग बेहतर है। "क्या अंतर है?", आप पूछ सकते हैं। खैर, अंतर ओ (एन) बनाम ओ (एन ^ 2) के बारे में है। ठीक है, मैं इसे फिर से अंग्रेज़ी में कहूँगा। कर्सर का उपयोग करने की जटिलता यह है कि वे लूप के लिए नेस्टेड के समान पैटर्न का उपयोग करके डेटा सेट के माध्यम से लूप करते हैं। प्रत्येक अतिरिक्त डेटा सेट घातांक द्वारा कुल की जटिलता को बढ़ाता है। ऐसा इसलिए है क्योंकि प्रत्येक अतिरिक्त डेटा सेट प्रभावी रूप से एक और अंतरतम लूप बनाता है। दो डेटा सेट ओ (एन ^ 2) हैं, तीन डेटा सेट ओ (एन ^ 3) हैं और इसी तरह। चुनने के लिए बेहतर एल्गोरिदम होने पर कर्सर का उपयोग करने की आदत डालना महंगा हो सकता है।
वे इसे बिना किसी अनुकूलन के करते हैं जो डेटाबेस के निचले स्तर के कार्यों के लिए उपलब्ध होगा। यही है, वे किसी भी महत्वपूर्ण तरीके से इंडेक्स का उपयोग नहीं कर सकते हैं, उप-चयनों में परिवर्तित नहीं हो सकते हैं, जुड़ने में खींच सकते हैं या समानांतर रीड का उपयोग नहीं कर सकते हैं। डेटाबेस के पास उपलब्ध भविष्य के किसी भी अनुकूलन से भी उन्हें कोई लाभ नहीं होगा। मुझे आशा है कि आप एक ग्रैंडमास्टर कोडर हैं जो हमेशा सही एल्गोरिदम प्राप्त करता है और इसे पूरी तरह से पहली बार कोड करता है, क्योंकि आपने रिलेशनल डेटाबेस के सबसे महत्वपूर्ण लाभों में से एक को हराया है। सर्वोत्तम प्रथाओं, या कम से कम किसी और के कोड पर भरोसा करके प्रदर्शन।
हर कोई आपसे बेहतर है। शायद व्यक्तिगत रूप से नहीं, लेकिन सामूहिक रूप से लगभग निश्चित रूप से। घोषणात्मक बनाम अनिवार्य तर्क के अलावा, अंतर्निहित फ़ंक्शन लाइब्रेरी से एक बार हटा दी गई भाषा में कोडिंग, बाकी सभी को आपके परामर्श के बिना आपके कोड को तेज़, बेहतर और अधिक कुशलता से चलाने का प्रयास करने की अनुमति देता है। और यह आपके लिए बहुत, बहुत अच्छा है।
आइए कुछ डेटा के साथ खेलते हैं।
हम अगले कुछ लेखों के साथ खेलने के लिए कुछ डेटा सेट करके शुरू करेंगे।
कर्सर की सामग्री.बैश:
set -o nounset # Treat unset variables as an error
# This script assumes that you have PostgreSQL running locally,
# that you have a database with the same name as the local user,
# and that you can create all this structure.
# If not, then:
# sudo -iu postgres createuser -s $USER
# createdb
# Clean up from the last run
[[ -f itisPostgreSql.zip ]] && rm itisPostgreSql.zip
subdirs=$(ls -1 itisPostgreSql* | grep : | sed -e 's/://')
for sub in ${subdirs[@]}
do
rm -rf $sub
done
# Get the newest file
wget https://www.itis.gov/downloads/itisPostgreSql.zip
# Unpack it
unzip itisPostgreSql.zip
# This makes a directory with the stupidest f-ing name possible
# itisPostgreSqlDDMMYY
subdir=$(\ls -1 itisPostgreSql* | grep : | sed -e 's/://')
# The script wants to create an "ITIS" database. Let's just make that a schema.
sed -i $subdir/ITIS.sql -e '/"ITIS"/d' # Cut the lines about making the db
sed -i $subdir/ITIS.sql -e '/-- PostgreSQL database dump/s/.*/CREATE SCHEMA IF NOT EXISTS itis;/'
sed -i $subdir/ITIS.sql -e '/SET search_path = public, pg_catalog;/s/.*/SET search_path TO itis;/'
# ok, we have a schema to put the data in, let's do the import.
# timeout if we can't connect, fail on error.
PG_TIMEOUT=5 psql -v "ON_ERROR_STOP=1" -f $subdir/ITIS.sql
यह हमें itis.hierarchy तालिका में खेलने के लिए 600K से थोड़ा अधिक रिकॉर्ड देता है, जिसमें प्राकृतिक दुनिया का वर्गीकरण होता है। हम इस डेटा का उपयोग जटिल डेटा इंटरैक्शन से निपटने के विभिन्न तरीकों का वर्णन करने के लिए करेंगे।
पहला विकल्प।
महंगे ऑपरेशन करते हुए रिकॉर्डसेट के साथ चलने के लिए मेरा गो-टू डिज़ाइन पैटर्न कॉमन टेबल एक्सप्रेशन (CTE) है।
यहां मूल रूप का एक उदाहरण दिया गया है:
WITH RECURSIVE fauna AS (
SELECT tsn, parent_tsn, tsn::text taxonomy
FROM itis.hierarchy
WHERE parent_tsn = 0
UNION ALL
SELECT h1.tsn, h1.parent_tsn, f.taxonomy || '.' || h1.tsn
FROM itis.hierarchy h1
JOIN fauna f
ON h1.parent_tsn = f.tsn
)
SELECT *
FROM fauna
ORDER BY taxonomy;
जो निम्नलिखित परिणाम उत्पन्न करता है:
┌─────────┬────────┬──────────────────────────────────────────────────────────┐
│ tsn │ parent │ taxonomy │
│ │ tsn │ │
├─────────┼────────┼──────────────────────────────────────────────────────────┤
│ 202422 │ 0 │202422 │
│ 846491 │ 202422 │202422.846491 │
│ 660046 │ 846491 │202422.846491.660046 │
│ 846497 │ 660046 │202422.846491.660046.846497 │
│ 846508 │ 846497 │202422.846491.660046.846497.846508 │
│ 846553 │ 846508 │202422.846491.660046.846497.846508.846553 │
│ 954935 │ 846553 │202422.846491.660046.846497.846508.846553.954935 │
│ 5549 │ 954935 │202422.846491.660046.846497.846508.846553.954935.5549 │
│ 5550 │ 5549 │202422.846491.660046.846497.846508.846553.954935.5549.5550│
│ 954936 │ 846553 │202422.846491.660046.846497.846508.846553.954936 │
│ 954904 │ 660046 │202422.846491.660046.954904 │
│ 846509 │ 954904 │202422.846491.660046.954904.846509 │
│ 11473 │ 846509 │202422.846491.660046.954904.846509.11473 │
│ 11474 │ 11473 │202422.846491.660046.954904.846509.11473.11474 │
│ 11475 │ 11474 │202422.846491.660046.954904.846509.11473.11474.11475 │
│ ... │ │...snip... │
└─────────┴────────┴──────────────────────────────────────────────────────────┘
(601187 rows)
यह क्वेरी किसी भी गणना को करने के लिए आसानी से संशोधित की जा सकती है। इसमें डेटा संवर्धन, जटिल कार्य या कुछ भी शामिल है जो आपका दिल चाहता है।
"लेकिन देखो!", आप कहते हैं। “यह कहता है RECURSIVE
वहीं नाम में! क्या यह ठीक वही नहीं कर रहा है जो आपने नहीं करने के लिए कहा था?" खैर, वास्तव में नहीं। हुड के तहत, यह "रिकर्सन" करने के लिए नेस्टेड अर्थ या लूपिंग में रिकर्सन का उपयोग नहीं करता है। यह तालिका का केवल एक रैखिक पठन है जब तक कि अधीनस्थ क्वेरी किसी भी नए परिणाम को वापस करने में विफल न हो। और यह इंडेक्स के साथ भी काम करता है।
आइए निष्पादन योजना देखें:
┌──────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ QUERY PLAN │
├──────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ Sort (cost=211750.51..211840.16 rows=35858 width=40) │
│ Output: fauna.tsn, fauna.parent_tsn, fauna.taxonomy │
│ Sort Key: fauna.taxonomy │
│ CTE fauna │
│ -> Recursive Union (cost=1000.00..208320.69 rows=35858 width=40) │
│ -> Gather (cost=1000.00..15045.02 rows=18 width=40) │
│ Output: hierarchy.tsn, hierarchy.parent_tsn, ((hierarchy.tsn)::text) │
│ Workers Planned: 2 │
│ -> Parallel Seq Scan on itis.hierarchy (cost=0.00..14043.22 rows=8 width=40) │
│ Output: hierarchy.tsn, hierarchy.parent_tsn, (hierarchy.tsn)::text │
│ Filter: (hierarchy.parent_tsn = 0) │
│ -> Hash Join (cost=5.85..19255.85 rows=3584 width=40) │
│ Output: h1.tsn, h1.parent_tsn, ((f.taxonomy || '.'::text) || (h1.tsn)::text) │
│ Hash Cond: (h1.parent_tsn = f.tsn) │
│ -> Seq Scan on itis.hierarchy h1 (cost=0.00..16923.87 rows=601187 width=8) │
│ Output: h1.hierarchy_string, h1.tsn, h1.parent_tsn, h1.level, h1.childrencount │
│ -> Hash (cost=3.60..3.60 rows=180 width=36) │
│ Output: f.taxonomy, f.tsn │
│ -> WorkTable Scan on fauna f (cost=0.00..3.60 rows=180 width=36) │
│ Output: f.taxonomy, f.tsn │
│ -> CTE Scan on fauna (cost=0.00..717.16 rows=35858 width=40) │
│ Output: fauna.tsn, fauna.parent_tsn, fauna.taxonomy │
│ JIT: │
│ Functions: 13 │
│ Options: Inlining false, Optimization false, Expressions true, Deforming true │
└──────────────────────────────────────────────────────────────────────────────────────────────────────┘
आइए आगे बढ़ते हैं और एक इंडेक्स बनाते हैं, और देखते हैं कि यह कैसे काम करता है।
CREATE UNIQUE INDEX taxonomy_parents ON itis.hierarchy (parent_tsn, tsn);
┌─────────────────────────────────────────────────────────────────────────────┐
│ QUERY PLAN │
├─────────────────────────────────────────────────────────────────────────────┤
│Sort (cost=135148.13..135237.77 rows=35858 width=40) │
│ Output: fauna.tsn, fauna.parent_tsn, fauna.taxonomy │
│ Sort Key: fauna.taxonomy │
│ CTE fauna │
│ -> Recursive Union (cost=4.56..131718.31 rows=35858 width=40) │
│ -> Bitmap Heap Scan on itis.hierarchy (cost=4.56..74.69 rows=18) │
│ Output: hierarchy.tsn, hierarchy.parent_tsn, (hierarchy.tsn) │
│ Recheck Cond: (hierarchy.parent_tsn = 0) │
│ -> Bitmap Index Scan on taxonomy_parents │
│ (cost=0.00..4.56 rows=18) │
│ Index Cond: (hierarchy.parent_tsn = 0) │
│ -> Nested Loop (cost=0.42..13092.65 rows=3584 width=40) │
│ Output: h1.tsn, h1.parent_tsn,((f.taxonomy || '.')||(h1.tsn))│
│ -> WorkTable Scan on fauna f (cost=0.00..3.60 rows=180) │
│ Output: f.tsn, f.parent_tsn, f.taxonomy │
│ -> Index Only Scan using taxonomy_parents on itis.hierarchy │
│ h1 (cost=0.42..72.32 rows=20 width=8) │
│ Output: h1.parent_tsn, h1.tsn │
│ Index Cond: (h1.parent_tsn = f.tsn) │
│ -> CTE Scan on fauna (cost=0.00..717.16 rows=35858 width=40) │
│ Output: fauna.tsn, fauna.parent_tsn, fauna.taxonomy │
│JIT: │
│ Functions: 6 │
└─────────────────────────────────────────────────────────────────────────────┘
अच्छा, यह संतोषजनक था, है ना? और एक ही काम करने के लिए एक कर्सर के साथ संयोजन में एक इंडेक्स बनाना निषेधात्मक रूप से कठिन होता। यह संरचना हमें काफी जटिल वृक्ष संरचना पर चलने और साधारण लुकअप के लिए इसका उपयोग करने में सक्षम बनाती है।
अगली किस्त में, हम उसी परिणाम को और भी तेज़ी से प्राप्त करने की एक और विधि के बारे में बात करेंगे। हमारे अगले लेख के लिए, हम विस्तार ltree के बारे में बात करेंगे, और पदानुक्रमित डेटा को आश्चर्यजनक रूप से कैसे देखें। बने रहें।