每组值的自定义序列/自动增量
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
我正在尝试制作一个博客系统,但我 运行 遇到了一个小问题。
简单地说,我的 article
table:
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