如何设置PostgreSQL表字段为自增主键
目录
- 前言
- 设置自增主键
- 去重
- 表复制
前言
- 最近公司的项目遇到多次这样的需求:需要将原表符合条件的记录复制到新表,但原表的主键字段在新表中需转为普通字段。这篇博客主要介绍如何为新表设置自增主键字段。
- 还有一个是如何利用窗口函数将表中某几个字段值重复的记录只保留一条有效,其余重复记录作废或者删除,这个需求也是最近经常遇到
设置自增主键
-
我们经常碰到这样的场景,通过
create table as select xxx from table where xx
需要将原表符合条件的记录复制到新表,值得注意的是这样新创建的表,并没有将表结构复制到新表,原表的主键字段在新表中是一个普通字段 -
首先,先确保当前表中没有主键以及当前要设置主键字段的字段类型。
-
第一步,要在pg数据库中创建一个序列,序列是PostgreSQL实现自增功能的核心数据库对象,用处就是字段值可以设置为由已创建的序列提供,每次序列可以自增生成一个唯一的整数,我们可以设置序列的初始值和步长
-- 序列名称最好以表名_id_sq CREATE SEQUENCE [IF NOT EXISTS] sequence_name[ INCREMENT BY increment ][ START WITH start ][ ... ] -- 其他参数如 MINVALUE, MAXVALUE, CACHE, CYCLE
因此,我们首先要知道当前要设置为主键字段的最大值,如果当前字段的最大值为100,那么序列的初始值就要设为101,步长默认是1,一般不需要设置步长
-- 先查出当前字段的最大值 select max(id) from table_name -- 创建序列,指定初始值和步长 CREATE SEQUENCE table_name_id_seq START WITH 100 INCREMENT BY 1;
当然,如果序列创建错了,也可以直接修改已存在序列地初始值和步长
-- 修改序列初始值为500 ALTER SEQUENCE table_name_id_seq RESTART WITH 500; -- 修改序列步长为5 ALTER SEQUENCE table_name_id_seq INCREMENT BY 5;
-
下一步,就是指定字段的数值来源为已创建的序列,由序列提供字段数值
ALTER TABLE table_name ALTER COLUMN id SET DEFAULT nextval('table_name_id_seq ');
-
最后,为字段添加主键约束即可
ALTER TABLE table_name ADD PRIMARY KEY (id);
去重
-
我们经常碰到的数据库需求就是根据某几个字段,对重复记录去重,只保留一条,其余作废(valid=0)或者删除,在pg数据库,一般我们通过窗口函数ROW_NUMBER()实现这个功能
UPDATE table_name SET valid = '0' WHERE ctid IN (SELECT ctidFROM (SELECTctid,ROW_NUMBER() OVER (PARTITION BY field1, field2, field3 ORDER BY ctid) AS rnFROM table_name) AS ranked_rowsWHERE rn > 1 -- 将过滤条件放在正确的外层 );
-
这段sql核心是通过窗口函数
ROW_NUMBER()
将同组(重复记录集合)的记录编号,然后保留编号最小的一个,编号>1的记录作废来实现去重的目的SELECT ctid, ROW_NUMBER() OVER (PARTITION BY field1, field2, field3 ORDER BY ctid) AS rn FROM table_name
-
ROW_NUMBER() OVER (...) AS rn:
这行代码是核心。ROW_NUMBER()是一个窗口函数,它会为每一行分配一个唯一的序号 -
PARTITION BY field1, field2, field3
:PARTITION BY
子句定义了如何将数据分组。这里表示将 field1, field2, field3这三个字段完全相同的记录归为同一组。窗口函数会在每个组内独立进行编号 -
ORDER BY ctid
: 这指定了在每个分组内,记录按 ctid排序。ctid是 PostgreSQL 系统列,代表行的物理位置(文件号和页内的元组索引),可以理解为每行的唯一物理标识。按此排序意味着在同一分组内,先插入的行(理论上ctid更小)会获得更小的行号
表复制
- 仅复制表结构(不复制数据)
-- 基本用法,复制列定义(包括NOT NULL约束)
CREATE TABLE new_table (LIKE old_table);-- 扩展用法,复制包括索引、约束等更多对象
CREATE TABLE new_table (LIKE old_table INCLUDING ALL);
- 复制表结构及其所有数据
记得要注意,表的约束、索引等并没有复制,需要手动重新添加
-- 标准语法
CREATE TABLE new_table AS SELECT * FROM old_table;-- 简化语法,效果相同
CREATE TABLE new_table AS TABLE old_table;