Trong một bài đăng trên blog trước Các truy vấn PostgreSQL Yêu thích của tôi và Tại sao Chúng lại Quan trọng, tôi đã xem các truy vấn thú vị có ý nghĩa đối với tôi khi tôi tìm hiểu, phát triển và trở thành một nhà phát triển SQL.
Đặc biệt, một trong số đó là CẬP NHẬT nhiều hàng với một biểu thức CASE duy nhất, đã khơi dậy một cuộc trò chuyện thú vị trên Hacker News.
Trong bài đăng trên blog này, tôi muốn quan sát các so sánh giữa truy vấn cụ thể đó và một truy vấn liên quan đến nhiều câu lệnh CẬP NHẬT đơn lẻ. Tốt hay xấu.
Thông số kỹ thuật máy / môi trường:
- CPU Intel (R) Core (TM) i5-6200U @ 2,30 GHz
- RAM 8GB
- Bộ nhớ 1TB
- Xubuntu Linux 16.04.3 LTS (Xenial Xerus)
- PostgreSQL 10.4
Lưu ý:Để bắt đầu, tôi đã tạo một bảng 'dàn dựng' với tất cả các cột loại TEXT để tải dữ liệu.
Tập dữ liệu mẫu mà tôi đang sử dụng có tại liên kết này ở đây.
Nhưng hãy nhớ rằng, bản thân dữ liệu được sử dụng trong ví dụ này vì nó là một tập hợp có kích thước phù hợp với nhiều cột. Mọi 'phân tích' hoặc CẬP NHẬT / ĐẦU VÀO cho tập dữ liệu này, không phản ánh hoạt động GPS / GIS thực tế 'trong thế giới thực' và không nhằm mục đích như vậy.
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)
Chúng tôi có khoảng nửa triệu hàng dữ liệu trong bảng này.
Đối với lần so sánh đầu tiên này, tôi sẽ CẬP NHẬT cột proj_code.
Đây là một truy vấn khám phá để xác định các giá trị hiện tại của nó:
location=# SELECT DISTINCT proj_code FROM data_staging;
proj_code
-----------
"70"
""
"72"
"71"
"51"
"15"
"16"
(7 rows)
Tôi sẽ sử dụng trim để xóa dấu ngoặc kép khỏi các giá trị và chuyển thành INT và xác định số hàng tồn tại cho mỗi giá trị riêng lẻ:
Hãy sử dụng một CTE cho điều đó, sau đó CHỌN từ nó:
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)
Trước khi chạy các bài kiểm tra này, tôi sẽ tiếp tục và ALTER cột proj_code để nhập INTEGER:
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;
Và dọn dẹp giá trị cột NULL đó (được đại diện bởi ELSE '00' trong biểu thức CASE khám phá ở trên), đặt nó thành một số tùy ý, 10, với CẬP NHẬT này:
UPDATE data_staging
SET proj_code = 10
WHERE proj_code IS NULL;
Giờ đây, tất cả các cột proj_code đều có giá trị INTEGER.
Hãy tiếp tục và chạy một biểu thức CASE duy nhất cập nhật tất cả các giá trị cột proj_code và xem báo cáo thời gian là gì. Tôi sẽ đặt tất cả các lệnh trong tệp nguồn .sql để dễ xử lý.
Đây là nội dung tệp:
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;
Hãy chạy tệp này và kiểm tra báo cáo thời gian:
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
Chỉ hơn nửa triệu hàng trong hơn 6 giây.
Dưới đây là những thay đổi được phản ánh trong bảng cho đến nay:
location=# SELECT DISTINCT proj_code FROM data_staging;
proj_code
-----------
7070
1616
1010
7171
1515
7272
5151
(7 rows)
Tôi sẽ QUAY LẠI (không hiển thị) những thay đổi này để tôi có thể chạy các câu lệnh INSERT riêng lẻ để kiểm tra những thay đổi đó.
Dưới đây phản ánh các sửa đổi đối với tệp nguồn .sql cho loạt so sánh này:
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;
Và những kết quả đó,
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
Hãy kiểm tra các giá trị:
location=# SELECT DISTINCT proj_code FROM data_staging;
proj_code
-----------
7222
1616
7070
1010
7171
1515
5151
(7 rows)
Và thời gian (Lưu ý:Tôi sẽ thực hiện phép toán trong một truy vấn vì \ timing không báo cáo toàn bộ giây trong lần chạy này):
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 riêng lẻ mất khoảng một nửa thời gian so với CASE đơn lẻ.
Thử nghiệm đầu tiên này bao gồm toàn bộ bảng, với tất cả các cột. Tôi tò mò về bất kỳ sự khác biệt nào trong một bảng có cùng số hàng, nhưng ít cột hơn, do đó, loạt bài kiểm tra tiếp theo.
Tôi sẽ tạo một bảng có 2 cột (bao gồm kiểu dữ liệu SERIAL cho PRIMARY KEY và INTEGER cho cột proj_code) và di chuyển qua dữ liệu:
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
( Lưu ý:Các lệnh SQL từ nhóm hoạt động đầu tiên được sử dụng với (các) sửa đổi thích hợp. Tôi bỏ qua chúng ở đây để hiển thị và ngắn gọn trên màn hình )
Trước tiên, tôi sẽ chạy biểu thức CASE đơn:
location=# \i /case_insert.sql
BEGIN
Timing is on.
UPDATE 546895
Time: 4355.332 ms (00:04.355)
SAVEPOINT
Time: 0.137 ms
Và sau đó là CẬP NHẬT của từng cá nhân:
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)
Thời gian có phần đồng đều giữa cả hai nhóm thao tác trên bảng chỉ với 2 cột.
Tôi sẽ nói rằng sử dụng biểu thức CASE dễ nhập hơn một chút, nhưng không nhất thiết là lựa chọn tốt nhất trong mọi trường hợp. Như với những gì đã nêu trong một số nhận xét về chuỗi Tin tức Hacker được tham chiếu ở trên, nó thường "phụ thuộc" vào nhiều yếu tố mà có thể có hoặc có thể không phải là lựa chọn tối ưu.
Tôi nhận thấy rằng những thử nghiệm này là chủ quan. Một trong số đó, trên bảng có 11 cột trong khi bảng kia chỉ có 2 cột, cả hai đều thuộc kiểu dữ liệu số.
Biểu thức CASE cho nhiều bản cập nhật hàng vẫn là một trong những truy vấn yêu thích của tôi, nếu chỉ để dễ nhập trong môi trường được kiểm soát, nơi nhiều truy vấn CẬP NHẬT riêng lẻ là lựa chọn thay thế khác.
Tuy nhiên, tôi có thể thấy bây giờ không phải lúc nào cũng là lựa chọn tối ưu khi tôi tiếp tục phát triển và học hỏi.
Như câu nói cũ đó là " Nửa tá trong một tay, 6 trong tay kia . "
Một truy vấn yêu thích bổ sung - Sử dụng PLpgSQL CURSOR's
Tôi đã bắt đầu lưu trữ và theo dõi tất cả các số liệu thống kê về bài tập (đi bộ đường dài) của mình với PostgreSQL trên máy phát triển cục bộ của mình. Có nhiều bảng liên quan, như với bất kỳ cơ sở dữ liệu chuẩn hóa nào.
Tuy nhiên, vào cuối tháng, tôi muốn lưu trữ thống kê của các cột cụ thể, trong bảng riêng, của chúng.
Đây là bảng 'hàng tháng' tôi sẽ sử dụng:
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 | | |
Tôi sẽ tập trung vào kết quả của tháng 5 với truy vấn SELECT này:
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;
Và đây là 3 hàng mẫu được trả về từ truy vấn đó:
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)
Sự thật mà nói, tôi có thể điền vào bảng hiking_month_total target bằng cách sử dụng truy vấn SELECT ở trên trong một câu lệnh INSERT.
Nhưng niềm vui ở đó là ở đâu?
Thay vào đó, tôi sẽ bỏ qua sự nhàm chán đối với một hàm PLpgSQL với một CURSOR.
Tôi đã nghĩ ra chức năng này để thực hiện INSERT với CURSOR:
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;
Hãy gọi hàm month_total_stats () để thực hiện INSERT:
fitness=> SELECT monthly_total_stats();
monthly_total_stats
---------------------
(1 row)
Vì hàm được định nghĩa là void RETURNS, chúng ta có thể thấy không có giá trị nào được trả về cho người gọi.
Tại thời điểm này, tôi không quan tâm cụ thể đến bất kỳ giá trị trả lại nào,
chỉ rằng hàm thực hiện hoạt động đã xác định, điền vào bảng hiking_month_total.
Tôi sẽ truy vấn số lượng bản ghi trong bảng mục tiêu, xác nhận rằng nó có dữ liệu:
fitness=> SELECT COUNT(*) FROM hiking_month_total;
count
-------
25
(1 row)
Hàm month_total_stats () hoạt động, nhưng có lẽ trường hợp sử dụng tốt hơn cho CURSOR là cuộn qua một số lượng lớn các bản ghi. Có thể là một bảng có khoảng nửa triệu bản ghi?
CURSOR tiếp theo này được liên kết với một truy vấn nhắm mục tiêu bảng data_staging từ một loạt các so sánh trong phần trên:
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;
Sau đó, để sử dụng CURSOR này, hãy hoạt động trong GIAO DỊCH (được chỉ ra trong tài liệu ở đây).
location=# BEGIN;
BEGIN
location=# SELECT location_curs();
location_curs
--------------------
<unnamed portal 1>
(1 row)
Vậy bạn có thể làm gì với "
Đây chỉ là một số điều:
Chúng ta có thể trả về hàng đầu tiên từ CURSOR bằng cách sử dụng đầu tiên hoặc ABSOLUTE 1:
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)
Muốn có một hàng gần như nửa chặng đường của kết quả đã đặt? (Giả sử chúng ta biết ước tính có khoảng nửa triệu hàng được liên kết với CURSOR.)
Bạn có thể là người 'cụ thể' với CURSOR không?
Đúng.
Chúng tôi có thể định vị và tìm kiếm các giá trị cho bản ghi tại hàng 234888 (chỉ là một số ngẫu nhiên tôi đã chọn):
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)
Khi đã định vị ở đó, chúng ta có thể di chuyển CURSOR 'lùi lại một':
location=# FETCH BACKWARD FROM "<unnamed portal 1>";
segment_num | latitude | longitude | proj_code | asbuilt_flag
-------------+------------------+-------------------+-----------+--------------
" 11261" | " 28.1159358200" | " -90.7778242300" | 10 | "Y"
(1 row)
Giống như:
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)
Sau đó, chúng ta có thể di chuyển CURSOR ngay trở lại ABSOLUTE 234888 bằng:
location=# FETCH FORWARD FROM "<unnamed portal 1>";
segment_num | latitude | longitude | proj_code | asbuilt_flag
-------------+------------------+-------------------+-----------+--------------
" 11261" | " 28.1159541400" | " -90.7778003500" | 10 | "Y"
(1 row)
Mẹo tiện dụng:để đặt lại vị trí của CURSOR, hãy sử dụng MOVE thay vì FETCH nếu bạn không cần các giá trị từ hàng đó.
Xem đoạn văn này từ tài liệu:
"MOVE định vị lại một con trỏ mà không truy xuất bất kỳ dữ liệu nào. MOVE hoạt động chính xác như lệnh FETCH, ngoại trừ lệnh này chỉ định vị con trỏ và không trả về hàng."
Tên "
Tôi sẽ truy cập lại dữ liệu thống kê thể chất của mình để viết một hàm và đặt tên là CURSOR, cùng với một trường hợp sử dụng tiềm năng 'trong thế giới thực'.
CURSOR sẽ nhắm mục tiêu bảng bổ sung này, bảng này lưu trữ kết quả không giới hạn trong tháng 5 (về cơ bản là tất cả những gì tôi đã thu thập cho đến nay) như trong ví dụ trước:
fitness=> CREATE TABLE cp_hiking_total AS SELECT * FROM hiking_month_total WITH NO DATA;
CREATE TABLE AS
Sau đó, điền nó với dữ liệu:
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
Bây giờ với hàm PLpgSQL bên dưới, TẠO một CURSOR 'có tên':
CREATE OR REPLACE FUNCTION stats_cursor(refcursor)
RETURNS refcursor
AS $$
BEGIN
OPEN $1 FOR
SELECT *
FROM cp_hiking_total;
RETURN $1;
END;
$$ LANGUAGE plpgsql;
Tôi sẽ gọi đây là CURSOR 'thống kê':
fitness=> BEGIN;
BEGIN
fitness=> SELECT stats_cursor('stats');
stats_cursor
--------------
stats
(1 row)
Giả sử, tôi muốn hàng 'thứ 12' liên kết với CURSOR.
Tôi có thể định vị CURSOR trên hàng đó, truy xuất các kết quả đó bằng lệnh dưới đây:
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)
Đối với mục đích của bài đăng trên blog này, hãy tưởng tượng tôi biết trực tiếp giá trị cột nhịp độ cho hàng này là không chính xác.
Tôi đặc biệt nhớ mình đã 'chết mê chết mệt' vào ngày hôm đó và chỉ duy trì được tốc độ 3.0 trong suốt chuyến đi bộ đường dài đó. (Này, nó sẽ xảy ra.)
Được rồi, tôi sẽ chỉ CẬP NHẬT bảng cp_hiking_total để phản ánh thay đổi đó.
Tương đối đơn giản không có nghi ngờ gì. Nhàm chán…
Thay vào đó, với số liệu thống kê CURSOR thì sao?
fitness=> UPDATE cp_hiking_total
fitness-> SET pace = 3.0
fitness-> WHERE CURRENT OF stats;
UPDATE 1
Để thực hiện thay đổi này vĩnh viễn, hãy phát hành COMMIT:
fitness=> COMMIT;
COMMIT
Hãy truy vấn và xem CẬP NHẬT được phản ánh trong bảng 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)
Điều đó thật tuyệt làm sao?
Di chuyển trong tập kết quả của CURSOR và chạy CẬP NHẬT nếu cần.
Khá mạnh mẽ nếu bạn hỏi tôi. Và thuận tiện.
Một số "thận trọng" và thông tin từ tài liệu về loại CURSOR này:
"Thông thường, bạn nên sử dụng FOR UPDATE nếu con trỏ được dự định sử dụng với UPDATE ... WHERE CURRENT OF hoặc DELETE ... WHERE CURRENT OF. Sử dụng FOR UPDATE ngăn các phiên khác thay đổi hàng giữa thời gian chúng được tìm nạp và thời gian chúng được cập nhật. Nếu không có FOR UPDATE, lệnh WHERE CURRENT OF tiếp theo sẽ không có hiệu lực nếu hàng đã được thay đổi kể từ khi con trỏ được tạo.
Một lý do khác để sử dụng FOR UPDATE là nếu không có nó, WHERE CURRENT OF tiếp theo có thể không thành công nếu truy vấn con trỏ không đáp ứng các quy tắc của tiêu chuẩn SQL là "có thể cập nhật đơn giản" (cụ thể là con trỏ chỉ phải tham chiếu đến một bảng và không sử dụng nhóm hoặc ĐẶT HÀNG THEO). Con trỏ không thể cập nhật đơn giản có thể hoạt động hoặc có thể không, tùy thuộc vào chi tiết lựa chọn gói; vì vậy trong trường hợp xấu nhất, một ứng dụng có thể hoạt động trong quá trình thử nghiệm và sau đó bị lỗi trong quá trình sản xuất. "
Với CURSOR mà tôi đã sử dụng ở đây, tôi đã tuân theo các quy tắc chuẩn của SQL (từ các đoạn trên) về khía cạnh:Tôi chỉ tham chiếu đến một bảng, không có nhóm hoặc mệnh đề ORDER theo.
Tại sao nó lại quan trọng.
Cũng như với nhiều hoạt động, truy vấn hoặc tác vụ trong PostgreSQL (và SQL nói chung), thường có nhiều cách để hoàn thành và đạt được mục tiêu cuối cùng của bạn. Đó là một trong những lý do chính khiến tôi bị cuốn hút vào SQL và cố gắng học hỏi thêm.
Tôi hy vọng thông qua bài đăng blog tiếp theo này, tôi đã cung cấp một số thông tin chi tiết về lý do tại sao UPDATE nhiều hàng với CASE lại được đưa vào như một trong những truy vấn yêu thích của tôi, trong bài đăng blog đi kèm đầu tiên đó. Đối với tôi, chỉ cần có nó như một lựa chọn cũng đáng giá.
Ngoài ra, khám phá CURSORS, để duyệt qua các tập kết quả lớn. Thực hiện các hoạt động DML, như CẬP NHẬT và / hoặc XÓA, với loại CURSOR chính xác, chỉ là 'đóng băng trên bánh'. Tôi nóng lòng muốn nghiên cứu thêm về chúng để có nhiều trường hợp sử dụng hơn.