Nhanh hơn với hstore
Kể từ khi Postgres 9.0 , với mô-đun bổ sung hstore
được cài đặt trong cơ sở dữ liệu của bạn, có một giải pháp rất đơn giản và nhanh chóng với #=
toán tử mà ...
thay thế [s] các trường trong
record
với các giá trị phù hợp từhstore
.
Để cài đặt mô-đun:
CREATE EXTENSION hstore;
Ví dụ:
SELECT my_record #= '"field"=>"value"'::hstore; -- with string literal
SELECT my_record #= hstore(field, value); -- with values
Giá trị phải được chuyển thành text
và quay lại, hiển nhiên.
Ví dụ về các hàm plpgsql với nhiều chi tiết hơn:
- Vòng lặp vô tận trong chức năng kích hoạt
- Gán thành MỚI theo khóa trong trình kích hoạt Postgres
Hiện hoạt động với json
/ jsonb
, quá!
Có các giải pháp tương tự với json
(pg 9.3+) hoặc jsonb
(trang 9.4+)
SELECT json_populate_record (my_record, json_build_object('key', 'new-value');
Chức năng này không có tài liệu, nhưng nó chính thức kể từ Postgres 13. Hướng dẫn sử dụng:
Tuy nhiên, nếu cơ sở không phải là NULL thì các giá trị mà nó chứa sẽ được sử dụng cho các cột chưa khớp.
Vì vậy, bạn có thể lấy bất kỳ hàng hiện có nào và điền vào các trường tùy ý (ghi đè lên những hàng trong đó).
Ưu điểm chính của json
so với hstore
:
- hoạt động với Postgres có sẵn, vì vậy bạn không cần thêm mô-đun.
- cũng hoạt động cho các kiểu mảng và tổ hợp lồng nhau.
Nhược điểm nhỏ:chậm hơn một chút.
Xem câu trả lời đã thêm của @ Geir để biết chi tiết.
Không có hstore
và json
Nếu bạn đang sử dụng phiên bản cũ hơn hoặc không thể cài đặt mô-đun bổ sung hstore
hoặc không thể cho rằng nó đã được cài đặt, đây là phiên bản cải tiến của những gì tôi đã đăng trước đây. Vẫn chậm hơn hstore
nhà điều hành, mặc dù:
CREATE OR REPLACE FUNCTION f_setfield(INOUT _comp_val anyelement
, _field text, _val text)
RETURNS anyelement
LANGUAGE plpgsql STABLE AS
$func$
BEGIN
EXECUTE 'SELECT ' || array_to_string(ARRAY(
SELECT CASE WHEN attname = _field
THEN '$2'
ELSE '($1).' || quote_ident(attname)
END AS fld
FROM pg_catalog.pg_attribute
WHERE attrelid = pg_typeof(_comp_val)::text::regclass
AND attnum > 0
AND attisdropped = FALSE
ORDER BY attnum
), ',')
USING _comp_val, _val
INTO _comp_val;
END
$func$;
Gọi:
CREATE TEMP TABLE t( a int, b text); -- Composite type for testing
SELECT f_setfield(NULL::t, 'a', '1');
Ghi chú
-
Truyền giá trị
_val
rõ ràng đối với kiểu dữ liệu đích là không cần thiết, một chuỗi ký tự trong truy vấn động sẽ được cưỡng chế tự động, loại bỏ truy vấn con trênpg_type
. Nhưng tôi đã tiến thêm một bước nữa: -
Thay thế
quote_literal(_val)
có chèn giá trị trực tiếp quaUSING
mệnh đề. Lưu một lệnh gọi hàm và hai lệnh, và dù sao cũng an toàn hơn.text
được buộc phải tự động chuyển sang kiểu đích trong PostgreSQL hiện đại. (Không thử nghiệm với các phiên bản trước 9.1.) -
array_to_string(ARRAY())
nhanh hơnstring_agg()
. -
Không cần biến, không cần
DECLARE
. Ít bài tập hơn. -
Không có truy vấn con nào trong SQL động.
($1).field
nhanh hơn. -
pg_typeof(_comp_val)::text::regclass
tương tự như
(SELECT typrelid FROM pg_catalog.pg_type WHERE oid = pg_typeof($1)::oid)
cho các loại kết hợp hợp lệ, nhanh hơn.
Sửa đổi cuối cùng này được xây dựng dựa trên giả định rằngpg_type.typname
luôn giống vớipg_class.relname
được liên kết đối với các loại kết hợp đã đăng ký và kết hợp kép có thể thay thế truy vấn con. Tôi đã chạy thử nghiệm này trong một cơ sở dữ liệu lớn để xác minh và nó trống như mong đợi:
SELECT *
FROM pg_catalog.pg_type t
JOIN pg_namespace n ON n.oid = t.typnamespace
WHERE t.typrelid > 0 -- exclude non-composite types
AND t.typrelid IS DISTINCT FROM
(quote_ident(n.nspname ) || '.' || quote_ident(typname))::regclass
- Việc sử dụng
INOUT
tham số loại bỏ nhu cầu về mộtRETURN
rõ ràng . Đây chỉ là một phím tắt ký hiệu. Pavel sẽ không thích nó, anh ấy thích mộtRETURN
rõ ràng tuyên bố ...
Mọi thứ kết hợp lại với nhau sẽ nhanh gấp đôi như phiên bản trước.
Câu trả lời gốc (lỗi thời):
Kết quả là một phiên bản nhanh hơn ~ 2,25 lần . Nhưng có lẽ tôi không thể làm được nếu không xây dựng trên phiên bản thứ hai của Pavel.
Ngoài ra, phiên bản này tránh hầu hết quá trình truyền để nhắn tin và quay lại bằng cách thực hiện mọi thứ trong một truy vấn duy nhất, do đó, nó sẽ ít bị lỗi hơn nhiều.
Đã thử nghiệm với PostgreSQL 9.0 và 9.1 .
CREATE FUNCTION f_setfield(_comp_val anyelement, _field text, _val text)
RETURNS anyelement
LANGUAGE plpgsql STABLE AS
$func$
DECLARE
_list text;
BEGIN
_list := (
SELECT string_agg(x.fld, ',')
FROM (
SELECT CASE WHEN a.attname = $2
THEN quote_literal($3) || '::'|| (SELECT quote_ident(typname)
FROM pg_catalog.pg_type
WHERE oid = a.atttypid)
ELSE quote_ident(a.attname)
END AS fld
FROM pg_catalog.pg_attribute a
WHERE a.attrelid = (SELECT typrelid
FROM pg_catalog.pg_type
WHERE oid = pg_typeof($1)::oid)
AND a.attnum > 0
AND a.attisdropped = false
ORDER BY a.attnum
) x
);
EXECUTE 'SELECT ' || _list || ' FROM (SELECT $1.*) x'
USING $1
INTO $1;
RETURN $1;
END
$func$;