每组值的自定义序列/自动增量

Custom SERIAL / autoincrement per group of values

我正在尝试制作一个博客系统,但我 运行 遇到了一个小问题。

简单地说,我的 article table:

中有 3 列
id SERIAL,
category VARCHAR FK,
category_id INT

id 列显然是 PK,它被用作所有文章的全局标识符。

category 专栏很好..类别。

category_id 用作类别中的 UNIQUE ID,因此当前存在 UNIQUE(category, category_id) 约束。

不过,我也想要category_id自动递增

我想要它,以便每次执行

这样的查询时
INSERT INTO article(category) VALUES ('Whosebug');

我希望 category_id 列根据 'Whosebug' 类别的最新 category_id 自动填充。

在我的逻辑代码中实现这一点非常容易。我只是 select latest num 并插入其中的 +1,但这涉及两个单独的查询。 我正在寻找一种 SQL 可以在一个查询中完成所有这些的解决方案。

Postgresql 使用序列来实现这一点;这与您在 MySQL 中习惯的方法不同。查看 http://www.postgresql.org/docs/current/static/sql-createsequence.html 以获取完整参考。

基本上,您通过以下方式创建序列(数据库对象):

CREATE SEQUENCE serials;

然后当您想要添加到您的 table 时,您将拥有:

INSERT INTO mytable (name, id) VALUES ('The Name', NEXTVAL('serials')

概念

至少有几种方法可以解决这个问题。我想到的第一个:

通过覆盖 INSERT 语句中的输入值,为每一行执行的触发器内的 category_id 列分配一个值。

动作

这里是 SQL Fiddle 以查看实际代码


为了进行简单测试,我正在创建 article table 馆藏类别及其 id 类别,每个 category 类别应该是唯一的。我省略了约束创建 - 这与提出要点无关。

create table article ( id serial, category varchar, category_id int )

正在使用 generate_series() 函数为两个不同的类别插入一些值,以使 auto-increment 已经到位。

insert into article(category, category_id)
  select 'Whosebug', i from generate_series(1,1) i
  union all
  select 'stackexchange', i from generate_series(1,3) i

创建一个触发器函数,它将 select MAX(category_id) 并将其值增加 1 对于 category 我们插入一行然后覆盖在继续使用实际 INSERT 到 table 之前的值(BEFORE INSERT 触发器负责)。

CREATE OR REPLACE FUNCTION category_increment()
RETURNS trigger
LANGUAGE plpgsql
AS
$$
DECLARE
  v_category_inc int := 0;
BEGIN
  SELECT MAX(category_id) + 1 INTO v_category_inc FROM article WHERE category = NEW.category;
  IF v_category_inc is null THEN
    NEW.category_id := 1;
  ELSE
    NEW.category_id := v_category_inc;
  END IF;
RETURN NEW;
END;
$$ 

将函数用作触发器。

CREATE TRIGGER trg_category_increment 
  BEFORE INSERT ON article 
  FOR EACH ROW EXECUTE PROCEDURE category_increment()

为现有类别和 non-existing 插入更多值(post 触发设备)。

INSERT INTO article(category) VALUES 
  ('Whosebug'),
  ('stackexchange'),
  ('nonexisting');

查询用于select数据:

select category, category_id From article order by 1,2

初始 插入的结果:

category    category_id
stackexchange   1
stackexchange   2
stackexchange   3
Whosebug   1

最终插入后的结果:

category    category_id
nonexisting     1
stackexchange   1
stackexchange   2
stackexchange   3
stackexchange   4
Whosebug   1
Whosebug   2

这个问题已经被问过很多次了,一般的想法是 在 multi-user 环境中肯定会失败 - 博客系统听起来就是这样。

所以最佳答案是:不要。考虑不同的方法。

从您的 table 中完全删除 category_id 列 - 它不存储其他两列 (id, category) 不会存储的任何信息已经存储了。

您的 id 是一个 serial 专栏,并且已经 auto-increments 可靠。

  • Auto increment SQL function

如果您需要某种 category_id 且每个 category 没有间隙,请使用 row_number():[=19 即时生成它=]

  • Serial numbers per group of rows for compound key