PostgreSQL
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> PostgreSQL

Cách đặt giá trị của trường biến tổng hợp bằng SQL động

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ó hstorejson

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ên pg_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 qua USING 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ơn string_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ằng pg_type.typname luôn giống với pg_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ột RETURN 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ột RETURN 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$;


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Về tính hữu dụng của các chỉ mục biểu thức

  2. Kích hoạt so với kiểm tra ràng buộc

  3. Hiệu suất TPC-H kể từ PostgreSQL 8.3

  4. Postgresql:Làm cách nào để chọn các mục nhập n phần trăm (%) hàng đầu từ mỗi nhóm / danh mục

  5. Làm cách nào để tôi (hoặc tôi có thể) CHỌN DISTINCT trên nhiều cột?