三大数据库 sequence 之华山论剑 (上篇)
前言
本文将基于以下三种关系型数据库,对 sequence (序列) 展开讨论。
Oracle - 应用最广泛的商用关系型数据库
PostgreSQL - 功能最强大的开源关系型数据库
MySQL - 应用最广泛的开源关系型数据库
sequence 适用场景
主键
用于整型主键数据的生成,一般一个 sequence 仅用于一张表的主键。这是最常用的用途。
本文讨论的主要是此用途。
非主键
只使用 sequence 本身自增的功能,可多表共用一个 sequence,或整个数据库共用一个 sequence。
sequence 不适用的场景
对于要求实际的值一定是连续的(如1,2,3,4,5),sequence 则不适用。
首先,sequence 生成时是连续的,但由于其生成的值会丢失或被消耗掉等原因,从而导致实际使用时不一定是连续的。
sequence 用法一 显式调用
这种方式是单独创建 sequence 和表,在 INSERT 等语句中显式调用 sequence。
如下示例。
Oracle
SQL> CREATE SEQUENCE seq_test;
Sequence created.
SQL> CREATE TABLE tb_test (
test_id NUMBER PRIMARY KEY
); 2 3
Table created.
SQL> INSERT INTO tb_test (test_id) VALUES (seq_test.nextval);
1 row created.
SQL> COMMIT;
Commit complete.
SQL> SELECT * FROM tb_test ORDER BY 1 DESC;
TEST_ID
----------
1
PostgreSQL
如下示例,PostgreSQL 的 SQL 与 Oracle 的 SQL 很类似。
$ psql -U alvin -d alvindb
psql (11.9)
Type "help" for help.
alvindb=> CREATE SEQUENCE seq_test;
CREATE SEQUENCE
alvindb=> CREATE TABLE tb_test (
alvindb(> test_id INTEGER PRIMARY KEY
alvindb(> );
CREATE TABLE
alvindb=> INSERT INTO tb_test (test_id) VALUES (nextval('seq_test'));
INSERT 0 1
alvindb=> SELECT * FROM tb_test ORDER BY 1 DESC;
test_id
---------
1
(1 row)
MySQL
MySQL 不支持单独创建sequence。 参考 用法四 AUTO INCREMENT 中 MySQL 部分。
sequence 用法二 触发器中调用
是否可以在 INSERT 语句中不显式调用 sequence,而使其自动调用呢?
当然可以!通常有三种方法。一是通过触发器实现,二是在 DEFAULT 中调用sequence,三是通过 AUTO INCREMENT 方式。
我们先来看一下如何在触发器中实现。
可以在表的 BEFORE INSERT 触发器中,调用 sequence,从而达到在插入前自动给主键赋值。这样,在 INSERT 中就不需要显式调用 sequence 了。
Oracle
SQL> CREATE SEQUENCE seq_test2;
Sequence created.
SQL> CREATE TABLE tb_test2 (
test_id NUMBER PRIMARY KEY,
test_order NUMBER
); 2 3 4
Table created.
SQL> CREATE OR REPLACE TRIGGER trg_b_ins_tb_test2
BEFORE INSERT ON tb_test2
FOR EACH ROW
BEGIN
SELECT seq_test2.nextval
INTO :new.test_id
FROM dual;
END; 2 3 4 5 6 7 8
9 /
Trigger created.
SQL> INSERT INTO tb_test2 (test_order) VALUES (1);
1 row created.
SQL> SELECT * FROM tb_test2 ORDER BY 2 DESC;
TEST_ID TEST_ORDER
---------- ------------
1 1
下面测试表明,当在 INSERT 中指定列 test_id 为 NULL 时,会从 sequence 中取值。但这是 trigger 的原理决定的,与传入的值是否为 NULL 无关。
SQL> INSERT INTO tb_test2 (test_id,test_order) VALUES (NULL,2);
1 row created.
SQL> COMMIT;
Commit complete.
SQL> SELECT * FROM tb_test2 ORDER BY 2 DESC;
TEST_ID TEST_ORDER
---------- ----------
2 2
1 1
PostgreSQL
如下示例,PostgreSQL 的 SQL 与 Oracle 的 SQL 也很类似。触发器的创建方式略有差异。
alvindb=> CREATE SEQUENCE seq_test2;
CREATE SEQUENCE
alvindb=> CREATE TABLE tb_test2 (
alvindb(> test_id INTEGER PRIMARY KEY,
alvindb(> test_order INTEGER
alvindb(> );
CREATE TABLE
alvindb=> CREATE OR REPLACE FUNCTION trgf_b_ins_tb_test2()
alvindb-> RETURNS TRIGGER AS
alvindb-> $$
alvindb$> BEGIN
alvindb$> NEW.test_id := nextval('seq_test2');
alvindb$> RETURN NEW;
alvindb$> END;
alvindb$> $$
alvindb-> LANGUAGE 'plpgsql';
CREATE FUNCTION
alvindb=> CREATE TRIGGER trg_b_ins_tb_test2
alvindb-> BEFORE INSERT ON tb_test2
alvindb-> FOR EACH ROW
alvindb-> EXECUTE PROCEDURE trgf_b_ins_tb_test2();
CREATE TRIGGER
alvindb=> \d+ tb_test2
Table "public.tb_test2"
Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
------------+---------+-----------+----------+---------+---------+--------------+-------------
test_id | integer | | not null | | plain | |
test_order | integer | | | | plain | |
Indexes:
"tb_test2_pkey" PRIMARY KEY, btree (test_id)
Triggers:
trg_b_ins_tb_test2 BEFORE INSERT ON tb_test2 FOR EACH ROW EXECUTE PROCEDURE trgf_b_ins_tb_test2()
alvindb=> INSERT INTO tb_test2 (test_order) VALUES (1);
INSERT 0 1
alvindb=> SELECT * FROM tb_test2 ORDER BY 2 DESC;
test_id | test_order
---------+--------------
1 | 1
(1 row)
下面测试表明,同 Oracle 中一样,当在 INSERT 中指定列 test_id 为 NULL 时,同样,这也是 trigger 的原理决定的,与传入的值是否为 NULL 无关。
alvindb=> INSERT INTO tb_test2 (test_id,test_order) VALUES (NULL,2);
INSERT 0 1
alvindb=> SELECT * FROM tb_test2 ORDER BY 2 DESC;
test_id | test_order
---------+------------
2 | 2
1 | 1
(2 rows)
MySQL
MySQL 不支持单独创建sequence。 参考 用法四 AUTO INCREMENT 中 MySQL 部分。
sequence 用法三 DEFAULT 中调用
看完上面的用法,我们不禁感觉,创建触发器有有点麻烦。
有没有简单用法呢,手动创建完 sequence 后,一句话就可以调用的那种?
当然,就是在 DEFAULT 调用 sequence!
Oracle
以下为 Oracle 中代码示例。
Oracle Database 11g Release 11.2.0.4.0
先在 Oracle 11g 中试一下。
SQL> CREATE SEQUENCE seq_test3;
Sequence created.
SQL> CREATE TABLE tb_test3 (
test_id NUMBER DEFAULT seq_test3.nextval PRIMARY KEY,
test_order NUMBER
); 2 3 4
test_id NUMBER DEFAULT seq_test3.nextval PRIMARY KEY,
*
ERROR at line 2:
ORA-00984: column not allowed here
什么?报错!这是为什么呢?
根据 Oracle 官方文档,原来在 Oracle 11g 中这种用法不支持。想要实现类似功能,只能用 trigger 了。
Restriction on Default Column Values
A DEFAULT expression cannot contain references to PL/SQL functions or to other columns, the pseudocolumns CURRVAL, NEXTVAL, LEVEL, PRIOR, and ROWNUM, or date constants that are not fully specified.
Oracle Database 12c Release 12.2.0.1.0
在 Oracle 12c 中 DEFAULT 中调用 sequence 是可以的。
SQL> CREATE SEQUENCE seq_test3;
Sequence created.
SQL> CREATE TABLE tb_test3 (
test_id NUMBER DEFAULT seq_test3.nextval PRIMARY KEY,
test_order NUMBER
); 2 3 4
Table created.
SQL> INSERT INTO tb_test3 (test_id,test_order) VALUES (seq_test3.nextval,1);
1 row created.
SQL> INSERT INTO tb_test3 (test_id,test_order) VALUES (DEFAULT,2);
1 row created.
SQL> INSERT INTO tb_test3 (test_order) VALUES (3);
1 row created.
SQL> COMMIT;
Commit complete.
SQL> SELECT * FROM tb_test3 ORDER BY 2 DESC;
TEST_ID TEST_ORDER
---------- ------------
3 3
2 2
1 1
通过如下 SQL 可查询数据字典中表列的 DEFAULT
SQL> SET linesize 100
COL table_name FOR a30
COL column_name FOR a30
COL data_default FOR a30
SQL> SELECT table_name,column_name,data_default FROM user_tab_columns WHERE table_name = 'TB_TEST3';
TABLE_NAME COLUMN_NAME DATA_DEFAULT
------------------------------ ------------------------------ ------------------------------
TB_TEST3 TEST_ID "TEST"."SEQ_TEST3"."NEXTVAL"
TB_TEST3 TEST_ORDER
那么在表列的 DEFAULT 中调用了 sequence 后,sequence 可以被删除吗?
SQL> DROP SEQUENCE seq_test3;
Sequence dropped.
可以看到,DEFAULT 中的 sequence 可以被删除。
那么删除 sequence 后表列的 DEFAULT 变不变呢?再插入数据会怎么样呢?
如下示例,删除 sequence 后再插入数据,删除 sequence 后表列的 DEFAULT 不变!但再插入数据时会报错。
SQL> SELECT table_name,column_name,data_default FROM user_tab_columns WHERE table_name = 'TB_TEST3';
TABLE_NAME COLUMN_NAME DATA_DEFAULT
------------------------------ ------------------------------ ------------------------------
TB_TEST3 TEST_ID "TEST"."SEQ_TEST3"."NEXTVAL"
TB_TEST3 TEST_ORDER
SQL>
SQL> INSERT INTO tb_test3 (test_order) VALUES (5);
INSERT INTO tb_test3 (test_order) VALUES (5)
*
ERROR at line 1:
ORA-02289: sequence does not exist
PostgreSQL
在 PostgreSQL 中同样可以。PostgreSQL 的 SQL 与 Oracle 的 SQL 依然很类似。
alvindb=> CREATE SEQUENCE seq_test3;
CREATE SEQUENCE
alvindb=> CREATE TABLE tb_test3 (
alvindb(> test_id INTEGER DEFAULT nextval('seq_test3') PRIMARY KEY,
alvindb(> test_order INTEGER
alvindb(> );
CREATE TABLE
alvindb=> INSERT INTO tb_test3 (test_id,test_order) VALUES (nextval('seq_test3'),1);
INSERT 0 1
alvindb=> INSERT INTO tb_test3 (test_id,test_order) VALUES (DEFAULT,2);
INSERT 0 1
alvindb=> INSERT INTO tb_test3 (test_order) VALUES (3);
INSERT 0 1
alvindb=> SELECT * FROM tb_test3 ORDER BY 2 DESC;
test_id | test_order
---------+--------------
3 | 3
2 | 2
1 | 1
(3 rows)
我们尝试 DROP 一下 sequence。
从下面的示例中可以看出,DEFAULT 中的 sequence 可以删除。同时也会提示,表列的 DEFAULT 也被删除了,这个是十分友好的。
alvindb=> CREATE SEQUENCE seq_test3;
CREATE SEQUENCE
alvindb=> CREATE TABLE tb_test3 (
alvindb(> test_id INTEGER DEFAULT nextval('seq_test3') PRIMARY KEY,
alvindb(> test_order INTEGER
alvindb(> );
CREATE TABLE
alvindb=> \d+ tb_test3
Table "public.tb_test3"
Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
------------+---------+-----------+----------+--------------------------------+---------+--------------+-------------
test_id | integer | | not null | nextval('seq_test3'::regclass) | plain | |
test_order | integer | | | | plain | |
Indexes:
"tb_test3_pkey" PRIMARY KEY, btree (test_id)
alvindb=> DROP SEQUENCE seq_test3;
ERROR: cannot drop sequence seq_test3 because other objects depend on it
DETAIL: default value for column test_id of table tb_test3 depends on sequence seq_test3
HINT: Use DROP ... CASCADE to drop the dependent objects too.
alvindb=> DROP SEQUENCE tb_test4_test_id_seq CASCADE;
NOTICE: drop cascades to default value for column test_id of table tb_test4
DROP SEQUENCE
刚才提到,在 Oracle 中,这个用法是从 Oracle 12c 中才开始支持的。
那么 PostgreSQL 是哪个版本开始支持的呢?
PostgreSQL 官网文档中列出的最早的版本是 PostgreSQL 7.1(7.1 之前的文档官网中未列出),在这个文档中,已支持这种用法。
这就 PostgreSQL 7.1 文档中的例子
CREATE TABLE distributors (
name VARCHAR(40) DEFAULT 'luso films',
did INTEGER DEFAULT NEXTVAL('distributors_serial'),
modtime TIMESTAMP DEFAULT now()
);
Oracle 和 PostgreSQL 这些版本是什么时候发布的呢?
根据 PostgreSQL 官网, PostgreSQL Release 7.1.3 是 2001-08-15。
根据 Wikipedia, Oracle Database 12c Release 1 是 July 2014 发布的。
即 PostgreSQL 2001 年已支持 sequence 的 DEFAULT nextval 用法,十三年后,Oracle 也支持了。
MySQL
MySQL 不支持单独创建sequence。 参考 用法四 AUTO INCREMENT 中 MySQL 部分。
公众号
关注 DBA Daily 公众号,第一时间收到文章的更新。
通过一线 DBA 的日常工作,学习实用数据库技术干货!

公众号优质文章推荐
[PG Upgrade Series] Extract Epoch Trap
[PG Upgrade Series] Toast Dump Error
GitLab supports only PostgreSQL now
三大数据库 sequence 之华山论剑 (上篇)的更多相关文章
- 三大数据库 sequence 之华山论剑 (中篇)
sequence 用法四 AUTO INCREMENT 通过 DEFAULT 还是需要手动创建 sequence.有没有更简单的用法呢? 当然,就是通过 AUTO INCREMENT 方式,自动创建 ...
- 三大数据库 sequence 之华山论剑 (下篇)
MySQL 5.7 MYISAM ENGINE 以下是 MySQL 5.7 MYISAM ENGINE 中的运行结果 mysql> CREATE TABLE tb_test5 ( -> t ...
- redis mongodb mysql 三大数据库的更简单的批量操作。批量任务自动聚合器。
1.redis mongodb mysql的python包都提供了批量插入操作,但需要自己在外部对一个例如1000 001个任务进行分解成每1000个为1个小批次,还要处理整除批次数量后的余数,如果做 ...
- 数据库sequence的作用和用法
转: 数据库sequence的作用和用法 2016年10月14日 19:51:03 很菜很菜的鸟 阅读数 14456 标签: oracle数据库db2sequence seqence的作用: se ...
- JDBC读取新插入Oracle数据库Sequence值的5种方法
Oracle的sequence实现非常灵活,所以也带来一些易用性问题,如何取到新插入记录生成的sequence值与其它数据库有较大差别,本文详国介绍了5种实现读取新插入记录sequence值的方法. ...
- 数据库Sequence创建与使用
最近几天使用Oracle的sequence序列号,发现对如何创建.修改.使用存在很多迷茫点,在上网寻找答案后,根据各路大神的总结,汇总下对自己的学习成果: 在Oracle中sequence就是序号,每 ...
- Oracle,Mysql ,SQL Server 三大数据库带参数的模糊查询, 拼接查询条件问题
最近项目开发一直在不断切换数据库,有时候一条sql 要同时考虑多种数据库中的兼容问题 , 先总结一条模糊查询拼接查询条件的问题,后续追加总结. 目前使用 mybatis: 1. Oracle 中使 ...
- Mysql、SqlServer、Oracle三大数据库的区别
一.MySQL 优点: 体积小.速度快.总体拥有成本低,开源: 支持多种操作系统: 是开源数据库,提供的接口支持多种语言连接操作 : MySQL的核心程序采用完全的多线程编程.线程是轻量级的进程,它可 ...
- oracle、mysql、db2三大数据库分页方法的整理
最近项目中经常会涉及到代码中支持三种数据库的分页的功能,自己整理了关于三种数据库的分页的写法,分享给大家,以供大家使用.希望能帮到更多的码友! 先来看一个代码片段: String page = ala ...
随机推荐
- 使用.NET 6开发TodoList应用(填坑1)——实现当前登录用户获取
系列导航及源代码 使用.NET 6开发TodoList应用文章索引 需求 在前面的文章里使用.NET 6开发TodoList应用(5)--领域实体创建,我们留了一个坑还没有填上,就是在数据库保存的时候 ...
- SQLServer触发器调用JavaWeb接口
这几天接到一个需求需要吧不同系统的数据库进行同步,需要我做一个中间平台进行连接,瞬间就想到了触发器调用接口然后通过API进行传递再写入另一个数据库. sqlServer触发器调用JavaWeb接口 1 ...
- 猫与ThinkPad
高中时候看见过家里橘猫谁在舅舅的ThinkPad笔记本了,可惜没拍下来,我也不喜欢那只猫,更喜欢幼时的白猫和黑白猫. ThinkPad宣传图片诚不欺我. 怀念青春与当年陪我游戏的IBM的ThinkPa ...
- 论文解读《The Emerging Field of Signal Processing on Graphs》
感悟 看完图卷积一代.二代,深感图卷积的强大,刚开始接触图卷积的时候完全不懂为什么要使用拉普拉斯矩阵( $L=D-W$),主要是其背后的物理意义.通过借鉴前辈们的论文.博客.评论逐渐对图卷积有了一定的 ...
- [JavaWeb]利用JSP的编码特性制作免杀后门
利用JSP的编码特性制作免杀后门 这里是借鉴了Y4stacker师傅的thinkings 待解决的问题 JSP解析 JSP"乱码"为什么还能被识别 "乱码"的J ...
- 论文解读GALA《Symmetric Graph Convolutional Autoencoder for Unsupervised Graph Representation Learning》
论文信息 Title:<Symmetric Graph Convolutional Autoencoder for Unsupervised Graph Representation Learn ...
- 关于一键提取QQ群成员信息的记录
问题情境 昨天晚上回宿舍看到舍友在吐槽:天哪,我要把70多个人的QQ号全统计出来,只能一个一个地在咱们学院的学院群里找.吐了. 当时我第一反应是用python写个脚本之类的去做,因此查阅了一下资料,找 ...
- python 小兵(7)迭代器
阅读目录 函数名的使用以及第一类对象 闭包 迭代器 回到顶部 函数名的使用以及第一类对象 函数名的运用 函数名是一个变量, 但它是一个特殊的变量, 与括号配合可以执行函数的变量 1.函数名的内存地址 ...
- AtCoder Beginner Contest 220 A-F
A #include <iostream> using namespace std; int main() { int a, b, c; cin >> a >> b ...
- Programiz 中文系列教程·翻译完成
原文:Programiz 协议:CC BY-NC-SA 4.0 欢迎任何人参与和完善:一个人可以走的很快,但是一群人却可以走的更远. 在线阅读 ApacheCN 学习资源 目录 Programiz C ...