【问题标题】:How to implement multidimensional sequences如何实现多维序列
【发布时间】:2017-04-28 03:16:18
【问题描述】:

例如,这是一个年度序列。 noyear 递增:

| no | year |
+----+------+
|  1 | 2016 |
|  2 | 2016 |
|  3 | 2016 |
|  1 | 2017 |
|  2 | 2017 |
|  4 | 2016 |

现在我已经为每一年创建了序列
但问题是Oracle不会在明年自动创建新序列。

另一个问题是如果我想使用 3D 序列,在 yeartype 内递增:

| 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) 之前锁定表,但它导致了死锁。

【问题讨论】:

  • 你可以只使用正常的序列,然后在选择数据的同时使用带有分区选项的row_number来模拟效果。
  • 查看您的样本,它不是序列,它是复合主键,并认为它必须以其他方式计算。是什么想法,制作一组独特的列,或者有一个递增的排序顺序?
  • @Gurv 使用运行时计算的“Id”并不稳定,如果我删除中间项,最后一项 Id 会改变。
  • @Seyran 实际上将“ID”列作为主键。 composit uniqe "no-year-type" 只显示给用户。但我仍然希望这可以由数据库生成。

标签: oracle oracle12c database-sequence


【解决方案1】:

做到这一点的唯一方法是使用代码控制表...

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 ... FOR UPDATE。悲观锁定保证了多用户环境中的唯一性。 PRAGMA 确保维护code_control 不会污染更广泛的交易。它允许我们在没有死锁的情况下在触发器中调用函数。

这里有一张像你这样的钥匙的桌子:

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 表上进行序列化。这是绝对正确的。然而,锁的持有时间尽可能短,因此即使t42 表每秒填充多次,这也不应该成为问题。

但是,如果表受到大量并发插入的影响,则锁定可能会成为问题。表有足够的兴趣事务槽(INITRANS、MAXTRANS)来处理并发需求是至关重要的。但是非常繁忙的系统可能需要更智能的实现(可能批量生成 ID);否则放弃复合键以支持序列(因为序列确实在多用户环境中扩展)。

【讨论】:

  • 我用 2 个并行线程测试它没有错误。很抱歉延迟回复。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-05-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-01-31
相关资源
最近更新 更多