使用来自foreign_key列的字符串前缀递增序列

时间:2017-08-09 15:48:43

标签: sql postgresql

这是Cluster表:

╭────╥────────────┬─────────────╮
│ id ║  name      │   prefix    │
╞════╬════════════╪═════════════╡
│ 1  ║ Yard work  │ YA          │
│ 2  ║ Construc.. │ CR          │
└────╨────────────┴─────────────┘

nameprefix都具有唯一性和非空约束。

现在我们到达一个材质表,其中每一行都有一个集群的外键。

╭────╥──────────────┬─────────────╮
│ id ║  cluster_id  │ name        │
╞════╬══════════════╪═════════════│
│ 1  ║ 1            │ Shovel      │
│ 2  ║ 1            │ Lawn mower  │
└────╨──────────────┴─────────────┘

我们希望为每种材料提供另一种唯一标识符(甚至可能是主键),我无法弄清楚如何编写序列:

  1. 带有硬编码字母的前缀(此处为W
  2. 从此处获取群集的prefixYA
  3. 鉴于我们现在拥有的内容(W-YA)找到最后一个值并增加1,这应该最终为5个字符长,并用0填充
  4. 因此,我们最终会得到^示例,

    1. W-YA-00001
    2. W-YA-00002
    3. W-YA-00003
    4. 使用另一个cluster_id,我们最终会以

      结束
      1. W-CR-00001
      2. W-CR-00002
      3. 我想这可以通过CREATE SEQUENCE解决,但我不知道从哪里开始。

        请注意,cluster表可以在任何给定时刻接收新行。但是,行既不能改变也不能删除。

        无法删除cluster_materials的行,并且可以更改 NOT 的cluster_id。

        UPDATE:序列不是这里的方式,因为我需要保证数字无间隙增加,而序列不提供。

        更新2: Gapless Sequences for Primary Keys确实描述了如何实现无间隙密钥,我认为可以对其进行修改以满足我的需求。然而,如果插入失败,这似乎就像计数增加一样会爆炸,但是从未插入行(比如因为它没有通过所有约束。)我想这可以通过事务解决。

        更新3:我在这个小提琴中慢慢取得进展:http://sqlfiddle.com/#!15/791ed/2

        更新4:最新进展。现在这很好用。但它并没有进行任何锁定,而且我并不确切知道它在并发插入过程中是如何工作的(这不是一个问题,但锁定可能有助于防止将来出现任何意外问题。){{3 }}

2 个答案:

答案 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)

  • 这是一个可怕的设计;你应该从(连接)表达式中组成(候选)键,而肯定不是来自聚合。
  • 由于您的组合标识符在功能上依赖于实际键,因此可以动态构建(在这种情况下:基于窗口函数)
  • 使用某些特定的ORM不是数据库设计错误的原因。从不。
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)
相关问题