SQL CREATE SEQUENCE 基于日期和计数器的组合

Posted

技术标签:

【中文标题】SQL CREATE SEQUENCE 基于日期和计数器的组合【英文标题】:SQL CREATE SEQUENCE based on a combination of date and counter 【发布时间】:2017-09-28 16:35:12 【问题描述】:

我正在使用 Oracle SQL:

Oracle Database 12c Enterprise Edition Release 12.1.0.2.0 - 64bit Production              0 
PL/SQL Release 12.1.0.2.0 - Production                                                    0 
CORE    12.1.0.2.0  Production                                                            0 
TNS for Linux: Version 12.1.0.2.0 - Production                                            0 
NLSRTL Version 12.1.0.2.0 - Production    

我需要帮助弄清楚如何为下面的关键字段创建序列:

我有一个表,其中包含一个名为 MY_ID 的字段。

MY_ID 需要在插入记录时使用序列自动生成。

序列的规则是字符串前缀、每天递增 1 并在午夜重置的计数器以及日期的组合。

例如: 9 月 10 日,我们插入了 2 条记录,那么 MY_ID 应该是:

PREFIX_01_20170910
PREFIX_02_20170910

9 月 11 日:

PREFIX_01_20170911
PREFIX_02_20170911
PREFIX_03_20170911

9 月 12 日,整张桌子可能是这样的

PREFIX_01_20170910
PREFIX_02_20170910
PREFIX_01_20170911
PREFIX_02_20170911
PREFIX_03_20170911
PREFIX_01_20170912

到目前为止,无论日期如何,我对序列所能做的就是递增 1:

CREATE SEQUENCE SEQ_MY_TABLE INCREMENT BY 1 MAXVALUE 9999999999999999999999999999 MINVALUE 1 NOCACHE;

和触发器:

create or replace TRIGGER MY_TABLE_TRG 
BEFORE INSERT ON MY_TABLE
FOR EACH ROW
BEGIN
  <<COLUMN_SEQUENCES>>
  BEGIN
    IF INSERTING AND :NEW.MY_ID IS NULL THEN
      SELECT SEQ_MY_TABLE .NEXTVAL INTO :NEW.MY_ID FROM SYS.DUAL;
    END IF;
  END COLUMN_SEQUENCES;
END;

谢谢!

【问题讨论】:

【参考方案1】:

您可能会想出一个方案来生成此PREFIX_nn_date 密钥,该密钥似乎可以在您的开发环境中工作,但我要给您一些不想要的建议:不要在上面浪费时间。将主键设为简单的 NUMBER 列,从序列中填充它,然后继续。一旦你的代码投入生产,更多的用户将同时敲打你的桌子,你精心设计的生成 PREFIX_nn_date 密钥的方案很可能会失败——你扔给它的创可贴越多来解决问题就越糟糕它会变成的。

祝你好运。

【讨论】:

感谢 Bob 的建议。但是在这种情况下,每年只会插入 【参考方案2】:

按照 Bob Jarvis 的回答,您可以随时在需要时创建字符串。这是一个简单的例子。

with records as (
select 1 id, to_date('20170901', 'yyyymmdd') theDate
from dual
union
select 2 id, to_date('20170901', 'yyyymmdd') theDate
from dual
union
select 3 id, to_date('20170902', 'yyyymmdd') theDate
from dual

)

select 'prefix_' ||  
to_char(theDate, 'yyyymmdd') || '_' || 
to_char( rank() over (partition by theDate order by id)) prefix
from records

返回:

prefix_20170901_1                                        
prefix_20170901_2                                        
prefix_20170902_1

我没有做太多的 oracle 工作,但如果您要反复执行此操作,您可能希望将此逻辑合并到函数或视图中。

【讨论】:

【参考方案3】:

您可以如下修改TRIGGER。由于您需要将序列中的每个数字作为 01,02,03 附加到您的前缀上,因此我在 TO_CHAR 中使用了 fm 说明符和 '00'。如果每天的总插入次数超过 99 ,则需要使用 fm000

create or replace TRIGGER MY_TABLE_TRG 
BEFORE INSERT ON MY_TABLE
FOR EACH ROW
DECLARE
BEGIN
  <<COLUMN_SEQUENCES>>
  BEGIN
    IF INSERTING AND :NEW.MY_ID IS NULL THEN
      SELECT 'PREFIX_'||TO_CHAR(SEQ_MY_TABLE.NEXTVAL,'fm00')||'_'||TO_CHAR(SYSDATE,'YYYYMMDD') INTO :NEW.MY_ID FROM SYS.DUAL;
    END IF;
  END COLUMN_SEQUENCES;
END;

注意:为了建议一个好的做法,我不建议将其用作您的PRIMARY KEY。在填充记录时,最好在所有应用程序代码中简单地创建序列PRIMARY KEY

【讨论】:

但是这个触发器不会重置 SEQ_MY_TABLE.NEXTVAL 对吧?如果我今天插入 1 条记录,我将获得 PREFIX_01_20170927,如果我明天插入另一条记录,我将获得 PREFIX_02_20170928,有没有办法重置 NEXTVAL,以便我明天获得 PREFIX_01_20170928? 不这样做,在上午 12:00 从调度程序调用此链接中描述的重置过程。 ***.com/questions/51470/…【参考方案4】:

最简单的方法是每天午夜通过作业重新创建序列。但是使用序列不是一个好主意。我认为这个 ID 对你很重要,但是序列可以缓存一些值,一些值可能会丢失。所以你会得到:

PREFIX_01_20170910
PREFIX_02_20170910
PREFIX_04_20170910
PREFIX_07_20170910

... 等等。例如,您有“缓存 10”,您插入了 2 条记录并退出,或者进行了回滚,或其他。

仅使用数字作为增量字段并计算此假 ID。

【讨论】:

你是对的!这不是一个可靠的方法。我可能应该通过查询表并计算具有当前日期的记录在后端生成此 ID,并附加前缀和日期以生成该 ID。【参考方案5】:

您可以创建如下函数来获取新的 id 并在插入查询中使用它。

CREATE OR REPLACE FUNCTION F_GETID (P_DT IN VARCHAR2) RETURN VARCHAR2
IS
V_NEW_ID VARCHAR2(50);
BEGIN
SELECT 'PREFIX_' || COUNT(*)+1 ||'_' || P_DT INTO V_NEW_ID FROM MY_TABLE 
WHERE MY_ID LIKE   'PREFIX%'||P_DT;
RETURN V_NEW_ID;
END;

然后

 insert into my_table(my_id , ...) values(F_GETID('20170927'),...);

【讨论】:

以上是关于SQL CREATE SEQUENCE 基于日期和计数器的组合的主要内容,如果未能解决你的问题,请参考以下文章

如何sql 在表中创建一个sequence

Sequence在Oracle中的使用

Sequence在Oracle中的使用

Sequence在Oracle中的使用

CREATE SEQUENCE - 创建一个新的序列发生器

基于日期获取所有日期 - sql