SQL:如何插入自定义增量值

Posted

技术标签:

【中文标题】SQL:如何插入自定义增量值【英文标题】:SQL: How to insert a custom increment value 【发布时间】:2018-12-19 00:08:02 【问题描述】:

目前我有一个看起来像这样的表:

Year | Branch_Code | Registration_Number | ...
______________________________________________
2018 | BRANCH1     | 1                   | ...
2018 | BRANCH1     | 2                   | ...
2018 | BRANCH2     | 1                   | ...

因此,每次我将数据插入表中时,我都希望 Registration_Number 自动递增,并依赖于 Year 和 Branch_Code。我试图先获取最大值然后再插入,但如果我的客户同时插入,它有时会插入重复的数字。

有人有解决办法吗?

附:我正在使用 Laravel 框架和 Oracle 数据库。

【问题讨论】:

您将从以下链接***.com/questions/10990347/…得到答案 @NazmulHasan - 自动递增与 OP 想要做的不同。 【参考方案1】:

我建议您使用 序列 并不要担心它。

或者,您可以尝试以下方法:

创建一个表(在我的示例中为 regnum),其中包含 [year, branch_code] 组合的最后一个 registration_number 在一个自治事务的函数中增加它(这样它的COMMIT就不会影响主事务) 在触发器中填充目标表(在我的示例中为yourt

方法如下:

表格优先:

SQL> create table yourt (year number, branch_code varchar2(20), registration_number number, datum date);

Table created.

SQL> create table regnum (year number, branch_code varchar2(20), registration_number number);

Table created.

功能:

SQL> create or replace function f_regnum (par_year in number, par_branch_code in varchar2)
  2    return number
  3  is
  4    pragma autonomous_transaction;
  5    l_nextval number;
  6  begin
  7    select registration_number + 1
  8      into l_nextval
  9      from regnum
 10      where year = par_year
 11        and branch_code = par_branch_code
 12    for update of registration_number;
 13
 14    update regnum set
 15      registration_number = l_nextval
 16      where year = par_year
 17        and branch_code = par_branch_code;
 18
 19    commit;
 20    return (l_nextval);
 21
 22  exception
 23    when no_data_found then
 24      lock table regnum in exclusive mode;
 25
 26      insert into regnum (year, branch_code, registration_number)
 27      values (par_year, par_branch_code, 1);
 28
 29      commit;
 30      return(1);
 31  end;
 32  /

Function created.

触发器:

SQL> create or replace trigger trg_bi_yourt
  2    before insert on yourt
  3    for each row
  4  begin
  5    :new.registration_number := f_regnum(:new.year, :new.branch_code);
  6  end;
  7  /

Trigger created.

测试:

SQL> insert into yourt (year, branch_code, datum) values (2017, 'branch 1', date '2017-01-01');

1 row created.

SQL> insert into yourt (year, branch_code, datum) values (2017, 'branch 1', date '2017-01-25');

1 row created.

SQL> insert into yourt (year, branch_code, datum) values (2017, 'branch 2', date '2017-04-14');

1 row created.

SQL> insert into yourt (year, branch_code, datum) values (2018, 'branch 3', date '2018-07-11');

1 row created.

SQL> insert into yourt (year, branch_code, datum) values (2018, 'branch 1', date '2018-05-21');

1 row created.

SQL> insert into yourt (year, branch_code, datum) values (2018, 'branch 3', date '2018-03-14');

1 row created.

SQL> insert into yourt (year, branch_code, datum) values (2018, 'branch 3', date '2018-05-17');

1 row created.

结果:

SQL> select * from yourt order by branch_code, year;

      YEAR BRANCH_CODE          REGISTRATION_NUMBER DATUM
---------- -------------------- ------------------- ----------
      2017 branch 1                               2 2017-01-25
      2017 branch 1                               1 2017-01-01
      2018 branch 1                               1 2018-05-21
      2017 branch 2                               1 2017-04-14
      2018 branch 3                               2 2018-03-14
      2018 branch 3                               3 2018-05-17
      2018 branch 3                               1 2018-07-11

7 rows selected.

SQL> select * from regnum order by branch_code, year;

      YEAR BRANCH_CODE          REGISTRATION_NUMBER
---------- -------------------- -------------------
      2017 branch 1                               2
      2018 branch 1                               1
      2017 branch 2                               1
      2018 branch 3                               3

SQL>

solution 将在多用户环境中工作,不会引发 mutating table 错误,但如果您加载大量行(例如,使用 SQL*Loader)。再次使用序列

【讨论】:

使用autonomous_transaction 时可能会出现间隙。考虑插入发生但随后发生回滚。自治事务已提交,主事务已回滚。 registration_number 已生成但未使用。 我从来没有说过它是无缝的。 @MaximBornov - 该函数需要应用自治事务杂注以最小化序列化对regnum 表的影响。这是在多用户环境中扩展的唯一方法。 @Littlefoot - 这样做可能有正当理由。如果我们正在使用year, branch_code, registration_number 的遗留/面向用户的业务键构建应用程序:我们可以使用序列作为技术主键,但我们仍然需要支持用户习惯的引用。 对,@APC。我也使用类似的东西,因为用户希望每年“重新启动”序列。【参考方案2】:

在我看来,使用自动增量触发器或其他程序来维护Registration_Number 列的完整性是毫无价值的,我的意思是可能会有像delete/ update 这样的操作很难使用这样的设置。我宁愿使用更简单的方法并创建一个视图

CREATE OR replace VIEW view_t 
AS 
  SELECT year, 
         branch_code, 
         row_number() 
           over ( 
             PARTITION BY year, branch_code 
             ORDER BY NULL) AS REGISTRATION_NUMBER 
  FROM   t; 

Demo

这样,registration_number 将在您查询视图时“自动”包含所需的数字,您可能希望将其用于显示或来自外部应用程序。

【讨论】:

这种方法的问题是 ORDER BY NULL,它将registration_number 变成一个随机值。假设对这个数字有一个有效的要求(例如实现一个(旧)业务密钥:在这种情况下,我们需要 year, branch_code, registration_number 是稳定的。 @APC:你是对的。我仍然觉得使用主键或重新设计数据模型比触发器和其他东西更好。这是我的意见。 我曾在用户经常通过案例编号引用案例的系统上工作过,基本上是year, branch_code, registration_number。我们不应该试图强迫用户改变他们的商业行为。【参考方案3】:

感谢一个告诉我创建before insert 触发器的人(我无法记住他的名字,因为他已经删除了他的答案)。我试着创造一个,它就像一个魅力。

CREATE OR REPLACE TRIGGER TRIGGER_NAME 
BEFORE INSERT ON TABLE_NAME 
FOR EACH ROW
    BEGIN
        SELECT NVL(MAX(REGISTRATION_NUMBER), 0) + 1 
        INTO :NEW.REGISTRATION_NUMBER
        FROM TABLE_NAME 
        WHERE TABLE_NAME.YEAR = :NEW.YEAR 
        AND TABLE_NAME.BRANCH_CODE = :NEW.BRANCH_CODE;
    END;

【讨论】:

以上是关于SQL:如何插入自定义增量值的主要内容,如果未能解决你的问题,请参考以下文章

添加自定义自动增量值

在用户重新启动应用程序后,我应该如何在自定义列表视图中反映文本视图的增量值?

sql 自增列删除

如何通过 SQL 插入增量值

sql的自增列如何重置

如何在单个查询(sql server)中插入1条记录后检索自动增量值