这是Cluster
表:
╭────╥────────────┬─────────────╮
│ id ║ name │ prefix │
╞════╬════════════╪═════════════╡
│ 1 ║ Yard work │ YA │
│ 2 ║ Construc.. │ CR │
└────╨────────────┴─────────────┘
name
和prefix
都具有唯一性和非空约束。
现在我们到达一个材质表,其中每一行都有一个集群的外键。
╭────╥──────────────┬─────────────╮
│ id ║ cluster_id │ name │
╞════╬══════════════╪═════════════│
│ 1 ║ 1 │ Shovel │
│ 2 ║ 1 │ Lawn mower │
└────╨──────────────┴─────────────┘
我们希望为每种材料提供另一种唯一标识符(甚至可能是主键),我无法弄清楚如何编写序列:
W
)prefix
(YA
)W-YA
)找到最后一个值并增加1,这应该最终为5个字符长,并用0填充因此,我们最终会得到^示例,
使用另一个cluster_id,我们最终会以
结束我想这可以通过CREATE SEQUENCE
解决,但我不知道从哪里开始。
请注意,cluster表可以在任何给定时刻接收新行。但是,行既不能改变也不能删除。
无法删除cluster_materials
的行,并且可以更改 NOT 的cluster_id。
UPDATE:序列不是这里的方式,因为我需要保证数字无间隙增加,而序列不提供。
更新2: Gapless Sequences for Primary Keys确实描述了如何实现无间隙密钥,我认为可以对其进行修改以满足我的需求。然而,如果插入失败,这似乎就像计数增加一样会爆炸,但是从未插入行(比如因为它没有通过所有约束。)我想这可以通过事务解决。
更新3:我在这个小提琴中慢慢取得进展:http://sqlfiddle.com/#!15/791ed/2
更新4:最新进展。现在这很好用。但它并没有进行任何锁定,而且我并不确切知道它在并发插入过程中是如何工作的(这不是一个问题,但锁定可能有助于防止将来出现任何意外问题。){{3 }}
答案 0 :(得分:1)
如果性能根本不是问题,那么我建议您遵循以下解决方案:
架构:
create table cluster (
id bigint primary key,
name text not null unique,
prefix text not null unique
);
create table material (
id text primary key,
cluster_id bigint not null references cluster,
name text not null
);
群集的一些数据:
insert into cluster (id, name, prefix)
values
(1, 'Yard work', 'YW'),
(2, 'Construc..', 'CR');
添加材料的存储过程:
create or replace function add_material(
p_cluster_id bigint,
p_name text
) returns text as
$body$
-- for gapless ids
-- prevents concurrent updates and inserts
-- release on commit or rollback
lock table material in exclusive mode;
insert into material (id, cluster_id, name)
select
'W-' || c.prefix
|| '-'
|| lpad(
(
select coalesce(max(substring(m.id from '.....$')::integer) + 1, 1)
from material m
where m.cluster_id = c.id
)::text,
5,
'0'
) id,
c.id cluster_id,
p_name as "name"
from cluster c
where c.id = p_cluster_id
returning id;
$body$
language sql volatile;
使用示例:
select add_material(1, 'test1');
结果:W-YW-00001
select add_material(1, 'test2');
结果:W-YW-00002
select add_material(2, 'test3');
结果:W-CR-00001
要提高select max(...)
的效果,您可以在material (cluster_id, substring(m.id from '.....$')::integer)
答案 1 :(得分:-1)
CREATE TABLE clus (
id integer not null primary key
, name varchar UNIQUE
, prefix varchar(2) UNIQUE
);
INSERT INTO clus(id, name, prefix)VALUES
(1,'Yard work','YA' ), (2,'Construc..', 'CR' );
-- Both name and prefix have uniqueness and non-null constraints.
-- Now we get to a material table where each row has a foreign key to a cluster.
CREATE TABLE mat (
id integer not null
, cluster_id integer not null REFERENCES clus(id)
, name varchar UNIQUE
, PRIMARY KEY(id,cluster_id)
);
INSERT INTO mat(id, cluster_id, name)VALUES
( 1 , 1 , 'Shovel' ) ,( 2 , 1 , 'Lawn mower')
,( 5 , 1 , 'fire hose' );
SELECT omg.*
, 'W-' || omg.pfx || '-' || to_char(omg.rnk, 'FM0000') AS wtf
FROM(SELECT c.id AS cid, c.name AS cname, c.prefix AS pfx
,m.id AS mid, m.name AS mname
, rank()over (partition by m.cluster_id ORDER BY m.id) AS rnk
FROM clus c
JOIN mat m ON m.cluster_id = c.id
) omg
;
结果:
CREATE TABLE
INSERT 0 2
CREATE TABLE
INSERT 0 3
cid | cname | pfx | mid | mname | rnk | wtf
-----+-----------+-----+-----+------------+-----+-----------
1 | Yard work | YA | 1 | Shovel | 1 | W-YA-0001
1 | Yard work | YA | 2 | Lawn mower | 2 | W-YA-0002
1 | Yard work | YA | 5 | fire hose | 3 | W-YA-0003
(3 rows)