और अब हम Oracle से PostgreSQL श्रृंखला में अपने प्रवास के दूसरे लेख पर पहुँचते हैं। इस बार हम START WITH/CONNECT BY
पर एक नज़र डालेंगे निर्माण।
Oracle में, START WITH/CONNECT BY
किसी दिए गए सेंटीनेल पंक्ति से शुरू होने वाली एकल लिंक्ड सूची संरचना बनाने के लिए उपयोग किया जाता है। लिंक की गई सूची एक पेड़ का रूप ले सकती है, और इसमें संतुलन की कोई आवश्यकता नहीं है।
उदाहरण के लिए, आइए एक क्वेरी से शुरू करें, और मान लें कि तालिका में 5 पंक्तियाँ हैं।
SELECT * FROM person;
last_name | first_name | id | parent_id
------------+------------+----+-----------
Dunstan | Andrew | 1 | (null)
Roybal | Kirk | 2 | 1
Riggs | Simon | 3 | 1
Eisentraut | Peter | 4 | 1
Thomas | Shaun | 5 | 3
(5 rows)
यहाँ Oracle सिंटैक्स का उपयोग करके तालिका की श्रेणीबद्ध क्वेरी है।
select id, parent_id
from person
start with parent_id IS NULL
connect by prior id = parent_id;
id | parent_id
----+-----------
1 | (null)
4 | 1
3 | 1
2 | 1
5 | 3
और यहाँ यह फिर से PostgreSQL का उपयोग कर रहा है।
WITH RECURSIVE a AS (
SELECT id, parent_id
FROM person
WHERE parent_id IS NULL
UNION ALL
SELECT d.id, d.parent_id
FROM person d
JOIN a ON a.id = d.parent_id )
SELECT id, parent_id FROM a;
id | parent_id
----+-----------
1 | (null)
4 | 1
3 | 1
2 | 1
5 | 3
(5 rows)
यह क्वेरी बहुत सारी PostgreSQL सुविधाओं का उपयोग करती है, तो चलिए इसे धीरे-धीरे समझते हैं।
WITH RECURSIVE
यह एक "कॉमन टेबल एक्सप्रेशन" (CTE) है। यह प्रश्नों के एक सेट को परिभाषित करता है जिसे एक ही कथन में निष्पादित किया जाएगा, न कि केवल उसी लेनदेन में। आपके पास कितनी भी संख्या में मूलक व्यंजक हो सकते हैं, और एक अंतिम कथन हो सकता है। इस उपयोग के लिए, हमें केवल एक की आवश्यकता है। उस कथन को RECURSIVE
. घोषित करके , यह पुनरावृत्त रूप से तब तक निष्पादित होगा जब तक कोई और पंक्तियाँ वापस नहीं आतीं।
SELECT
UNION ALL
SELECT
यह पुनरावर्ती क्वेरी के लिए एक निर्धारित वाक्यांश है। इसे दस्तावेज़ीकरण में शुरुआती बिंदु और रिकर्सन एल्गोरिदम को अलग करने की विधि के रूप में परिभाषित किया गया है। Oracle के शब्दों में, आप उन्हें START WITH क्लॉज़ के रूप में सोच सकते हैं जो CONNECT BY क्लॉज़ से जुड़ा हुआ है।
JOIN a ON a.id = d.parent_id
यह सीटीई स्टेटमेंट में सेल्फ-जॉइन है जो बाद की पुनरावृत्ति के लिए पिछली पंक्ति डेटा प्रदान करता है।
यह कैसे काम करता है यह स्पष्ट करने के लिए, आइए क्वेरी में एक पुनरावृत्ति संकेतक जोड़ें।
WITH RECURSIVE a AS (
SELECT id, parent_id, 1::integer recursion_level
FROM person
WHERE parent_id IS NULL
UNION ALL
SELECT d.id, d.parent_id, a.recursion_level +1
FROM person d
JOIN a ON a.id = d.parent_id )
SELECT * FROM a;
id | parent_id | recursion_level
----+-----------+-----------------
1 | (null) | 1
4 | 1 | 2
3 | 1 | 2
2 | 1 | 2
5 | 3 | 3
(5 rows)
हम रिकर्सन लेवल इंडिकेटर को एक वैल्यू के साथ इनिशियलाइज़ करते हैं। ध्यान दें कि लौटाई गई पंक्तियों में, पहला रिकर्सन स्तर केवल एक बार होता है। ऐसा इसलिए है क्योंकि पहला क्लॉज केवल एक बार निष्पादित होता है।
दूसरा खंड वह है जहां पुनरावृत्त जादू होता है। यहां, हमारे पास वर्तमान पंक्ति डेटा के साथ-साथ पिछली पंक्ति डेटा की दृश्यता है। यह हमें पुनरावर्ती गणना करने की अनुमति देता है।
ग्राफ़ डेटाबेस डिज़ाइन के लिए इस सुविधा का उपयोग करने के तरीके के बारे में साइमन रिग्स के पास एक बहुत अच्छा वीडियो है। यह अत्यधिक जानकारीपूर्ण है, और आपको इसे देखना चाहिए।
आपने देखा होगा कि यह प्रश्न एक गोलाकार स्थिति का कारण बन सकता है। यह सही है। इस अंतहीन रिकर्सन को रोकने के लिए दूसरी क्वेरी में सीमित खंड जोड़ने के लिए डेवलपर पर निर्भर है। उदाहरण के लिए, केवल हार मानने से पहले केवल 4 स्तरों को गहराई से रिकर्स करना।
WITH RECURSIVE a AS (
SELECT id, parent_id, 1::integer recursion_level --<-- initialize it here
FROM person
WHERE parent_id IS NULL
UNION ALL
SELECT d.id, d.parent_id, a.recursion_level +1 --<-- iteration increment
FROM person d
JOIN a ON a.id = d.parent_id
WHERE d.recursion_level <= 4 --<-- bail out here
) SELECT * FROM a;
कॉलम नाम और डेटा प्रकार पहले खंड द्वारा निर्धारित किए जाते हैं। ध्यान दें कि उदाहरण रिकर्सन स्तर के लिए कास्टिंग ऑपरेटर का उपयोग करता है। बहुत गहरे ग्राफ़ में, इस डेटा प्रकार को 1::bigint recursion_level
के रूप में भी परिभाषित किया जा सकता है ।
यह ग्राफ़ एक छोटी शेल स्क्रिप्ट और ग्राफ़विज़ उपयोगिता के साथ कल्पना करना बहुत आसान है।
#!/bin/bash -
#===============================================================================
#
# FILE: pggraph
#
# USAGE: ./pggraph
#
# DESCRIPTION:
#
# OPTIONS: ---
# REQUIREMENTS: ---
# BUGS: ---
# NOTES: ---
# AUTHOR: Kirk Roybal (), [email protected]
# ORGANIZATION:
# CREATED: 04/21/2020 14:09
# REVISION: ---
#===============================================================================
set -o nounset # Treat unset variables as an error
dbhost=localhost
dbport=5432
dbuser=$USER
dbname=$USER
ScriptVersion="1.0"
output=$(basename $0).dot
#=== FUNCTION ================================================================
# NAME: usage
# DESCRIPTION: Display usage information.
#===============================================================================
function usage ()
{
cat <<- EOT
Usage : ${0##/*/} [options] [--]
Options:
-h|host name Database Host Name default:localhost
-n|name name Database Name default:$USER
-o|output file Output file default:$output.dot
-p|port number TCP/IP port default:5432
-u|user name User name default:$USER
-v|version Display script version
EOT
} # ---------- end of function usage ----------
#-----------------------------------------------------------------------
# Handle command line arguments
#-----------------------------------------------------------------------
while getopts ":dh:n:o:p:u:v" opt
do
case $opt in
d|debug ) set -x ;;
h|host ) dbhost="$OPTARG" ;;
n|name ) dbname="$OPTARG" ;;
o|output ) output="$OPTARG" ;;
p|port ) dbport=$OPTARG ;;
u|user ) dbuser=$OPTARG ;;
v|version ) echo "$0 -- Version $ScriptVersion"; exit 0 ;;
\? ) echo -e "\n Option does not exist : $OPTARG\n"
usage; exit 1 ;;
esac # --- end of case ---
done
shift $(($OPTIND-1))
[[ -f "$output" ]] && rm "$output"
tee "$output" <<eof< span="">
digraph g {
node [shape=rectangle]
rankdir=LR
EOF
psql -h $dbhost -U $dbuser -d $dbname -p $dbport -qtAf cte.sql |
sed -e 's/^/node/' -e 's/.*(null)|/node/' -e 's/^/\t/' -e 's/|[[:digit:]]*$//' |
sed -e 's/|/ -> node/' | tee -a "$output"
tee -a "$output" <<eof< span="">
}
EOF
dot -Tpng "$output" > "${output/dot/png}"
[[ -f "$output" ]] && rm "$output"
open "${output/dot/png}"</eof<></eof<>
इस स्क्रिप्ट को cte.sql नामक फ़ाइल में इस SQL कथन की आवश्यकता है
WITH RECURSIVE a AS (
SELECT id, parent_id, 1::integer recursion_level
FROM person
WHERE parent_id IS NULL
UNION ALL
SELECT d.id, d.parent_id, a.recursion_level +1
FROM person d
JOIN a ON a.id = d.parent_id )
SELECT parent_id, id, recursion_level FROM a;
फिर आप इसे इस तरह कहते हैं:
chmod +x pggraph
./pggraph
और आप परिणामी ग्राफ देखेंगे।
INSERT INTO person (id, parent_id) VALUES (6,2);
उपयोगिता को फिर से चलाएँ, और अपने निर्देशित ग्राफ़ में तत्काल परिवर्तन देखें:
अब, यह इतना कठिन नहीं था, है ना?