![](/img/trans.png)
[英]How to create a column that is filled automatically based on a sequence in Postgresql?
[英]PostgreSQL sequence based on another column
可以說我有一張這樣的桌子:
Column | Type | Notes
---------+------------ +----------------------------------------------------------
id | integer | An ID that's FK to some other table
seq | integer | Each ID gets its own seq number
data | text | Just some text, totally irrelevant.
id
+ seq
是一個組合鍵。
我想看到的是:
ID | SEQ | DATA
----+------ +----------------------------------------------
1 | 1 | Quick brown fox, lorem ipsum, lazy dog, etc etc.
1 | 2 | Quick brown fox, lorem ipsum, lazy dog, etc etc.
1 | 3 | Quick brown fox, lorem ipsum, lazy dog, etc etc.
1 | 4 | Quick brown fox, lorem ipsum, lazy dog, etc etc.
2 | 1 | Quick brown fox, lorem ipsum, lazy dog, etc etc.
3 | 1 | Quick brown fox, lorem ipsum, lazy dog, etc etc.
3 | 2 | Quick brown fox, lorem ipsum, lazy dog, etc etc.
3 | 3 | Quick brown fox, lorem ipsum, lazy dog, etc etc.
3 | 4 | Quick brown fox, lorem ipsum, lazy dog, etc etc.
如您所見, id
和seq
的組合是唯一的。
我不確定如何設置我的表(或插入語句?)來做到這一點。 我想插入id
和data
,導致seq
是一個依賴於id
的子序列。
沒問題! 我們要制作兩張桌子, things
和stuff
。 stuff
將是您在問題中描述的表格,而things
是它所指的表格:
CREATE TABLE things (
id serial primary key,
name text
);
CREATE TABLE stuff (
id integer references things,
seq integer NOT NULL,
notes text,
primary key (id, seq)
);
然后things
將使用觸發器進行設置,該觸發器將在每次創建一行時創建一個新序列:
CREATE FUNCTION make_thing_seq() RETURNS trigger
LANGUAGE plpgsql
AS $$
begin
execute format('create sequence thing_seq_%s', NEW.id);
return NEW;
end
$$;
CREATE TRIGGER make_thing_seq AFTER INSERT ON things FOR EACH ROW EXECUTE PROCEDURE make_thing_seq();
現在我們將結束thing_seq_1
, thing_seq_2
等等,等等......
現在另一個stuff
,以便它每次都使用正確的序列:
CREATE FUNCTION fill_in_stuff_seq() RETURNS trigger
LANGUAGE plpgsql
AS $$
begin
NEW.seq := nextval('thing_seq_' || NEW.id);
RETURN NEW;
end
$$;
CREATE TRIGGER fill_in_stuff_seq BEFORE INSERT ON stuff FOR EACH ROW EXECUTE PROCEDURE fill_in_stuff_seq();
這將確保當行進入stuff
時, id
列用於找到正確的序列以調用nextval
。
這是一個演示:
test=# insert into things (name) values ('Joe');
INSERT 0 1
test=# insert into things (name) values ('Bob');
INSERT 0 1
test=# select * from things;
id | name
----+------
1 | Joe
2 | Bob
(2 rows)
test=# \d
List of relations
Schema | Name | Type | Owner
--------+---------------+----------+----------
public | stuff | table | jkominek
public | thing_seq_1 | sequence | jkominek
public | thing_seq_2 | sequence | jkominek
public | things | table | jkominek
public | things_id_seq | sequence | jkominek
(5 rows)
test=# insert into stuff (id, notes) values (1, 'Keychain');
INSERT 0 1
test=# insert into stuff (id, notes) values (1, 'Pet goat');
INSERT 0 1
test=# insert into stuff (id, notes) values (2, 'Family photo');
INSERT 0 1
test=# insert into stuff (id, notes) values (1, 'Redundant lawnmower');
INSERT 0 1
test=# select * from stuff;
id | seq | notes
----+-----+---------------------
1 | 1 | Keychain
1 | 2 | Pet goat
2 | 1 | Family photo
1 | 3 | Redundant lawnmower
(4 rows)
test=#
您可以使用窗口函數來分配您的SEQ
值,例如:
INSERT INTO YourTable
(ID, SEQ, DATA)
SELECT ID, ROW_NUMBER() OVER(PARTITION BY ID ORDER BY DATA), DATA
FROM YourSource
如果seq
反映(或應該反映)插入行的順序,我寧願使用自動填充的timestamp
,並在使用row_number()
選擇行時動態生成序列號:
create table some_table
(
id integer not null,
inserted_at timestamp not null default current_timestamp,
data text
);
要獲取seq
列,您可以執行以下操作:
select id,
row_number() over (partition by id order by inserted_at) as seq,
data
from some_table
order by id, seq;
然而,與使用持久化的seq
列(尤其是在id, seq
上的索引)相比,選擇會慢一些。
如果這成為問題,您可以考慮使用物化視圖,或者添加seq
列,然后定期更新它(出於性能原因,我不會在觸發器中這樣做)。
SQLFiddle 示例: http ://sqlfiddle.com/#!15/db69b/1
只是一個猜測。
INSERT INTO TABLE (ID, SEQ, DATA)
VALUES
(
IDVALUE,
(SELECT max(SEQ) +1 FROM TABLE WHERE ID = IDVALUU),
DATAVALUE
);
這是使用標准 SQL 的簡單方法:
INSERT INTO mytable (id, seq, data)
SELECT << your desired ID >>,
COUNT(*) + 1,
'Quick brown fox, lorem ipsum, lazy dog, etc etc.'
FROM mytable
WHERE id = << your desired ID (same as above) >>;
請參閱SQL Fiddle 演示。
(如果你想更聰明一點,你可以考慮創建一個觸發器,在插入后立即使用相同的方法更新行。)
我同樣需要動態存儲樹狀結構,而不是一次添加所有 ID。
我不喜歡為每個組使用序列表,因為可能有數千個。
它在密集的多處理環境中運行,因此它必須是競爭條件證明的。
這里是第一級的插入函數。 其他級別遵循相同的原則。
每個組作為獨立的不可重復使用的順序 ID,該函數接收組名稱和子組名稱,為您提供現有 ID 或創建它並返回新 ID。
我嘗試了一個循環來進行一次選擇,但代碼既長又難讀。
CREATE OR REPLACE FUNCTION getOrInsert(myGroupName TEXT, mySubGroupName TEXT)
RETURNS INT AS
$BODY$
DECLARE
myId INT;
BEGIN -- 1st try to get it if it already exists
SELECT id INTO myId FROM myTable
WHERE groupName=myGroupName AND subGroupName=mySubGroupName;
IF NOT FOUND THEN
-- Only 1 session can get it but others can read
LOCK TABLE myTable IN SHARE ROW EXCLUSIVE MODE;
-- 2nd try in case of race condition
SELECT id INTO myId FROM myTable
WHERE groupName=myGroupName AND subGroupName=mySubGroupName;
IF NOT FOUND THEN -- Doesn't exist. Get next ID for this group.
SELECT COALESCE(MAX(id), 0)+1 INTO myId FROM myTable
WHERE groupName=myGroupName;
INSERT INTO myTable (groupName, id, subGroupName)
VALUES (myGroupName, myId, mySubGroupName);
END IF;
END IF;
RETURN myId;
END;
$BODY$
LANGUAGE plpgsql VOLATILE COST 100;
嘗試一下:
CREATE TABLE myTable (GroupName TEXT, SubGroupName TEXT, id INT);
SELECT getOrInsert('groupA', 'subgroupX'); -- Returns 1
...
SELECT * FROM myTable;
groupname | subgroupname | id
-----------+--------------+----
groupA | subgroupX | 1
groupA | subgroupY | 2
groupA | subgroupZ | 3
groupB | subgroupY | 1
PostgreSQL 支持分組的唯一列,例如:
CREATE TABLE example (
a integer,
b integer,
c integer,
UNIQUE (a, c)
);
我沒有任何特定於 postgresql 的經驗,但是您可以在插入語句中使用子查詢嗎? 類似的東西,在 Mysqlish 中,
INSERT INTO MYTABLE SET
ID=4,
SEQ=( SELECT MAX(SEQ)+1 FROM MYTABLE WHERE ID=4 ),
DATA="Quick brown fox, lorem ipsum, lazy dog, etc etc."
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.