PostgreSQL序列基于另一列

Inc*_*ito 37 sql postgresql

可以说我有一张桌子:

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.
Run Code Online (Sandbox Code Playgroud)

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.
Run Code Online (Sandbox Code Playgroud)

如您所见,组合idseq独特.

我不知道如何设置我的表(或插入语句?)来做到这一点.我想插入,iddata导致seq依赖于子序列id.

Jay*_*nek 22

没问题!我们将让两个表,thingsstuff.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)
);
Run Code Online (Sandbox Code Playgroud)

然后我们将设置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();
Run Code Online (Sandbox Code Playgroud)

现在,我们将结束与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();
Run Code Online (Sandbox Code Playgroud)

这将确保当行进入时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=#
Run Code Online (Sandbox Code Playgroud)

  • `things.id`是PK,但没有什么能阻止我删除和重新插入相同的`id`."UPDATE"也没有涵盖.如果在同一语句中插入父行和子行,则"AFTER"触发器为时已晚.(子表上的触发器"BEFORE"运行.)即使没有,数据修改CTE也会同时虚拟地操作两个表.这有多种方法可能会失败.即使它工作,序列也不保证序列号开始.编号的差距是可以预期的. (3认同)
  • `create sequence IF NOT EXISTS`肯定会解决删除并重新插入主ID可能出现的问题。 (2认同)

Joe*_*lli 17

您可以使用窗口函数来分配SEQ值,例如:

INSERT INTO YourTable
    (ID, SEQ, DATA)
    SELECT ID, ROW_NUMBER() OVER(PARTITION BY ID ORDER BY DATA), DATA
        FROM YourSource
Run Code Online (Sandbox Code Playgroud)

  • 窗口函数非常常见,你可能不会遇到"当它可能不起作用"的情况.查看Joe的回答中的链接.一旦开始使用它们,它将在SQL语句中开辟一个新的可能性世界. (2认同)
  • 如果您打算删除除最后一条记录之外的任何记录,此操作可能会失败。在这种情况下, row_number() 将与最后一条记录冲突。换句话说,在很多非常常见的场景中,这在某些时候都会失败。 (2认同)

a_h*_*ame 5

如果seq反映(或应该反映)插入行的顺序,我宁愿使用timestamp自动填充并在使用row_number()以下方法选择行时动态生成序列号:

create table some_table
( 
  id          integer   not null,
  inserted_at timestamp not null default current_timestamp,
  data text
);
Run Code Online (Sandbox Code Playgroud)

要获取seq列,您可以执行以下操作:

select id,  
       row_number() over (partition by id order by inserted_at) as seq,
       data
from some_table
order by id, seq;
Run Code Online (Sandbox Code Playgroud)

然而,与使用持久seq列(尤其是在 上有索引的情况下id, seq)相比,选择会慢一点。

如果这成为一个问题,您可以考虑使用物化视图,或者添加seq列然后定期更新它(出于性能原因,我不会在触发器中这样做)。

SQLFiddle 示例:http ://sqlfiddle.com/#!15/db69b/1