CREATETABLE users ( name TEXT PRIMARY KEY, age INTEGERNOTNULL ); INSERTINTO users (name, age) values ('foo', 20);
接著 insert one record and update one record:
1 2 3 4 5 6 7 8 9
INSERTINTO users (name, age) values ('foo', 30), ('bar', 40) ON CONFLICT (name) DO UPDATESET age = EXCLUDED.age RETURNING *, (xmax =0 ) AS inserted;
name | age | inserted ------+-----+---------- foo |30| f bar |40| t (2rows)
關鍵在於透過 (xmax = 0) 可以判斷出該 row 是 insert 還是 update:
The identity (transaction ID) of the deleting transaction, or zero for an undeleted row version. It is possible for this column to be nonzero in a visible row version. That usually indicates that the deleting transaction hasn’t committed yet, or that an attempted deletion was rolled back.
當 PostgreSQL update a record 實際的行為是為該 record 再額外建立一個新版本 (每個 record 的版本通常又稱為 tuple),舊版本會跟著 auto vacuum 而刪掉,而 xmax 的作用:
CREATETABLE sale ( id INT GENERATED ALWAYS ASIDENTITYPRIMARY KEY, sold_at TIMESTAMPTZ, amount INT );
postgres=# INSERTINTO sale (sold_at, amount) VALUES (now(), 1000); INSERT01
postgres=# INSERTINTO sale (id, sold_at, amount) VALUES (2, now(), 1000); ERROR: cannot insertintocolumn "id" DETAIL: Column "id" is an identitycolumn defined as GENERATED ALWAYS. HINT: Use OVERRIDING SYSTEMVALUEto override.
就算設定了 ALWAYS 也可透過 OVERRIDING SYSTEM VALUE 來強制設定。因此最好的情況都是設定 ALWAYS。
Dollar Quoting 來處理特殊字元在字串中
在使用 text column 的時候,裡面的值可能會包含一些 escape characters,像是 single quote,需要用這樣的方式來處理:
postgres=# SELECT $$a long string withnew lines and'single quotes' and "double quotes
PostgreSQL doesn't mind ;)$$ AS text;
既可以處理 escape characters 也能處理多行。
而如果字串需要放 $ 的字元的話那可以使用 Dollar Quoting 搭配 tag 的方式:
1 2 3 4 5 6 7 8 9 10 11
postgres=# SELECT $JSON${ "name": "Kenny's Pizza", "tagline": "Best value for your $$" }$JSON$ AS json; json ----------------------------------------- { + "name": "Kenny's Pizza", + "tagline": "Best value for your $$"+ } (1row)
也能透過這樣的方式產生 jsonb objects
1 2 3 4 5 6 7 8
postgres=# SELECT $JSON${ "name": "Kenny's Pizza", "tagline": "Best value for your $$" }$JSON$::jsonb AS json; json ---------------------------------------------------------------- {"name": "Kenny's Pizza", "tagline": "Best value for your $$"} (1row)
postgres=# COMMENT ONTABLE sale IS'Sales made in the system';
透過 \dt+ 來看到 comment 的內容:
1 2 3 4 5 6
postgres=# \dt+ sale List of relations Schema | Name | Type | Owner | Persistence | Access method| Size | Description --------+------+-------+----------+-------------+---------------+------------+-------------------------- public | sale |table| postgres | permanent | heap |8192 bytes | Sales made in the system (1row)
也可以對 table 上的 column 下註解:
1 2 3 4 5 6 7 8 9
postgres=# COMMENT ONCOLUMN sale.sold_at IS'When was the sale finalized'; COMMENT
postgres=# \d+ sale Column │ Type │ Description ──────────┼──────────────────────────┼───────────────────────────── id │ integer │ sold_at │ timestampwithtime zone │ When was the sale finalized amount │ integer
postgres=# \df+ generate_random_string List of functions -[ RECORD 1 ]-------+--------------------------------------------------------------------------------------------------------------- Schema | public Name | generate_random_string Result data type | text Argument data types | length integer, characters text DEFAULT'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'::text Type | func Volatility | volatile Parallel | unsafe Owner | postgres Security | invoker Access privileges | Language| plpgsql Internal name | Description |+ | Generate a random string at a given length from a list of possible characters. + |+ | Parameters: + |+ |- length (int): length of the output string + |- characters (text): possible characters to choose from+ |+ | Example: + |+ | postgres=# SELECT generate_random_string(10); + | generate_random_string + | ──────────────────────── + | o0QsrMYRvp + |+ | postgres=# SELECT generate_random_string(3, 'AB'); + | generate_random_string + | ──────────────────────── + | ABB + |
postgres=# SELECT random() AS random_float, ceil(random() *10) AS random_int_0_10, '2023-01-01'::date+interval'1 days'*ceil(random() *365) AS random_day_in_2023;
-[ RECORD 1 ]------+-------------------- random_float |0.5202183827443916 random_int_0_10 |6 random_day_in_2023 |2023-11-2700:00:00
WITH new_meetings AS (/* ... */) SELECT * FROM meetings, new_meetings WHERE (new_meetings.starts_at > meetings.starts_at AND new_meetings.starts_at < meetings.ends_at) OR (new_meetings.ends_at > meetings.starts_at AND new_meetings.ends_at < meetings.ends_at) OR (meetings.starts_at > new_meetings.starts_at AND meetings.starts_at < new_meetings.ends_at) OR (meetings.ends_at > new_meetings.starts_at AND meetings.ends_at < new_meetings.ends_at) OR (meetings.starts_at = new_meetings.starts_at AND meetings.ends_at = new_meetings.ends_at);
starts_at │ ends_at │ id │ starts_at │ ends_at ─────────────────────┼─────────────────────┼────┼─────────────────────┼──────────────────── 2021-10-0111:15:00 │ 2021-10-0112:00:00 │ A │ 2021-10-0111:10:00 │ 2021-10-0111:55:00 2021-10-0111:15:00 │ 2021-10-0112:00:00 │ B │ 2021-10-0111:20:00 │ 2021-10-0112:05:00 2021-10-0111:15:00 │ 2021-10-0112:00:00 │ C │ 2021-10-0111:20:00 │ 2021-10-0111:55:00 2021-10-0111:15:00 │ 2021-10-0112:00:00 │ D │ 2021-10-0111:10:00 │ 2021-10-0112:05:00 2021-10-0111:15:00 │ 2021-10-0112:00:00 │ E │ 2021-10-0111:15:00 │ 2021-10-0112:00:00