PostgreSQL
 sql >> डेटाबेस >  >> RDS >> PostgreSQL

मेरे पसंदीदा पोस्टग्रेएसक्यूएल प्रश्नों में से अधिक - और वे भी क्यों मायने रखते हैं

पिछले ब्लॉग पोस्ट में माई फेवरेट पोस्टग्रेएसक्यूएल क्वेरीज़ एंड व्हाई वे मैटर में, जैसे-जैसे मैं सीखता, विकसित करता, और एक SQL डेवलपर भूमिका में विकसित होता गया, मैंने अपने लिए सार्थक दिलचस्प प्रश्नों का दौरा किया।

उनमें से एक, विशेष रूप से, एकल CASE अभिव्यक्ति के साथ एक बहु-पंक्ति अद्यतन, ने हैकर समाचार पर एक दिलचस्प बातचीत को जन्म दिया।

इस ब्लॉग पोस्ट में, मैं उस विशेष क्वेरी के बीच तुलना करना चाहता हूं, और एक में कई एकल अद्यतन विवरण शामिल हैं। अच्छे या अभिशाप के लिए।

मशीन/पर्यावरण विनिर्देश:

  • Intel(R) Core(TM) i5-6200U CPU @ 2.30GHz
  • 8GB रैम
  • 1TB संग्रहण
  • ज़ुबंटू लिनक्स 16.04.3 एलटीएस (ज़ेनियल ज़ेरस)
  • पोस्टग्रेएसक्यूएल 10.4

नोट:शुरू करने के लिए, मैंने डेटा लोड करने के लिए सभी टेक्स्ट प्रकार के कॉलम के साथ एक 'स्टेजिंग' तालिका बनाई है।

मैं जिस नमूना डेटा सेट का उपयोग कर रहा हूं, वह इस लिंक पर यहां पाया गया है।

लेकिन ध्यान रखें, इस उदाहरण में डेटा का ही उपयोग किया गया है क्योंकि यह कई स्तंभों के साथ एक सभ्य आकार का सेट है। इस डेटा सेट का कोई भी 'विश्लेषण' या अद्यतन/इन्सर्ट्स, वास्तविक 'वास्तविक दुनिया' के जीपीएस/जीआईएस संचालन को प्रतिबिंबित नहीं करता है और इस तरह का इरादा नहीं है।

location=# \d data_staging;
               Table "public.data_staging"
    Column     |  Type   | Collation | Nullable | Default 
---------------+---------+-----------+----------+---------
 segment_num   | text    |           |          | 
 point_seg_num | text    |           |          | 
 latitude      | text    |           |          | 
 longitude     | text    |           |          | 
 nad_year_cd   | text    |           |          | 
 proj_code     | text    |           |          | 
 x_cord_loc    | text    |           |          | 
 y_cord_loc    | text    |           |          | 
 last_rev_date | text    |           |          | 
 version_date  | text    |           |          | 
 asbuilt_flag  | text    |           |          | 

location=# SELECT COUNT(*) FROM data_staging;
count
--------
546895
(1 row)

इस तालिका में हमारे पास डेटा की लगभग आधा मिलियन पंक्तियाँ हैं।

इस पहली तुलना के लिए, मैं proj_code कॉलम को अपडेट करूंगा।

इसके वर्तमान मूल्यों को निर्धारित करने के लिए यहां एक खोजपूर्ण प्रश्न है:

location=# SELECT DISTINCT proj_code FROM data_staging;
proj_code
-----------
"70"
""
"72"
"71"
"51"
"15"
"16"
(7 rows)

मैं मूल्यों से उद्धरण हटाने के लिए ट्रिम का उपयोग करूंगा और एक आईएनटी पर डालूंगा और निर्धारित करूंगा कि प्रत्येक व्यक्तिगत मूल्य के लिए कितनी पंक्तियां मौजूद हैं:

आइए उसके लिए एक सीटीई का उपयोग करें, फिर उसमें से चुनें:

location=# WITH cleaned_nums AS (
SELECT NULLIF(trim(both '"' FROM proj_code), '') AS p_code FROM data_staging
)
SELECT COUNT(*),
CASE
WHEN p_code::int = 70 THEN '70'
WHEN p_code::int = 72 THEN '72'
WHEN p_code::int = 71 THEN '71'
WHEN p_code::int = 51 THEN '51'
WHEN p_code::int = 15 THEN '15'
WHEN p_code::int = 16 THEN '16'
ELSE '00'
END AS proj_code_num
FROM cleaned_nums
GROUP BY p_code
ORDER BY p_code DESC;
count  | proj_code_num
--------+---------------
353087 | 0
139057 | 72
25460  | 71
3254   | 70
1      | 51
12648  | 16
13388  | 15
(7 rows)

इन परीक्षणों को चलाने से पहले, मैं आगे बढ़ूंगा और INTEGER टाइप करने के लिए proj_code कॉलम को बदलूंगा:

BEGIN;
ALTER TABLE data_staging ALTER COLUMN proj_code SET DATA TYPE INTEGER USING NULLIF(trim(both '"' FROM proj_code), '')::INTEGER;
SAVEPOINT my_save;
COMMIT;

और उस NULL स्तंभ मान को साफ़ करें (जो ऊपर खोजपूर्ण CASE अभिव्यक्ति में ELSE '00' द्वारा दर्शाया गया है), इसे इस अद्यतन के साथ एक मनमाना संख्या, 10 पर सेट करना:

UPDATE data_staging
SET proj_code = 10
WHERE proj_code IS NULL;

अब सभी proj_code कॉलम में एक INTEGER मान होता है।

आइए आगे बढ़ते हैं और सभी proj_code कॉलम मानों को अपडेट करते हुए एक केस एक्सप्रेशन चलाते हैं और देखते हैं कि टाइमिंग क्या रिपोर्ट करती है। प्रबंधन में आसानी के लिए मैं सभी कमांड को .sql स्रोत फ़ाइल में रखूंगा।

यहाँ फ़ाइल सामग्री है:

BEGIN;
\timing on
UPDATE data_staging
SET proj_code =
(
CASE proj_code
WHEN 72 THEN 7272
WHEN 71 THEN 7171
WHEN 15 THEN 1515
WHEN 51 THEN 5151
WHEN 70 THEN 7070
WHEN 10 THEN 1010
WHEN 16 THEN 1616
END
)
WHERE proj_code IN (72, 71, 15, 51, 70, 10, 16);
SAVEPOINT my_save;

आइए इस फ़ाइल को चलाते हैं और जाँचते हैं कि समय क्या रिपोर्ट करता है:

location=# \i /case_insert.sql
BEGIN
Time: 0.265 ms
Timing is on.
UPDATE 546895
Time: 6779.596 ms (00:06.780)
SAVEPOINT
Time: 0.300 ms

6+ सेकंड में केवल 50 लाख से अधिक पंक्तियाँ।

तालिका में अब तक परिलक्षित परिवर्तन यहां दिए गए हैं:

location=# SELECT DISTINCT proj_code FROM data_staging;
proj_code
-----------
7070
1616
1010
7171
1515
7272
5151
(7 rows)

मैं इन परिवर्तनों को रोलबैक (दिखाया नहीं गया) करूंगा ताकि मैं इनका परीक्षण करने के लिए अलग-अलग INSERT स्टेटमेंट भी चला सकूं।

तुलना की इस श्रृंखला के लिए .sql स्रोत फ़ाइल में संशोधनों को नीचे दर्शाया गया है:

BEGIN;
\timing on

UPDATE data_staging
SET proj_code = 7222
WHERE proj_code = 72;

UPDATE data_staging
SET proj_code = 7171
WHERE proj_code = 71;

UPDATE data_staging
SET proj_code = 1515
WHERE proj_code = 15;

UPDATE data_staging
SET proj_code = 5151
WHERE proj_code = 51;

UPDATE data_staging
SET proj_code = 7070
WHERE proj_code = 70;

UPDATE data_staging
SET proj_code = 1010
WHERE proj_code = 10;

UPDATE data_staging
SET proj_code = 1616
WHERE proj_code = 16;
SAVEPOINT my_save;

और वे परिणाम,

location=# \i /case_insert.sql
BEGIN
Time: 0.264 ms
Timing is on.
UPDATE 139057
Time: 795.610 ms
UPDATE 25460
Time: 116.268 ms
UPDATE 13388
Time: 239.007 ms
UPDATE 1
Time: 72.699 ms
UPDATE 3254
Time: 162.199 ms
UPDATE 353087
Time: 1987.857 ms (00:01.988)
UPDATE 12648
Time: 321.223 ms
SAVEPOINT
Time: 0.108 ms

आइए मानों की जाँच करें:

location=# SELECT DISTINCT proj_code FROM data_staging;
proj_code
-----------
7222
1616
7070
1010
7171
1515
5151
(7 rows)

और समय (नोट:मैं एक प्रश्न में गणित करूँगा क्योंकि \time ने इस रन के पूरे सेकंड की रिपोर्ट नहीं की):

location=# SELECT round((795.610 + 116.268 + 239.007 + 72.699 + 162.199 + 1987.857 + 321.223) / 1000, 3) AS seconds;
seconds
---------
3.695
(1 row)

व्यक्तिगत INSERT ने एकल मामले के रूप में लगभग आधा समय लिया।

इस पहले परीक्षण में सभी स्तंभों के साथ संपूर्ण तालिका शामिल थी। मैं पंक्तियों की समान संख्या वाली तालिका में किसी भी अंतर के बारे में उत्सुक हूं, लेकिन कम कॉलम, इसलिए परीक्षणों की अगली श्रृंखला।

मैं 2 कॉलम के साथ एक टेबल बनाउंगा (प्राथमिक कुंजी के लिए एक सीरियल डेटा प्रकार और proj_code कॉलम के लिए एक INTEGER से बना) और डेटा पर आगे बढ़ें:

location=# CREATE TABLE proj_nums(n_id SERIAL PRIMARY KEY, proj_code INTEGER);
CREATE TABLE
location=# INSERT INTO proj_nums(proj_code) SELECT proj_code FROM data_staging;
INSERT 0 546895

(ध्यान देने के लिए:संचालन के पहले सेट से SQL कमांड उपयुक्त संशोधनों के साथ उपयोग किए जाते हैं। मैं उन्हें ऑन-स्क्रीन संक्षिप्तता और प्रदर्शन के लिए यहां छोड़ रहा हूं )

मैं पहले सिंगल केस एक्सप्रेशन चलाऊंगा:

location=# \i /case_insert.sql
BEGIN
Timing is on.
UPDATE 546895
Time: 4355.332 ms (00:04.355)
SAVEPOINT
Time: 0.137 ms

और फिर व्यक्तिगत अद्यतन:

location=# \i /case_insert.sql
BEGIN
Time: 0.282 ms
Timing is on.
UPDATE 139057
Time: 1042.133 ms (00:01.042)
UPDATE 25460
Time: 123.337 ms
UPDATE 13388
Time: 212.698 ms
UPDATE 1
Time: 43.107 ms
UPDATE 3254
Time: 52.669 ms
UPDATE 353087
Time: 2787.295 ms (00:02.787)
UPDATE 12648
Time: 99.813 ms
SAVEPOINT
Time: 0.059 ms
location=# SELECT round((1042.133 + 123.337 + 212.698 + 43.107 + 52.669 + 2787.295 + 99.813) / 1000, 3) AS seconds;
seconds
---------
4.361
(1 row)

टेबल पर केवल 2 कॉलम के साथ संचालन के दोनों सेटों के बीच का समय कुछ हद तक है।

मैं कहूंगा कि CASE अभिव्यक्ति का उपयोग करना थोड़ा आसान है, लेकिन जरूरी नहीं कि सभी अवसरों पर सबसे अच्छा विकल्प हो। जैसा कि ऊपर संदर्भित हैकर न्यूज थ्रेड पर कुछ टिप्पणियों में कहा गया था, यह आम तौर पर कई कारकों पर "बस निर्भर करता है" जो इष्टतम विकल्प हो सकता है या नहीं भी हो सकता है।

मुझे एहसास है कि ये परीक्षण व्यक्तिपरक हैं। उनमें से एक, 11 कॉलम वाली टेबल पर जबकि दूसरे में सिर्फ 2 कॉलम थे, दोनों ही नंबर डेटा टाइप के थे।

एकाधिक पंक्ति अद्यतनों के लिए CASE अभिव्यक्ति अभी भी मेरे पसंदीदा प्रश्नों में से एक है, यदि केवल नियंत्रित वातावरण में टाइपिंग की आसानी के लिए जहां कई अलग-अलग अद्यतन प्रश्न अन्य विकल्प हैं।

हालांकि, अब मैं देख सकता हूं कि यह हमेशा इष्टतम विकल्प नहीं है क्योंकि मैं बढ़ना और सीखना जारी रखता हूं।

जैसा कि वह पुरानी कहावत है, "एक हाथ में आधा दर्जन, दूसरे हाथ में 6 ।"

एक अतिरिक्त पसंदीदा क्वेरी - PLpgSQL CURSOR's का उपयोग करना

मैंने अपनी स्थानीय विकास मशीन पर PostgreSQL के साथ अपने सभी व्यायाम (ट्रेल हाइकिंग) आँकड़ों को संग्रहीत और ट्रैक करना शुरू कर दिया है। किसी भी सामान्यीकृत डेटाबेस की तरह इसमें कई टेबल शामिल हैं।

हालांकि, महीनों के अंत में, मैं विशिष्ट कॉलम के आँकड़ों को अपनी अलग तालिका में संग्रहीत करना चाहता हूँ।

यहां वह 'मासिक' तालिका है जिसका मैं उपयोग करूंगा:

fitness=> \d hiking_month_total;
                     Table "public.hiking_month_total"
     Column      |          Type          | Collation | Nullable | Default 
-----------------+------------------------+-----------+----------+---------
 day_hiked       | date                   |           |          | 
 calories_burned | numeric(4,1)           |           |          | 
 miles           | numeric(4,2)           |           |          | 
 duration        | time without time zone |           |          | 
 pace            | numeric(2,1)           |           |          | 
 trail_hiked     | text                   |           |          | 
 shoes_worn      | text                   |           |          |

मैं इस चयन क्वेरी के साथ मई के परिणामों पर ध्यान केंद्रित करूंगा:

fitness=> SELECT hs.day_walked, hs.cal_burned, hs.miles_walked, hs.duration, hs.mph, tr.name, sb.name_brand
fitness-> FROM hiking_stats AS hs
fitness-> INNER JOIN hiking_trail AS ht
fitness-> ON hs.hike_id = ht.th_id
fitness-> INNER JOIN trail_route AS tr
fitness-> ON ht.tr_id = tr.trail_id
fitness-> INNER JOIN shoe_brand AS sb
fitness-> ON hs.shoe_id = sb.shoe_id
fitness-> WHERE extract(month FROM hs.day_walked) = 5
fitness-> ORDER BY hs.day_walked ASC;

और यहां उस क्वेरी से लौटाई गई 3 नमूना पंक्तियां हैं:

day_walked | cal_burned | miles_walked | duration | mph | name | name_brand
------------+------------+--------------+----------+-----+------------------------+---------------------------------------
2018-05-02 | 311.2 | 3.27 | 00:57:13 | 3.4 | Tree Trail-extended | New Balance Trail Runners-All Terrain
2018-05-03 | 320.8 | 3.38 | 00:58:59 | 3.4 | Sandy Trail-Drive | New Balance Trail Runners-All Terrain
2018-05-04 | 291.3 | 3.01 | 00:53:33 | 3.4 | House-Power Line Route | Keen Koven WP(keen-dry)
(3 rows)

सच कहा जाए, तो मैं INSERT स्टेटमेंट में उपरोक्त SELECT क्वेरी का उपयोग करके लक्ष्य हाइकिंग_महीने_टोटल टेबल को पॉप्युलेट कर सकता हूं।

लेकिन इसमें मज़ा कहाँ है?

मैं इसके बजाय एक कर्सर के साथ एक PLpgSQL फ़ंक्शन के लिए बोरियत छोड़ दूंगा।

मैं एक कर्सर के साथ INSERT करने के लिए इस फ़ंक्शन के साथ आया:

CREATE OR REPLACE function monthly_total_stats()
RETURNS void
AS $month_stats$
DECLARE
v_day_walked date;
v_cal_burned numeric(4, 1);
v_miles_walked numeric(4, 2);
v_duration time without time zone;
v_mph numeric(2, 1);
v_name text;
v_name_brand text;
v_cur CURSOR for SELECT hs.day_walked, hs.cal_burned, hs.miles_walked, hs.duration, hs.mph, tr.name, sb.name_brand
FROM hiking_stats AS hs
INNER JOIN hiking_trail AS ht
ON hs.hike_id = ht.th_id
INNER JOIN trail_route AS tr
ON ht.tr_id = tr.trail_id
INNER JOIN shoe_brand AS sb
ON hs.shoe_id = sb.shoe_id
WHERE extract(month FROM hs.day_walked) = 5
ORDER BY hs.day_walked ASC;
BEGIN
OPEN v_cur;
<<get_stats>>
LOOP
FETCH v_cur INTO v_day_walked, v_cal_burned, v_miles_walked, v_duration, v_mph, v_name, v_name_brand;
EXIT WHEN NOT FOUND;
INSERT INTO hiking_month_total(day_hiked, calories_burned, miles,
duration, pace, trail_hiked, shoes_worn)
VALUES(v_day_walked, v_cal_burned, v_miles_walked, v_duration, v_mph, v_name, v_name_brand);
END LOOP get_stats;
CLOSE v_cur;
END;
$month_stats$ LANGUAGE PLpgSQL;

आइए INSERT को निष्पादित करने के लिए मासिक_टोटल_स्टैट्स () फ़ंक्शन को कॉल करें:

fitness=> SELECT monthly_total_stats();
monthly_total_stats
---------------------
(1 row)

चूंकि फ़ंक्शन को परिभाषित किया गया है रिटर्न शून्य, हम देख सकते हैं कि कॉलर को कोई मूल्य वापस नहीं किया गया है।

इस समय, मुझे किसी भी रिटर्न वैल्यू में विशेष रूप से दिलचस्पी नहीं है,

केवल यह कि फ़ंक्शन हाइकिंग_महीने_टोटल तालिका को पॉप्युलेट करते हुए परिभाषित संचालन करता है।

मैं लक्ष्य तालिका में रिकॉर्ड की संख्या के लिए क्वेरी करूंगा, यह पुष्टि करते हुए कि इसमें डेटा है:

fitness=> SELECT COUNT(*) FROM hiking_month_total;
count
-------
25
(1 row)

मासिक_टोटल_स्टैट्स () फ़ंक्शन काम करता है, लेकिन CURSOR के लिए शायद एक बेहतर उपयोग का मामला बड़ी संख्या में रिकॉर्ड के माध्यम से स्क्रॉल करना है। शायद एक टेबल जिसमें लगभग आधा मिलियन रिकॉर्ड हों?

यह अगला CURSOR उपरोक्त अनुभाग में तुलनाओं की श्रृंखला से data_staging तालिका को लक्षित करने वाली एक क्वेरी के साथ बाध्य है:

CREATE OR REPLACE FUNCTION location_curs()
RETURNS refcursor
AS $location$
DECLARE
v_cur refcursor;
BEGIN
OPEN v_cur for SELECT segment_num, latitude, longitude, proj_code, asbuilt_flag FROM data_staging;
RETURN v_cur;
END;
$location$ LANGUAGE PLpgSQL;

फिर, इस कर्सर का उपयोग करने के लिए, एक लेन-देन के भीतर काम करें (यहां दस्तावेज़ीकरण में बताया गया है)।

location=# BEGIN;
BEGIN
location=# SELECT location_curs();
location_curs 
--------------------
<unnamed portal 1>
(1 row)

तो आप इस "<अनाम पोर्टल>" के साथ क्या कर सकते हैं?

यहाँ कुछ ही चीज़ें दी गई हैं:

हम पहले या ABSOLUTE 1:

. का उपयोग करके CURSOR से पहली पंक्ति वापस कर सकते हैं
location=# FETCH first FROM "<unnamed portal 1>";
segment_num | latitude | longitude | proj_code | asbuilt_flag 
-------------+------------------+-------------------+-----------+--------------
" 3571" | " 29.0202942600" | " -90.2908612800" | 72 | "Y"
(1 row)

location=# FETCH ABSOLUTE 1 FROM "<unnamed portal 1>";
segment_num | latitude | longitude | proj_code | asbuilt_flag 
-------------+------------------+-------------------+-----------+--------------
" 3571" | " 29.0202942600" | " -90.2908612800" | 72 | "Y"
(1 row)

परिणाम सेट के माध्यम से लगभग आधी पंक्ति चाहते हैं? (मान लें कि हम जानते हैं कि अनुमानित आधा मिलियन पंक्तियां CURSOR से जुड़ी हैं।)

क्या आप CURSOR के साथ वह 'विशिष्ट' हो सकते हैं?

हां।

हम पंक्ति 234888 पर रिकॉर्ड के लिए मानों की स्थिति बना सकते हैं, और फ़ेच कर सकते हैं (बस एक यादृच्छिक संख्या जिसे मैंने चुना है):

location=# FETCH ABSOLUTE 234888 FROM "<unnamed portal 1>";
segment_num | latitude | longitude | proj_code | asbuilt_flag 
-------------+------------------+-------------------+-----------+--------------
" 11261" | " 28.1159541400" | " -90.7778003500" | 10 | "Y"
(1 row)

एक बार वहां स्थित होने के बाद, हम कर्सर को 'पिछड़े वाले' पर ले जा सकते हैं:

location=# FETCH BACKWARD FROM "<unnamed portal 1>";
segment_num | latitude | longitude | proj_code | asbuilt_flag 
-------------+------------------+-------------------+-----------+--------------
" 11261" | " 28.1159358200" | " -90.7778242300" | 10 | "Y"
(1 row)

जो समान है:

location=# FETCH ABSOLUTE 234887 FROM "<unnamed portal 1>";
segment_num | latitude | longitude | proj_code | asbuilt_flag 
-------------+------------------+-------------------+-----------+--------------
" 11261" | " 28.1159358200" | " -90.7778242300" | 10 | "Y"
(1 row)

फिर हम CURSOR को वापस ABSOLUTE 234888 पर ले जा सकते हैं:

location=# FETCH FORWARD FROM "<unnamed portal 1>";
segment_num | latitude | longitude | proj_code | asbuilt_flag 
-------------+------------------+-------------------+-----------+--------------
" 11261" | " 28.1159541400" | " -90.7778003500" | 10 | "Y"
(1 row)

आसान युक्ति:कर्सर की स्थिति बदलने के लिए, यदि आपको उस पंक्ति के मानों की आवश्यकता नहीं है, तो FETCH के बजाय MOVE का उपयोग करें।

दस्तावेज़ीकरण से यह अंश देखें:

"MOVE किसी भी डेटा को पुनर्प्राप्त किए बिना एक कर्सर को पुनर्स्थापित करता है। MOVE बिल्कुल FETCH कमांड की तरह काम करता है, सिवाय इसके कि यह केवल कर्सर को रखता है और पंक्तियों को वापस नहीं करता है।"

"<अनाम पोर्टल 1>" नाम सामान्य है और इसके बजाय वास्तव में इसे 'नाम' दिया जा सकता है।

मैं एक फ़ंक्शन लिखने और संभावित 'वास्तविक दुनिया' के उपयोग के मामले के साथ CURSOR को नाम देने के लिए अपने फ़िटनेस आँकड़े डेटा पर फिर से जाऊँगा।

CURSOR इस अतिरिक्त तालिका को लक्षित करेगा, जो पिछले उदाहरण की तरह मई के महीने (मूल रूप से अब तक मेरे द्वारा एकत्र किए गए सभी) तक सीमित नहीं परिणाम संग्रहीत करता है:

fitness=> CREATE TABLE cp_hiking_total AS SELECT * FROM hiking_month_total WITH NO DATA;
CREATE TABLE AS

फिर इसे डेटा से भर दें:

fitness=> INSERT INTO cp_hiking_total 
SELECT hs.day_walked, hs.cal_burned, hs.miles_walked, hs.duration, hs.mph, tr.name, sb.name_brand
FROM hiking_stats AS hs
INNER JOIN hiking_trail AS ht
ON hs.hike_id = ht.th_id
INNER JOIN trail_route AS tr
ON ht.tr_id = tr.trail_id
INNER JOIN shoe_brand AS sb
ON hs.shoe_id = sb.shoe_id
ORDER BY hs.day_walked ASC;
INSERT 0 51

अब नीचे दिए गए PLpgSQL फ़ंक्शन के साथ, एक 'नामित' कर्सर बनाएं:

CREATE OR REPLACE FUNCTION stats_cursor(refcursor)
RETURNS refcursor
AS $$
BEGIN
OPEN $1 FOR
SELECT *
FROM cp_hiking_total;
RETURN $1;
END;
$$ LANGUAGE plpgsql;

मैं इस कर्सर को 'आंकड़े' कहूंगा:

fitness=> BEGIN;
BEGIN
fitness=> SELECT stats_cursor('stats');
stats_cursor 
--------------
stats
(1 row)

मान लीजिए, मैं चाहता हूं कि '12वीं' पंक्ति कर्सर से जुड़ी हो।

मैं नीचे दिए गए आदेश के साथ उन परिणामों को पुनः प्राप्त करते हुए, उस पंक्ति पर CURSOR की स्थिति बना सकता हूं:

fitness=> FETCH ABSOLUTE 12 FROM stats;
day_hiked | calories_burned | miles | duration | pace | trail_hiked | shoes_worn 
------------+-----------------+-------+----------+------+---------------------+---------------------------------------
2018-05-02 | 311.2 | 3.27 | 00:57:13 | 3.4 | Tree Trail-extended | New Balance Trail Runners-All Terrain
(1 row)

इस ब्लॉग पोस्ट के प्रयोजनों के लिए, कल्पना कीजिए कि मैं पहली बार जानता हूं कि इस पंक्ति के लिए गति स्तंभ मान गलत है।

मुझे विशेष रूप से याद है कि मैं उस दिन 'थके हुए अपने पैरों पर मृत' था और उस वृद्धि के दौरान केवल 3.0 की गति बनाए रखी थी। (अरे ऐसा होता है।)

ठीक है, मैं उस परिवर्तन को दर्शाने के लिए cp_hiking_total तालिका को अभी अपडेट करूँगा।

अपेक्षाकृत सरल इसमें कोई संदेह नहीं है। उबाऊ…

इसके बजाय CURSOR आँकड़ों के बारे में क्या?

fitness=> UPDATE cp_hiking_total
fitness-> SET pace = 3.0
fitness-> WHERE CURRENT OF stats;
UPDATE 1

इस परिवर्तन को स्थायी बनाने के लिए, COMMIT जारी करें:

fitness=> COMMIT;
COMMIT

आइए क्वेरी करें और देखें कि UPDATE तालिका cp_hiking_total में परिलक्षित होता है:

fitness=> SELECT * FROM cp_hiking_total
fitness-> WHERE day_hiked = '2018-05-02';
day_hiked | calories_burned | miles | duration | pace | trail_hiked | shoes_worn 
------------+-----------------+-------+----------+------+---------------------+---------------------------------------
2018-05-02 | 311.2 | 3.27 | 00:57:13 | 3.0 | Tree Trail-extended | New Balance Trail Runners-All Terrain
(1 row)

वह कितना अच्छा है?

CURSOR के परिणाम सेट के भीतर चल रहा है, और यदि आवश्यक हो तो एक अद्यतन चलाएँ।

अगर आप मुझसे पूछें तो काफी शक्तिशाली। और सुविधाजनक।

कुछ 'सावधानी' और इस प्रकार के CURSOR पर दस्तावेज़ीकरण से जानकारी:

"आम तौर पर अद्यतन के लिए उपयोग करने की अनुशंसा की जाती है यदि कर्सर को अद्यतन के साथ उपयोग करने का इरादा है ... जहां वर्तमान या हटाएं ... जहां वर्तमान है। अद्यतन के लिए उपयोग करने से अन्य सत्रों को समय के बीच पंक्तियों को बदलने से रोकता है वे लाए गए हैं और समय वे अद्यतन कर रहे हैं। अद्यतन के बिना, बाद में जहां कर्सर के निर्माण के बाद से पंक्ति को बदल दिया गया था, वहां कमांड का कोई प्रभाव नहीं होगा।

अद्यतन के लिए उपयोग करने का एक अन्य कारण यह है कि इसके बिना, यदि कर्सर क्वेरी "बस अद्यतन करने योग्य" होने के लिए SQL मानक के नियमों को पूरा नहीं करती है, तो बाद में WHERE CURRENT OF विफल हो सकता है (विशेष रूप से, कर्सर को केवल एक तालिका का संदर्भ देना चाहिए और समूहीकरण या ORDER BY का उपयोग न करें)। योजना पसंद विवरण के आधार पर कर्सर जो केवल अद्यतन करने योग्य नहीं हैं, काम कर सकते हैं या नहीं भी कर सकते हैं; इसलिए सबसे खराब स्थिति में, कोई एप्लिकेशन परीक्षण में काम कर सकता है और फिर उत्पादन में विफल हो सकता है।"

मैंने यहां उपयोग किए गए कर्सर के साथ, मैंने इस पहलू में SQL मानक नियमों (उपरोक्त मार्ग से) का पालन किया है:मैंने केवल एक तालिका का संदर्भ दिया, जिसमें कोई समूह या खंड द्वारा आदेश नहीं है।

यह क्यों मायने रखता है।

जैसा कि PostgreSQL (और सामान्य रूप से SQL) में कई संचालन, प्रश्नों या कार्यों के साथ होता है, आमतौर पर आपके अंतिम लक्ष्य को पूरा करने और उस तक पहुंचने के एक से अधिक तरीके हैं। SQL की ओर आकर्षित होने और अधिक जानने का प्रयास करने के मुख्य कारणों में से कौन सा एक कारण है।

मुझे उम्मीद है कि इस फॉलो-अप ब्लॉग पोस्ट के माध्यम से, मैंने कुछ अंतर्दृष्टि प्रदान की है कि क्यों बहु-पंक्ति अद्यतन केस के साथ मेरे पसंदीदा प्रश्नों में से एक के रूप में शामिल किया गया था, उस पहले ब्लॉग पोस्ट में। बस इसे एक विकल्प के रूप में रखना मेरे लिए सार्थक है।

इसके अलावा, बड़े परिणाम सेट को ट्रैवर्स करने के लिए कर्सर की खोज करना। DML संचालन, जैसे UPDATES और/या DELETES, सही प्रकार के CURSOR के साथ करना, केवल 'आइसिंग ऑन द केक' है। मैं अधिक उपयोग के मामलों के लिए उनका और अध्ययन करने के लिए उत्सुक हूं।


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. SQL LIMIT और OFFSET क्वेरी का उपयोग करके सभी रिकॉर्ड का चयन करना

  2. पोस्टग्रेस्क्ल कंसोल में सामान्य होने पर क्वेरी सीएसवी फ़ाइल में क्यों नहीं सहेजी जाएगी?

  3. लॉक की गई पंक्तियों की प्रतीक्षा से बचने के लिए एडवाइजरी लॉक या NOWAIT?

  4. PostgreSQL:कमांड लाइन के माध्यम से PostgreSQL डेटाबेस को छोड़ें

  5. PostgreSQL में हाइफ़न (-) के लिए एस्केप सीक्वेंस क्या है