目录
MySQL
Oracle
标识列
序列
SQL Server
标识列
序列
PostgreSQL
标识列
序列
SERIAL
在设计数据库的表结构时,经常会使用一个自动增长的数字序列作为主键字段(代理主键)。
除了作为主键使用之外,自增字段也可以用于记录各个操作发生的先后顺序,因为它具有递增特性。当我们插入一行数据时,数据库会为自增字段生成一个新的数值。
下表列出了主流数据库中创建自增字段的几种方法:
AUTO_INCREMENT
MySQL 通过 AUTO_INCREMENT 属性定义自增字段,并且需要遵循以下规则:
以下语句创建了一个表 users,其中 user_id 是一个自增主键字段:
create table users(
user_id INT AUTO_INCREMENT PRIMARY KEY,
user_name VARCHAR(50) NOT NULL,
email VARCHAR(100)
);
接下来我们插入两条数据:
insert into users(user_name, email) values ('u1', '[email protected]');
insert into users(user_name, email) values ('u2', '[email protected]');
select * from users;
user_id|user_name|email |
-------|---------|-----------|
1|u1 |[email protected]|
2|u2 |[email protected]|
在上面的插入语句中,我们没有指定 user_id 的值,此时 MySQL 会自动为该字段生成一个递增序列值。AUTO_INCREMENT 字段的值默认从 1 开始,每次递增也是 1。
如果插入数据时为自增字段指定了 NULL 值或者 0,MySQL 同样会自动生成一个序列值。
insert into users(user_id, user_name, email) values (null, 'u3', '[email protected]');
insert into users(user_id, user_name, email) values (0, 'u4', '[email protected]');
select * from users;
user_id|user_name|email |
-------|---------|-----------|
1|u1 |[email protected]|
2|u2 |[email protected]|
3|u3 |[email protected]|
4|u4 |[email protected]|
如果插入数据时为自增字段指定了非空也非 0 的值,MySQL 会使用我们提供的值;而且还会将自增序列的起始值值设置为该值,可能导致自增字段值的跳跃。
insert into users(user_id, user_name, email) values (100, 'u5', '[email protected]');
insert into users(user_name, email) values ('u6', '[email protected]');
select * from users;
user_id|user_name|email |
-------|---------|-----------|
1|u1 |[email protected]|
2|u2 |[email protected]|
3|u3 |[email protected]|
4|u4 |[email protected]|
100|u5 |[email protected]|
101|u6 |[email protected]|
上面的第一个插入语句为 user_id 提供了值 100,第二个插入语句使用系统提供的自增序列值,此时跳跃到了 101。
MySQL 提供了 LAST_INSERT_ID 函数,用于获取最后一次生成的序列值。
另外,MySQL 也可以使用ALTER TABLE语句设置自增序列的值:
ALTER TABLE users AUTO_INCREMENT = 200;
insert into users(user_name, email) values ('u7', '[email protected]');
select * from users where user_name = 'u7';
user_id|user_name|email |
-------|---------|-----------|
200|u7 |[email protected]|
最后我们来看一个问题,当自增序列到达最大值之后怎么办。下面的语句演示了这种情况:
ALTER TABLE users AUTO_INCREMENT = 2147483647;
insert into users(user_name, email) values ('u8', '[email protected]');
insert into users(user_name, email) values ('u9', '[email protected]');
SQL Error [1062] [23000]: Duplicate entry '2147483647' for key 'users.PRIMARY'
先将 AUTO_INCREMENT 的值设置为 INT 类型的最大值;然后插入两条数据,第二个插入语句出现主键值重复,意味着自增字段到达最大值之后一直保持不变。
如果担心自增字段的值不够用,可以将 INT 类型改成 INT UNSIGNED,最大值可以到达 4294967295(2的32次方-1);还不够的话改成 BIGINT,最大值可以到达 9223372036854775807(2的63次方-1)。
MySQL 中的 SERIAL 是 BIGINT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE 的同义词。
Oracle 数据库提供了两种创建自增字段的方法:
Oracle 12c 提供创建 SQL 标准定义的标识列功能:
GENERATED [ ALWAYS | BY DEFAULT [ ON NULL ] ]
AS IDENTITY [ ( identity_options ) ]
其中,
Oracle 中的标识列实际上是一个内部创建序列对象,因此 identity_options 与序列的属性类似,主要包括:
以下语句创建了一个表 users,其中 user_id 是一个自增主键字段:
create table users(
user_id NUMBER GENERATED BY DEFAULT AS IDENTITY INCREMENT BY 10 START WITH 100 PRIMARY KEY,
user_name VARCHAR2(50) NOT NULL,
email VARCHAR2(100)
);
其中,INCREMENT BY 10 表示每次增量为 10;START WITH 100 表示序列值从 100 开始。
我们测试一下数据插入:
insert into users(user_name, email) values ('u1', '[email protected]');
insert into users(user_name, email) values ('u2', '[email protected]');
select * from users;
USER_ID|USER_NAME|EMAIL |
-------|---------|-----------|
100|u1 |[email protected]|
110|u2 |[email protected]|
我们没有使用GENERATED BY DEFAULT ON NULL选项,如果插入 NULL 值将会出错:
insert into users(user_id, user_name, email) values (null, 'u3', '[email protected]');
SQL Error [1400] [23000]: ORA-01400: cannot insert NULL into ("TONY"."USERS"."USER_ID")
不过,我们可以为 user_id 指定非空的值:
insert into users(user_id, user_name, email) values (0, 'u4', '[email protected]');
select * from users;
USER_ID|USER_NAME|EMAIL |
-------|---------|-----------|
100|u1 |[email protected]|
110|u2 |[email protected]|
0|u4 |[email protected]|
Oracle 标识列需要遵循以下限制:
序列(Sequence)是数据库中的一种对象,用于生成一系列递增或递减的数字。序列使用CREATE SEQUENCE语句创建:
CREATE SEQUENCE seq_users;
以上语句使用默认选项创建了一个序列 seq_users,等价于下面的语句:
CREATE SEQUENCE seq_users
START WITH 1
INCREMENT BY 1
NOMAXVALUE
NOMINVALUE
CACHE 20
NOCYCLE;
Oracle 序列的数据类型为 NUMBER,包含一个最小值,一个最大值,一个起始值,一个增量值,缓存选项以及一个循环使用选项。这些参数的作用可以参考上面的标识列。
创建之后,我们可以使用 NEXTVAL 和 CURRVAL 伪列获取序列的值:
SELECT seq_users.nextval FROM dual;
NEXTVAL|
-------|
1|
SELECT seq_users.currval FROM dual;
CURRVAL|
-------|
1|
NEXTVAL 用于从序列中获取下一个值,CURRVAL 返回了当前会话最后一次获取的序列值。
利用序列,我们可以为表中的字段生成不重复的数值:
create table users(
user_id NUMBER PRIMARY KEY,
user_name VARCHAR2(50) NOT NULL,
email VARCHAR2(100)
);
insert into users(user_id, user_name, email) values (seq_users.nextval, 'u1', '[email protected]');
insert into users(user_id, user_name, email) values (seq_users.nextval, 'u2', '[email protected]');
select * from users;
USER_ID|USER_NAME|EMAIL |
-------|---------|-----------|
2|u1 |[email protected]|
3|u2 |[email protected]|
在上面的示例中,我们手动为 user_id 字段指定了 seq_users.nextval 值。如果想要实现自增字段的效果,可以利用触发器实现:
CREATE OR REPLACE TRIGGER tri_user_insert
BEFORE INSERT ON users
FOR EACH ROW
DECLARE
BEGIN
IF (:NEW.user_id IS NULL) THEN
SELECT seq_users.nextval INTO :NEW.user_id FROM dual;
END IF;
END;
该触发器在插入数据之前判断 user_id 是否为空,如果为空就生成一个新的序列号。我们再插入一些数据:
insert into users(user_id, user_name, email) values (null, 'u3', '[email protected]');
insert into users(user_name, email) values ('u4', '[email protected]');
select * from users;
USER_ID|USER_NAME|EMAIL |
-------|---------|-----------|
2|u1 |[email protected]|
3|u2 |[email protected]|
4|u3 |[email protected]|
5|u4 |[email protected]|
上面两个插入语句都没有为 user_id 提供数据,而是由触发器自动生成一个数字编号。
另一个更简单的方式就是将字段的默认值设置为序列的值:
create table users(
user_id NUMBER DEFAULT seq_users.nextval PRIMARY KEY,
user_name VARCHAR2(50) NOT NULL,
email VARCHAR2(100)
);
实际上,Oracle 中的标识列就是采用这种方法实现的,只不过增加了一些额外的限制而已。
Oracle 提供了ALTER SEQUENCE语句,可以修改序列的属性:
ALTER SEQUENCE seq_users
INCREMENT BY 2
MAXVALUE 10000
CYCLE;
以上语句将序列 seq_users 的增量修改为 2,最大值修改为 10000,并且再到达最大值之后再次从最小值开始循环。不过,Oracle 序列不能修改起始值(START WITH),只能使用DROP SEQUENCE seq_name;语句删除序列再重建创建。
Microsoft SQL Server 提供了两种创建自增字段的方法:
SQL Server 支持为字段指定 IDENTITY(start, increment) 属性的方法定义一个标识列,start 表示序列的起始值,increment 表示每次的增量值。例如:
create table users(
user_id int identity primary key,
user_name varchar(50) not null,
email varchar(100)
);
其中,user_id 是一个 INTEGER 类型的标识列;系统生成的序列值默认从 1 开始,每次递增也是 1。SQL Server 中每个表只能定义一个标识列。
我们插入一些测试数据:
insert into users(user_name, email) values ('u1', '[email protected]');
insert into users(user_name, email) values ('u2', '[email protected]');
select * from users;
user_id|user_name|email |
-------|---------|-----------|
1|u1 |[email protected]|
2|u2 |[email protected]|
以上语句通过标识列自动生成了两个用户编号。我们可以利用获取最后一次插入的标识列的值:
select @@identity;
需要注意的是,不能为标识列指定 NULL 值;默认也不能为标识列手动指定值。
insert into users(user_id, user_name, email) values (null, 'u3', '[email protected]');
SQL Error [339] [S0001]: DEFAULT or NULL are not allowed as explicit identity values.
insert into users(user_id, user_name, email) values (0, 'u4', '[email protected]');
SQL Error [544] [S0001]: Cannot insert explicit value for identity column in table 'users' when IDENTITY_INSERT is set to OFF.
第一个语句为 user_id 指定了 NULL 值;第二个语句的错误在于为 user_id 指定了明确的值,不过可以通过设置表的 IDENTITY_INSERT 属性修改默认行为。
SQL Server 提供了和 Oracle 类似的序列对象,用于生成一个递增或递减的数字序列。创建序列的完整语法如下:
CREATE SEQUENCE sequence_name
[ AS integer_type ]
[ START WITH ]
[ INCREMENT BY ]
[ { MINVALUE [ ] } | { NO MINVALUE } ]
[ { MAXVALUE [ ] } | { NO MAXVALUE } ]
[ CYCLE | { NO CYCLE } ]
[ { CACHE [ ] } | { NO CACHE } ];
其中,
以下语句使用默认值创建一个序列 seq_users:
create sequence seq_users;
使用 NEXT VALUE FOR 函数获取一个序列的值:
select next value for seq_users;
|
--------------------|
-9223372036854775808|
返回的是 INTEGR 类型的最小值。
我们可以将字段的默认值设置为序列的 NEXT VALUE FOR 函数值,实现自增效果:
create table users(
user_id bigint default next value for seq_users primary key,
user_name varchar(50) not null,
email varchar(100)
);
insert into users(user_name, email) values ('u1', '[email protected]');
insert into users(user_name, email) values ('u2', '[email protected]');
select * from users;
user_id |user_name|email |
--------------------|---------|-----------|
-9223372036854775806|u1 |[email protected]|
-9223372036854775805|u2 |[email protected]|
ALTER SEQUENCE语句可以修改序列的属性,参数与CREATE SEQUENCE类似,除了 integer_type 之外的参数都可以修改。例如:
alter sequence seq_users restart with 1;
insert into users(user_name, email) values ('u3', '[email protected]');
select * from users;
user_id |user_name|email |
--------------------|---------|-----------|
-9223372036854775807|u1 |[email protected]|
-9223372036854775806|u2 |[email protected]|
1|u3 |[email protected]|
PostgreSQL 提供了多种方法实现自增字段,包括:
PostgreSQL 实现了 SQL 标准中的标识列,语法与 Oracle 几乎相同:
column_name data_type GENERATED { ALWAYS | BY DEFAULT } AS IDENTITY[ ( sequence_option ) ]
其中,
以下语句创建了一个表 users,其中 user_id 是一个标识列:
create table users(
user_id int generated always as identity primary key,
user_name varchar(50) not null,
email varchar(100)
);
此时,PostgreSQL 自动创建了一个序列对象 users_user_id_seq。我们测试一下数据插入:
insert into users(user_name, email) values ('u1', '[email protected]');
insert into users(user_name, email) values ('u2', '[email protected]');
select * from users;
user_id|user_name|email |
-------|---------|-----------|
1|u1 |[email protected]|
2|u2 |[email protected]|
标识列默认从 1 开始,每次递增也是 1。
如果我们为 user_id 指定明确的值:
insert into users(user_id, user_name, email) values (3, 'u3', '[email protected]');
SQL Error [428C9]: ERROR: cannot insert into column "user_id"
Detail: Column "user_id" is an identity column defined as GENERATED ALWAYS.
Hint: Use OVERRIDING SYSTEM VALUE to override.
该语句执行错误,不过我们可以使用INSERT语句的 OVERRIDING SYSTEM VALUE 选项覆盖系统提供的值。
与 Oracle 和 SQL Server 类似,PostgreSQL 也实现了 SQL 标准中的序列对象。创建序列的语法如下:
CREATE SEQUENCE [ IF NOT EXISTS ] name
[ AS data_type ]
[ INCREMENT [ BY ] increment ]
[ MINVALUE minvalue | NO MINVALUE ] [ MAXVALUE maxvalue | NO MAXVALUE ]
[ START [ WITH ] start ]
[ CACHE cache ]
[ [ NO ] CYCLE ]
[ OWNED BY { table_name.column_name | NONE } ]
其中,
以下语句使用默认值创建一个序列 seq_users:
create sequence seq_users;
该语句创建了一个从 1 开始,增量为 1,最小值为 1。
PostgreSQL 使用 nextval 和 currval 函数获取一个序列的值:
select nextval('seq_users');
nextval|
-------|
1|
select currval('seq_users');
currval|
-------|
1|
我们可以将字段的默认值设置为序列的 nextval 函数值,实现自增效果:
create table users(
user_id bigint default nextval('seq_users') primary key,
user_name varchar(50) not null,
email varchar(100)
);
insert into users(user_name, email) values ('u1', '[email protected]');
insert into users(user_name, email) values ('u2', '[email protected]');
select * from users;
user_id|user_name|email |
-------|---------|-----------|
2|u1 |[email protected]|
3|u2 |[email protected]|
ALTER SEQUENCE语句可以修改序列的属性,参数与CREATE SEQUENCE类似。例如:
alter sequence seq_users restart with 100;
insert into users(user_name, email) values ('u3', '[email protected]');
select * from users;
user_id|user_name|email |
-------|---------|-----------|
2|u1 |[email protected]|
3|u2 |[email protected]|
100|u3 |[email protected]|
除此之外,使用 setval 函数也可以修改序列的值。
SERIAL 与 PostgreSQL 标识列类似,实际上是一个内部的序列对象。例如:
create table users(
user_id serial primary key,
user_name varchar(50) not null,
email varchar(100)
);
版权声明:本文为CSDN博主「董旭阳TonyDong」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/horses/article/details/105427256