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

Mẹo và thủ thuật của Postgres

Bạn có làm việc với Postgres hàng ngày không? Viết mã ứng dụng talksto Postgres? Sau đó, hãy xem các đoạn mã SQL nhỏ dưới đây có thể giúp bạn làm việc nhanh hơn!

Chèn nhiều hàng trong một câu lệnh

Câu lệnh INSERT có thể chèn nhiều hàng trong một câu lệnh:

INSERT INTO planets (name, gravity)
     VALUES ('earth',    9.8),
            ('mars',     3.7),
            ('jupiter', 23.1);

Đọc thêm về những gì INSERT có thể làm tại đây.

Chèn một hàng và trả về các giá trị được chỉ định tự động

Các giá trị được tạo tự động với cấu trúc DEFAULT / serial / IDENTITY có thể được trả về bằng câu lệnh INSERT sử dụng mệnh đề RETURNING. Từ góc độ mã ứng dụng, một INSERT như vậy được thực thi giống như một SELECT trả về tập các từ.

-- table with 2 column values auto-generated on INSERT
CREATE TABLE items (
    slno       serial      PRIMARY KEY,
    name       text        NOT NULL,
    created_at timestamptz DEFAULT now()
);

INSERT INTO items (name)
     VALUES ('wooden axe'),
            ('loom'),
            ('eye of ender')
  RETURNING name, slno, created_at;

-- returns:
--      name     | slno |          created_at
-- --------------+------+-------------------------------
--  wooden axe   |    1 | 2020-08-17 05:35:45.962725+00
--  loom         |    2 | 2020-08-17 05:35:45.962725+00
--  eye of ender |    3 | 2020-08-17 05:35:45.962725+00

Khóa chính UUID được tạo tự động

UUID đôi khi được sử dụng thay vì khóa chính vì nhiều lý do khác nhau. Đây là cách bạn có thể sử dụng UUID thay vì serial hoặc IDENTITY:

CREATE EXTENSION IF NOT EXISTS "uuid-ossp";

CREATE TABLE items (
    id    uuid DEFAULT uuid_generate_v4(),
    name  text NOT NULL
);

INSERT INTO items (name)
     VALUES ('wooden axe'),
            ('loom'),
            ('eye of ender')
  RETURNING id, name;
  
-- returns:
--                   id                  |     name
-- --------------------------------------+--------------
--  1cfaae8c-61ff-4e82-a656-99263b7dd0ae | wooden axe
--  be043a89-a51b-4d8b-8378-699847113d46 | loom
--  927d52eb-c175-4a97-a0b2-7b7e81d9bc8e | eye of ender

Chèn nếu không tồn tại, cập nhật nếu không

Trong Postgres 9.5 trở lên, bạn có thể nâng cấp trực tiếp sử dụng cấu trúc BẬT MỨC:

CREATE TABLE parameters (
    key   TEXT PRIMARY KEY,
    value TEXT
);

-- when "key" causes a constraint violation, update the "value"
INSERT INTO parameters (key, value) 
     VALUES ('port', '5432')
ON CONFLICT (key) DO
            UPDATE SET value=EXCLUDED.value;

Sao chép hàng từ bảng này sang bảng khác

Câu lệnh INSERT có dạng trong đó các giá trị có thể được cung cấp bởi một câu lệnh SELECT. Sử dụng công cụ này để sao chép các hàng từ bảng này sang bảng khác:

-- copy between tables with similar columns 
  INSERT INTO pending_quests
SELECT * FROM quests
        WHERE progress < 100;

-- supply some values from another table, some directly
  INSERT INTO archived_quests
       SELECT now() AS archival_date, *
         FROM quests
        WHERE completed;

Nếu bạn đang muốn tải hàng loạt bảng, hãy xem lệnh COPY, lệnh này có thể được sử dụng để chèn các hàng từ tệp văn bản hoặc tệp CSV.

Xóa và trả lại thông tin đã xóa

Bạn có thể sử dụng RETURNING mệnh đề để trả về giá trị từ các hàng đã bị xóa bằng cách sử dụng câu lệnh xóa hàng loạt:

-- return the list of customers whose licenses were deleted after expiry
DELETE FROM licenses
      WHERE now() > expiry_date
  RETURNING customer_name;

Di chuyển hàng từ bảng này sang bảng khác

Bạn có thể di chuyển các hàng từ bảng này sang bảng khác trong một câu lệnh bằng cách sử dụng CTEs với DELETE .. RETURNING :

-- move yet-to-start todo items from 2020 to 2021
WITH ah_well AS (
    DELETE FROM todos_2020
          WHERE NOT started
      RETURNING *
)
INSERT INTO todos_2021
            SELECT * FROM ah_well;

Cập nhật hàng và trả lại giá trị đã cập nhật

Mệnh đề RETURNING cũng có thể được sử dụng trong UPDATE. Lưu ý rằng chỉ các giá trị mới của các cột được cập nhật mới có thể được trả về theo cách này.

-- grant random amounts of coins to eligible players
   UPDATE players
      SET coins = coins + (100 * random())::integer
    WHERE eligible
RETURNING id, coins;

Nếu bạn cần giá trị ban đầu của các cột được cập nhật:có thể thông qua aself-join, nhưng không có gì đảm bảo về tính nguyên tử. Thử sử dụng SELECT .. FOR UPDATE thay vào đó.

Cập nhật một vài hàng ngẫu nhiên và trả lại các hàng đã cập nhật

Dưới đây là cách bạn có thể chọn một vài hàng ngẫu nhiên từ bảng, cập nhật chúng và quay lại những hàng đã cập nhật, tất cả chỉ trong một lần:

WITH lucky_few AS (
    SELECT id
      FROM players
  ORDER BY random()
     LIMIT 5
)
   UPDATE players
      SET bonus = bonus + 100 
    WHERE id IN (SELECT id FROM lucky_few)
RETURNING id;

Tạo một bảng giống như một bảng khác

Sử dụng cấu trúc CREATE TABLE .. LIKE để tạo một bảng có các cột giống như một cột khác:

CREATE TABLE to_be_audited (LIKE purchases);

Theo mặc định, điều này không tạo ra các chỉ mục, ràng buộc, giá trị mặc định tương tự, v.v. Để làm được điều đó, hãy hỏi Postgres một cách rõ ràng:

CREATE TABLE to_be_audited (LIKE purchases INCLUDING ALL);

Xem cú pháp đầy đủ tại đây.

Trích xuất một tập hợp ngẫu nhiên các hàng vào một bảng khác

Kể từ Postgres 9.5, tính năng TABLESAMPLE có sẵn để trích xuất một mẫu mũi tên từ một bảng. Hiện có hai phương pháp lấy mẫu và bernoulli thường là một trong những bạn muốn:

-- copy 10% of today's purchases into another table
INSERT INTO to_be_audited
     SELECT *
       FROM purchases
TABLESAMPLE bernoulli(10)
      WHERE transaction_date = CURRENT_DATE;

Hệ thống phương thức lấy mẫu bảng nhanh hơn, nhưng không trả về phân phối đồng nhất. Xem tài liệu để biết thêm thông tin.

Tạo bảng từ một truy vấn chọn

Bạn có thể sử dụng cấu trúc CREATE TABLE .. AS để tạo bảng và điền từ truy vấn SELECT, tất cả chỉ trong một lần thực hiện:

CREATE TABLE to_be_audited AS
      SELECT *
        FROM purchases
 TABLESAMPLE bernoulli(10)
       WHERE transaction_date = CURRENT_DATE;

Bảng kết quả giống như một dạng xem hiện thực hóa mà không có một truy vấn liên quan. Đọc thêm về TẠO BẢNG .. NHƯ tại đây.

Tạo bảng chưa đăng ký

Đã đăng ký bảng không được hỗ trợ bởi các bản ghi WAL. Điều này có nghĩa là cập nhật và xóa các bảng như vậy nhanh hơn, nhưng chúng không chịu được sự cố và không thể sao chép.

CREATE UNLOGGED TABLE report_20200817 (LIKE report_v3);

Tạo bảng tạm thời

Tạm thời bảng là bảng được mở khóa ngầm, có thời gian tồn tại ngắn hơn. Chúng tự động tự hủy vào cuối phiên (mặc định) hoặc khi kết thúc giao dịch.

Dữ liệu trong các bảng tạm thời không thể được chia sẻ giữa các phiên. Nhiều phiên có thể tạo các bảng tạm thời có cùng tên.

-- temp table for duration of the session
CREATE TEMPORARY TABLE scratch_20200817_run_12 (LIKE report_v3);

-- temp table that will self-destruct after current transaction
CREATE TEMPORARY TABLE scratch_20200817_run_12
                      (LIKE report_v3)
                      ON COMMIT DROP;

-- temp table that will TRUNCATE itself after current transaction
CREATE TEMPORARY TABLE scratch_20200817_run_12
                       (LIKE report_v3)
                       ON COMMIT DELETE ROWS;

Thêm nhận xét

Chú thích có thể được thêm vào bất kỳ đối tượng nào trong cơ sở dữ liệu. Nhiều công cụ, bao gồmpg_dump, hiểu được những điều này. Một nhận xét hữu ích có thể tránh được rất nhiều rắc rối trong quá trình dọn dẹp!

COMMENT ON INDEX idx_report_last_updated
        IS 'needed for the nightly report app running in dc-03';

COMMENT ON TRIGGER tgr_fix_column_foo
        IS 'mitigates the effect of bug #4857';

Khóa tư vấn

Khóa tư vấn có thể được sử dụng để phối hợp hành động giữa hai ứng dụng được kết nối với giống nhau cơ sở dữ liệu. Ví dụ, bạn có thể sử dụng tính năng này để triển khai một mạng phân tán, toàn cầu cho một hoạt động nhất định. Đọc tất cả về nó tại đây trong thedocs.

-- client 1: acquire a lock 
SELECT pg_advisory_lock(130);
-- ... do work ...
SELECT pg_advisory_unlock(130);

-- client 2: tries to do the same thing, but mutually exclusive
-- with client 1
SELECT pg_advisory_lock(130); -- blocks if anyone else has held lock with id 130

-- can also do it without blocking:
SELECT pg_try_advisory_lock(130);
-- returns false if lock is being held by another client
-- otherwise acquires the lock then returns true

Tổng hợp vào Mảng, Mảng JSON hoặc Chuỗi

Postgres cung cấp các hàm tổng hợp nối các giá trị trong một GROUP mảng toyield, mảng JSON hoặc chuỗi:

-- get names of each guild, with an array of ids of players that
-- belong to that guild
  SELECT guilds.name AS guild_name, array_agg(players.id) AS players
    FROM guilds
    JOIN players ON players.guild_id = guilds.id
GROUP BY guilds.id;

-- same but the player list is a CSV string
  SELECT guilds.name, string_agg(players.id, ',') -- ...
  
-- same but the player list is a JSONB array
  SELECT guilds.name, jsonb_agg(players.id) -- ...
  
-- same but returns a nice JSONB object like so:
-- { guild1: [ playerid1, playerid2, .. ], .. }
SELECT jsonb_object_agg(guild_name, players) FROM (
  SELECT guilds.name AS guild_name, array_agg(players.id) AS players
    FROM guilds
    JOIN players ON players.guild_id = guilds.id
GROUP BY guilds.id
) AS q;

Tổng hợp với đơn đặt hàng

Trong khi chúng ta đang nói về chủ đề này, đây là cách đặt thứ tự các giá trị được chuyển vào hàm tổng hợp, trong mỗi nhóm :

-- each state with a list of counties sorted alphabetically
  SELECT states.name, string_agg(counties.name, ',' ORDER BY counties.name)
    FROM states JOIN counties
    JOIN states.name = counties.state_name
GROUP BY states.name;

Có, có một mệnh đề ORDER BY ở cuối bên trong tham số lệnh gọi hàm. Vâng, cú pháp khá lạ.

Mảng và Unnest

Sử dụng hàm tạo ARRAY để chuyển đổi một tập hợp các hàng, mỗi hàng có một cột, thành một mảng. Trình điều khiển cơ sở dữ liệu (như JDBC) sẽ có thể ánh xạ các mảng Postgres vào các mảng gốc và có thể dễ làm việc hơn.

-- convert rows (with 1 column each) into a 1-dimensional array
SELECT ARRAY(SELECT id FROM players WHERE lifetime_spend > 10000);

Hàm unnest thực hiện ngược lại - nó chuyển đổi từng mục trong một mảng thành arow. Chúng hữu ích nhất trong việc kết hợp chéo với danh sách các giá trị:

    SELECT materials.name || ' ' || weapons.name
      FROM weapons
CROSS JOIN UNNEST('{"wood","gold","stone","iron","diamond"}'::text[])
           AS materials(name);

-- returns:
--     ?column?
-- -----------------
--  wood sword
--  wood axe
--  wood pickaxe
--  wood shovel
--  gold sword
--  gold axe
-- (..snip..)

Kết hợp các Câu lệnh Chọn với Liên minh

Bạn có thể sử dụng cấu trúc UNION để kết hợp các kết quả từ nhiều SELECT tương tự:

SELECT name FROM weapons
UNION
SELECT name FROM tools
UNION
SELECT name FROM materials;

Sử dụng CTE để xử lý thêm kết quả tổng hợp:

WITH fight_equipment AS (
    SELECT name, damage FROM weapons
    UNION
    SELECT name, damage FROM tools
)
  SELECT name, damage
    FROM fight_equipment
ORDER BY damage DESC
   LIMIT 5;

Ngoài ra còn có các cấu trúc INTERSECT và EXCEPT, trong cùng mạch với UNION. Đọc thêm về các điều khoản này trong thedocs.

Sửa nhanh trong Chọn:case, merge và nullif

CASE, COALESCE và NULLIF để thực hiện các "bản sửa lỗi" nhỏ nhanh chóng cho dữ liệu đã CHỌN.CASE giống như chuyển đổi bằng các ngôn ngữ giống C:

SELECT id,
       CASE WHEN name='typ0' THEN 'typo' ELSE name END
  FROM items;
  
SELECT CASE WHEN rating='G'  THEN 'General Audiences'
            WHEN rating='PG' THEN 'Parental Guidance'
            ELSE 'Other'
       END
  FROM movies;

COALESCE có thể được sử dụng để thay thế một giá trị nhất định thay vì NULL.

-- use an empty string if ip is not available
SELECT nodename, COALESCE(ip, '') FROM nodes;

-- try to use the first available, else use '?'
SELECT nodename, COALESCE(ipv4, ipv6, hostname, '?') FROM nodes;

NULLIF hoạt động theo cách khác, cho phép bạn sử dụng NULL thay vì một giá trị nhất định:

-- use NULL instead of '0.0.0.0'
SELECT nodename, NULLIF(ipv4, '0.0.0.0') FROM nodes;

Tạo dữ liệu kiểm tra ngẫu nhiên và tuần tự

Các phương pháp tạo dữ liệu ngẫu nhiên khác nhau:

-- 100 random dice rolls
SELECT 1+(5 * random())::int FROM generate_series(1, 100);

-- 100 random text strings (each 32 chars long)
SELECT md5(random()::text) FROM generate_series(1, 100);

-- 100 random text strings (each 36 chars long)
SELECT uuid_generate_v4()::text FROM generate_series(1, 100);

-- 100 random small text strings of varying lengths
CREATE EXTENSION IF NOT EXISTS "pgcrypto";
SELECT gen_random_bytes(1+(9*random())::int)::text
  FROM generate_series(1, 100);

-- 100 random dates in 2019
SELECT DATE(
         DATE '2019-01-01' + ((random()*365)::int || ' days')::interval
       )
  FROM generate_series(1, 100);
  
-- 100 random 2-column data: 1st column integer and 2nd column string
WITH a AS (
  SELECT ARRAY(SELECT random() FROM generate_series(1,100))
),
b AS (
  SELECT ARRAY(SELECT md5(random()::text) FROM generate_series(1,100))
)
SELECT unnest(i), unnest(j)
  FROM a a(i), b b(j);

-- a daily count for 2020, generally increasing over time
SELECT i, ( (5+random()) * (row_number() over()) )::int
  FROM generate_series(DATE '2020-01-01', DATE '2020-12-31', INTERVAL '1 day')
       AS s(i);

Sử dụng bernoulli lấy mẫu bảng để chọn một số hàng ngẫu nhiên từ một bảng:

-- select 15% of rows from the table, chosen randomly  
     SELECT *
       FROM purchases
TABLESAMPLE bernoulli(15)

Sử dụng generate_series để tạo các giá trị tuần tự của số nguyên, ngày tháng và các loại tích hợp có thể tăng dần khác:

-- generate integers from 1 to 100
SELECT generate_series(1, 100);

-- call the generated values table as "s" with a column "i", to use in
-- CTEs and JOINs
SELECT i FROM generate_series(1, 100) AS s(i);

-- generate multiples of 3 in different ways
SELECT 3*i FROM generate_series(1, 100) AS s(i);
SELECT generate_series(1, 100, 3);

-- works with dates too: here are all the Mondays in 2020:
SELECT generate_series(DATE '2020-01-06', DATE '2020-12-31', INTERVAL '1 week');

Nhận Số lượng Hàng Gần đúng

Hiệu suất khủng khiếp của COUNT(*) có lẽ là sản phẩm phụ xấu xí nhất của kiến ​​trúcPostgres. Nếu bạn chỉ cần số hàng gần đúng cho một hàng có thể thu thập, bạn có thể tránh được COUNT đầy đủ bằng cách truy vấn trình thu thập thống kê:

SELECT relname, n_live_tup FROM pg_stat_user_tables;

Kết quả chính xác sau một PHÂN TÍCH và sẽ không chính xác dần khi các hàng được sửa đổi. Không sử dụng điều này nếu bạn muốn số lượng chính xác.

Loại Khoảng thời gian

Khoảng thời gian loại không chỉ có thể được sử dụng làm kiểu dữ liệu cột mà có thể được thêm vào và trừ đi từ ngày dấu thời gian giá trị:

-- get licenses that expire within the next 7 days
SELECT id
  FROM licenses
 WHERE expiry_date BETWEEN now() - INTERVAL '7 days' AND now();
 
-- extend expiry date
UPDATE licenses
   SET expiry_date = expiry_date + INTERVAL '1 year'
 WHERE id = 42;

Tắt Xác thực Ràng buộc Đối với Chèn Hàng loạt

-- add a constraint, set as "not valid"
ALTER TABLE players
            ADD CONSTRAINT fk__players_guilds
                           FOREIGN KEY (guild_id)
                            REFERENCES guilds(id)
            NOT VALID;

-- insert lots of rows into the table
COPY players FROM '/data/players.csv' (FORMAT CSV);

-- now validate the entire table
ALTER TABLE players
            VALIDATE CONSTRAINT fk__players_guilds;

Kết xuất Bảng hoặc Truy vấn vào Tệp CSV

-- dump the contents of a table to a CSV format file on the server
COPY players TO '/tmp/players.csv' (FORMAT CSV);

-- "header" adds a heading with column names
COPY players TO '/tmp/players.csv' (FORMAT CSV, HEADER);

-- use the psql command to save to your local machine
\copy players TO '~/players.csv' (FORMAT CSV);

-- can use a query instead of a table name
\copy ( SELECT id, name, score FROM players )
      TO '~/players.csv'
      ( FORMAT CSV );

Sử dụng nhiều loại dữ liệu gốc hơn trong thiết kế giản đồ của bạn

Postgres đi kèm với nhiều kiểu dữ liệu tích hợp sẵn. Việc trình bày dữ liệu mà bạn cần ứng dụng bằng một trong các loại này có thể tiết kiệm nhiều mã ứng dụng, giúp bạn phát triển nhanh hơn và dẫn đến ít lỗi hơn.

Ví dụ:nếu bạn đang đại diện cho vị trí của một người bằng loại dữ liệu point và vùng quan tâm dưới dạng polygon , bạn có thể kiểm tra xem các nhân vật trong khu vực chỉ bằng:

-- the @> operator checks if the region of interest (a "polygon") contains
-- the person's location (a "point")
SELECT roi @> person_location FROM live_tracking;

Dưới đây là một số kiểu dữ liệu Postgres thú vị và các liên kết đến nơi bạn có thể tìm thêm thông tin về chúng:

  • Các loại enum giống C
  • Các loại hình học - điểm, hộp, đoạn thẳng, đường thẳng, đường dẫn, đa giác, hình tròn
  • Địa chỉ IPv4, IPv6 và MAC
  • Các loại phạm vi - phạm vi số nguyên, ngày tháng và dấu thời gian
  • Mảng có thể chứa các giá trị thuộc bất kỳ loại nào
  • UUID - nếu bạn cần sử dụng UUID hoặc chỉ cần làm việc với các số nguyên ngẫu nhiên 129 byte, hãy xem xét sử dụng uuid nhập và uuid-oscp xây dựng phần mở rộng, tạo và định dạng UUIDs
  • Khoảng ngày và giờ sử dụng loại INTERVAL
  • và tất nhiên là JSON và JSONB ngày càng phổ biến

Tiện ích mở rộng được đóng gói

Hầu hết các lượt cài đặt Postgres đều bao gồm một loạt các “tiện ích mở rộng” tiêu chuẩn. Các phần mở rộng có thể cài đặt được (và có thể gỡ cài đặt một cách sạch sẽ) cung cấp chức năng không có trong lõi. Chúng có thể được cài đặt trên cơ sở từng cơ sở dữ liệu.

Một số trong số này khá hữu ích và bạn nên dành thời gian tìm hiểu chúng:

  • pg_stat_statements - thống kê theo dõi việc thực thi từng truy vấn SQL
  • auto_explain – ghi lại kế hoạch thực thi truy vấn của các truy vấn (chậm)
  • postgres_fdw, dblink vàfile_fdw - các cách để truy cập các nguồn dữ liệu khác (như máy chủ Postgres từ xa, máy chủ MySQL, tệp trên hệ thống tệp của máy chủ) như các bảng thông thường
  • citext - kiểu dữ liệu “văn bản không phân biệt chữ hoa chữ thường”, hiệu quả hơn so với low () - ăn nhập khắp nơi
  • hstore - một loại dữ liệu có giá trị khóa
  • pgcrypto –SHA hàm băm, mã hóa

  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Django-DB-Migrations:không thể ALTER TABLE vì nó có các sự kiện kích hoạt đang chờ xử lý

  2. Xuất truy vấn PostgreSQL sang tệp csv bằng Python

  3. Cách cập nhật nhiều cột trong PostgreSQL

  4. Khi autovacuum không hút chân không

  5. Làm cách nào để đặt cơ sở dữ liệu dưới quyền git (kiểm soát phiên bản)?