每组值自定义SERIAL / autoincrement

时间:2016-01-02 21:54:41

标签: sql postgresql database-design auto-increment

我试图建立一个排序的博客系统,但我遇到了一个小问题。

简单地说,我的article表中有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 ('stackoverflow');

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

在我的逻辑代码中实现这一点非常简单。我只选择最新的num并插入+1,但这涉及两个单独的查询。 我正在寻找一个可以在一个查询中完成所有这些操作的SQL解决方案。

3 个答案:

答案 0 :(得分:5)

概念

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

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

动作

在这里 SQL Fiddle 查看实际操作中的代码

对于简单的测试,我创建了article表格保留类别及其id个,这些类别对于每个category都应该是唯一的。我省略了约束创建 - 这与提出要点无关。

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

使用generate_series()函数为两个不同的类别插入一些值,以实现自动增量。

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

创建一个触发器函数,该函数将选择MAX(category_id)并为1增加category的值,我们会在移动之前插入一行,然后在移动之前覆盖该值使用实际INSERT到表格(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()

为现有类别和不存在的类别插入更多值(后触发设备)。

INSERT INTO article(category) VALUES 
  ('stackoverflow'),
  ('stackexchange'),
  ('nonexisting');
用于选择数据的

查询

select category, category_id From article order by 1,2

初始插入的结果:

category    category_id
stackexchange   1
stackexchange   2
stackexchange   3
stackoverflow   1

最终插入后的结果:

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

答案 1 :(得分:5)

这已被多次询问,并且一般的想法在多用户环境中一定会失败 - 并且博客系统听起来就是这样的情况。

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

完全从表中删除 category_id 列 - 它不存储其他两列(id, category)已经存储的信息。

您的idserial列,并且已经可靠地自动递增。

如果您需要某种category_id而不是category的差距,请使用row_number()动态生成

答案 2 :(得分:-1)

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

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

CREATE SEQUENCE serials;

然后当你想要添加到你的桌子时,你将拥有:

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