如何实现多维序列
How to implement multidimensional sequences
例如,这是一个每年的序列。 no
随着 year
递增:
| no | year |
+----+------+
| 1 | 2016 |
| 2 | 2016 |
| 3 | 2016 |
| 1 | 2017 |
| 2 | 2017 |
| 4 | 2016 |
现在我已经为每年创建了序列
但问题是 Oracle 不会在明年自动创建新序列。
另一个问题是如果我想使用 3D 序列,在 year
和 type
内递增:
| no | year | type |
+----+------+------+
| 1 | 2016 | a |
| 2 | 2016 | a |
| 1 | 2016 | b |
| 1 | 2017 | b |
| 2 | 2017 | b |
| 1 | 2017 | c |
数据库中的序列太多
由于并行访问问题,我不推荐 max(no)
。
我试图在触发器中获取 max(no)
之前锁定 table,但它导致了死锁。
唯一的方法是使用代码控件 table ...
create table code_control
(year number(4,0) not null
, type varchar2(1) not null
, last_number number(38,0) default 1 not null
, primary key (year,type)
)
organization index
/
...这样维护...
create or replace function get_next_number
(p_year in number, p_type in varchar2)
return number
is
pragma autonomous_transaction;
cursor cur_cc is
select last_number + 1
from code_control cc
where cc.year= p_year
and cc.type = p_type
for update of last_number;
next_number number;
begin
open cur_cc;
fetch cur_cc into next_number;
if cur_cc%found then
update code_control
set last_number = next_number
where current of cur_cc;
else
insert into code_control (year,type)
values (p_year, p_type)
returning last_number into next_number;
end if;
commit;
return next_number;
end;
/
重要的是 SELECT ...更新。悲观锁定保证了多用户环境中的唯一性。 PRAGMA 确保维护 code_control
不会污染更广泛的事务。它允许我们在没有死锁的情况下调用触发器中的函数。
这是一个 table,它的钥匙和你的一样:
create table t42
(year number(4,0) not null
, type varchar2(1) not null
, id number(38,0)
, primary key (year,type, id)
)
/
create or replace trigger t42_trg
before insert on t42 for each row
begin
:new.id := get_next_number(:new.year, :new.type);
end;
/
在我填充之前没有什么可以袖手旁观的 t42
:
SQL> select * from code_control;
no rows selected
SQL> select * from t42;
no rows selected
SQL> insert into t42 (year, type) values (2016, 'A');
1 row created.
SQL> insert into t42 (year, type) values (2016, 'A');
1 row created.
SQL> insert into t42 (year, type) values (2016, 'A');
1 row created.
SQL> insert into t42 (year, type) values (2016, 'B');
1 row created.
SQL> insert into t42 (year, type) values (2016, 'A');
1 row created.
SQL> insert into t42 (year, type) values (2017, 'A');
1 row created.
SQL> select * from t42;
YEAR T ID
---------- - ----------
2016 A 1
2016 A 2
2016 A 3
2016 A 4
2016 B 1
2017 A 1
6 rows selected.
SQL> select * from code_control;
YEAR T LAST_NUMBER
---------- - -----------
2016 A 4
2016 B 1
2017 A 1
SQL>
所以这个实现的明显反对意见是可伸缩性。插入事务在 code_control
table 上序列化。这是绝对正确的。然而,锁的持有时间尽可能短,所以即使 t42
table 每秒被填充多次,这也不应该成为问题。
但是,如果 table 受到大量并发插入的影响,锁定可能会成为一个问题。 table 有足够的兴趣事务槽(INITRANS、MAXTRANS)来应对并发需求是至关重要的。但是非常繁忙的系统可能需要更智能的实现(可能会批量生成 ID);否则放弃复合键以支持序列(因为序列在多用户环境中确实可以扩展)。
例如,这是一个每年的序列。 no
随着 year
递增:
| no | year |
+----+------+
| 1 | 2016 |
| 2 | 2016 |
| 3 | 2016 |
| 1 | 2017 |
| 2 | 2017 |
| 4 | 2016 |
现在我已经为每年创建了序列
但问题是 Oracle 不会在明年自动创建新序列。
另一个问题是如果我想使用 3D 序列,在 year
和 type
内递增:
| no | year | type |
+----+------+------+
| 1 | 2016 | a |
| 2 | 2016 | a |
| 1 | 2016 | b |
| 1 | 2017 | b |
| 2 | 2017 | b |
| 1 | 2017 | c |
数据库中的序列太多
由于并行访问问题,我不推荐 max(no)
。
我试图在触发器中获取 max(no)
之前锁定 table,但它导致了死锁。
唯一的方法是使用代码控件 table ...
create table code_control
(year number(4,0) not null
, type varchar2(1) not null
, last_number number(38,0) default 1 not null
, primary key (year,type)
)
organization index
/
...这样维护...
create or replace function get_next_number
(p_year in number, p_type in varchar2)
return number
is
pragma autonomous_transaction;
cursor cur_cc is
select last_number + 1
from code_control cc
where cc.year= p_year
and cc.type = p_type
for update of last_number;
next_number number;
begin
open cur_cc;
fetch cur_cc into next_number;
if cur_cc%found then
update code_control
set last_number = next_number
where current of cur_cc;
else
insert into code_control (year,type)
values (p_year, p_type)
returning last_number into next_number;
end if;
commit;
return next_number;
end;
/
重要的是 SELECT ...更新。悲观锁定保证了多用户环境中的唯一性。 PRAGMA 确保维护 code_control
不会污染更广泛的事务。它允许我们在没有死锁的情况下调用触发器中的函数。
这是一个 table,它的钥匙和你的一样:
create table t42
(year number(4,0) not null
, type varchar2(1) not null
, id number(38,0)
, primary key (year,type, id)
)
/
create or replace trigger t42_trg
before insert on t42 for each row
begin
:new.id := get_next_number(:new.year, :new.type);
end;
/
在我填充之前没有什么可以袖手旁观的 t42
:
SQL> select * from code_control;
no rows selected
SQL> select * from t42;
no rows selected
SQL> insert into t42 (year, type) values (2016, 'A');
1 row created.
SQL> insert into t42 (year, type) values (2016, 'A');
1 row created.
SQL> insert into t42 (year, type) values (2016, 'A');
1 row created.
SQL> insert into t42 (year, type) values (2016, 'B');
1 row created.
SQL> insert into t42 (year, type) values (2016, 'A');
1 row created.
SQL> insert into t42 (year, type) values (2017, 'A');
1 row created.
SQL> select * from t42;
YEAR T ID
---------- - ----------
2016 A 1
2016 A 2
2016 A 3
2016 A 4
2016 B 1
2017 A 1
6 rows selected.
SQL> select * from code_control;
YEAR T LAST_NUMBER
---------- - -----------
2016 A 4
2016 B 1
2017 A 1
SQL>
所以这个实现的明显反对意见是可伸缩性。插入事务在 code_control
table 上序列化。这是绝对正确的。然而,锁的持有时间尽可能短,所以即使 t42
table 每秒被填充多次,这也不应该成为问题。
但是,如果 table 受到大量并发插入的影响,锁定可能会成为一个问题。 table 有足够的兴趣事务槽(INITRANS、MAXTRANS)来应对并发需求是至关重要的。但是非常繁忙的系统可能需要更智能的实现(可能会批量生成 ID);否则放弃复合键以支持序列(因为序列在多用户环境中确实可以扩展)。