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

Hợp nhất bảng và nhật ký thay đổi thành một dạng xem trong PostgreSQL

Giả sử Postgres 9.1 trở lên.
Tôi đã đơn giản hóa / tối ưu hóa truy vấn cơ bản của bạn để truy xuất các giá trị mới nhất:

SELECT DISTINCT ON (1,2)
       c.unique_id, a.attname AS col, c.value
FROM   pg_attribute a
LEFT   JOIN changes c ON c.column_name = a.attname
                     AND c.table_name  = 'instances'
                 --  AND c.unique_id   = 3  -- uncomment to fetch single row
WHERE  a.attrelid = 'instances'::regclass   -- schema-qualify to be clear?
AND    a.attnum > 0                         -- no system columns
AND    NOT a.attisdropped                   -- no deleted columns
ORDER  BY 1, 2, c.updated_at DESC;

Tôi truy vấn danh mục PostgreSQL thay vì lược đồ thông tin tiêu chuẩn vì điều đó nhanh hơn. Lưu ý diễn viên đặc biệt cho ::regclass .

Bây giờ, điều đó cung cấp cho bạn bảng . Bạn muốn tất cả các giá trị cho một unique_id trong một hàng .
Để đạt được điều đó, về cơ bản bạn có ba lựa chọn:

  1. Một lựa chọn con (hoặc tham gia) trên mỗi cột. Đắt tiền và khó sử dụng. Nhưng một tùy chọn hợp lệ chỉ cho một vài cột.

  2. CASE lớn tuyên bố.

  3. Một chức năng tổng hợp . PostgreSQL cung cấp crosstab() chức năng trong mô-đun bổ sung tablefunc cho điều đó.
    Hướng dẫn cơ bản:

    • Truy vấn bảng chéo PostgreSQL

Bảng tổng hợp cơ bản với crosstab()

Tôi đã viết lại hoàn toàn hàm:

SELECT *
FROM   crosstab(
    $x$
    SELECT DISTINCT ON (1, 2)
           unique_id, column_name, value
    FROM   changes
    WHERE  table_name = 'instances'
 -- AND    unique_id = 3  -- un-comment to fetch single row
    ORDER  BY 1, 2, updated_at DESC;
    $x$,

    $y$
    SELECT attname
    FROM   pg_catalog.pg_attribute
    WHERE  attrelid = 'instances'::regclass  -- possibly schema-qualify table name
    AND    attnum > 0
    AND    NOT attisdropped
    AND    attname <> 'unique_id'
    ORDER  BY attnum
    $y$
    )
AS tbl (
 unique_id integer
-- !!! You have to list all columns in order here !!! --
);

Tôi đã tách tra cứu danh mục khỏi truy vấn giá trị, dưới dạng crosstab() hàm với hai tham số cung cấp tên cột riêng biệt. Các giá trị bị thiếu (không có mục thay đổi) được thay thế bằng NULL tự động. Một kết hợp hoàn hảo cho trường hợp sử dụng này!

Giả sử rằng attname khớp với column_name . Loại trừ unique_id , đóng một vai trò đặc biệt.

Tự động hóa hoàn toàn

Giải quyết nhận xét của bạn: Có một cách để cung cấp danh sách định nghĩa cột tự động. Tuy nhiên, nó không dành cho những người yếu tim.

Tôi sử dụng một số tính năng nâng cao của Postgres tại đây:crosstab() , hàm plpgsql với SQL động, xử lý kiểu kết hợp, trích dẫn đô la nâng cao, tra cứu danh mục, hàm tổng hợp, hàm cửa sổ, loại định danh đối tượng, ...

Môi trường thử nghiệm:

CREATE TABLE instances (
  unique_id int
, col1      text
, col2      text -- two columns are enough for the demo
);

INSERT INTO instances VALUES
  (1, 'foo1', 'bar1')
, (2, 'foo2', 'bar2')
, (3, 'foo3', 'bar3')
, (4, 'foo4', 'bar4');

CREATE TABLE changes (
  unique_id   int
, table_name  text
, column_name text
, value       text
, updated_at  timestamp
);

INSERT INTO changes VALUES
  (1, 'instances', 'col1', 'foo11', '2012-04-12 00:01')
, (1, 'instances', 'col1', 'foo12', '2012-04-12 00:02')
, (1, 'instances', 'col1', 'foo1x', '2012-04-12 00:03')
, (1, 'instances', 'col2', 'bar11', '2012-04-12 00:11')
, (1, 'instances', 'col2', 'bar17', '2012-04-12 00:12')
, (1, 'instances', 'col2', 'bar1x', '2012-04-12 00:13')

, (2, 'instances', 'col1', 'foo2x', '2012-04-12 00:01')
, (2, 'instances', 'col2', 'bar2x', '2012-04-12 00:13')

 -- NO change for col1 of row 3 - to test NULLs
, (3, 'instances', 'col2', 'bar3x', '2012-04-12 00:13');

 -- NO changes at all for row 4 - to test NULLs

Chức năng tự động cho một bảng

CREATE OR REPLACE FUNCTION f_curr_instance(int, OUT t public.instances) AS
$func$
BEGIN
   EXECUTE $f$
   SELECT *
   FROM   crosstab($x$
      SELECT DISTINCT ON (1,2)
             unique_id, column_name, value
      FROM   changes
      WHERE  table_name = 'instances'
      AND    unique_id =  $f$ || $1 || $f$
      ORDER  BY 1, 2, updated_at DESC;
      $x$
    , $y$
      SELECT attname
      FROM   pg_catalog.pg_attribute
      WHERE  attrelid = 'public.instances'::regclass
      AND    attnum > 0
      AND    NOT attisdropped
      AND    attname <> 'unique_id'
      ORDER  BY attnum
      $y$) AS tbl ($f$
   || (SELECT string_agg(attname || ' ' || atttypid::regtype::text
                       , ', ' ORDER BY attnum) -- must be in order
       FROM   pg_catalog.pg_attribute
       WHERE  attrelid = 'public.instances'::regclass
       AND    attnum > 0
       AND    NOT attisdropped)
   || ')'
   INTO t;
END
$func$  LANGUAGE plpgsql;

Bảng instances được mã hóa cứng, lược đồ đủ điều kiện để rõ ràng. Lưu ý việc sử dụng kiểu bảng làm kiểu trả về. Có một loại hàng được đăng ký tự động cho mọi bảng trong PostgreSQL. Điều này được ràng buộc phải khớp với kiểu trả về của crosstab() chức năng.

Điều này liên kết hàm với loại bảng:

  • Bạn sẽ nhận được thông báo lỗi nếu bạn cố gắng DROP cái bàn
  • Chức năng của bạn sẽ không thành công sau một ALTER TABLE . Bạn phải tạo lại nó (không có thay đổi). Tôi coi đây là một lỗi trong 9.1. ALTER TABLE không nên âm thầm phá vỡ chức năng mà gây ra lỗi.

Điều này hoạt động rất tốt.

Gọi:

SELECT * FROM f_curr_instance(3);

unique_id | col1  | col2
----------+-------+-----
 3        |<NULL> | bar3x

Lưu ý cách làm col1NULL tại đây.
Sử dụng trong truy vấn để hiển thị một phiên bản với các giá trị mới nhất của nó:

SELECT i.unique_id
     , COALESCE(c.col1, i.col1)
     , COALESCE(c.col2, i.col2)
FROM   instances i
LEFT   JOIN f_curr_instance(3) c USING (unique_id)
WHERE  i.unique_id = 3;

Tự động hóa hoàn toàn cho bất kỳ bảng nào

(Đã thêm năm 2016. Đây là thuốc nổ.)
Yêu cầu Postgres 9.1 hoặc sau đó. (Có thể được tạo ra để hoạt động với trang 8,4, nhưng tôi không bận tâm đến việc vá lại.)

CREATE OR REPLACE FUNCTION f_curr_instance(_id int, INOUT _t ANYELEMENT) AS
$func$
DECLARE
   _type text := pg_typeof(_t);
BEGIN
   EXECUTE
   (
   SELECT format
         ($f$
         SELECT *
         FROM   crosstab(
            $x$
            SELECT DISTINCT ON (1,2)
                   unique_id, column_name, value
            FROM   changes
            WHERE  table_name = %1$L
            AND    unique_id  = %2$s
            ORDER  BY 1, 2, updated_at DESC;
            $x$    
          , $y$
            SELECT attname
            FROM   pg_catalog.pg_attribute
            WHERE  attrelid = %1$L::regclass
            AND    attnum > 0
            AND    NOT attisdropped
            AND    attname <> 'unique_id'
            ORDER  BY attnum
            $y$) AS ct (%3$s)
         $f$
          , _type, _id
          , string_agg(attname || ' ' || atttypid::regtype::text
                     , ', ' ORDER BY attnum)  -- must be in order
         )
   FROM   pg_catalog.pg_attribute
   WHERE  attrelid = _type::regclass
   AND    attnum > 0
   AND    NOT attisdropped
   )
   INTO _t;
END
$func$  LANGUAGE plpgsql;

Gọi (cung cấp loại bảng với NULL::public.instances :

SELECT * FROM f_curr_instance(3, NULL::public.instances);

Có liên quan:

  • Cấu trúc lại một hàm PL / pgSQL để trả về kết quả đầu ra của các truy vấn SELECT khác nhau
  • Cách đặt giá trị của trường biến tổng hợp bằng SQL động



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Cơ chế sao chép vật lý trong PostgreSQL

  2. Không có kết quả trả về do lỗi Truy vấn trong PostgreSQL

  3. Làm thế nào để sử dụng kiểu dữ liệu Postgres JSONB với JPA?

  4. Nhập tệp .sql trên windows vào postgresql

  5. Làm thế nào để có được ngày hôm qua trong PostgreSQL