在问题中,如何在Oracle 10g中每年自动将Oracle序列的值重置为0?
我正在使用序列生成格式为YYYY<sequence value>
的标识符,并且序列值必须每年重置为0。
YYYY
从java获得,并与Oracle的序列值连接。由于外部第三方要求,无法更改标识符的格式。感谢您提前提供任何帮助。
答案 0 :(得分:6)
序列实际上并未设计为重置。但是在某些情况下需要重置序列,例如,在设置测试数据或将生产数据合并回测试环境时。此类活动不通常在生产中完成。
如果此类操作即将投入生产,则需要进行全面测试。 (引起最大关注的是重置程序在错误的时间意外执行的可能性,例如,在年中。
删除并重新创建序列是一种方法。作为一项操作,就SEQUENCE而言,它是相当简单的:
DROP SEQUENCE MY_SEQ; CREATE SEQUENCE MY_SEQ START WITH 1 INCREMENT BY 1 MINVALUE 0;
[编辑]正如Matthew Watson正确指出的那样,每个DDL语句(例如DROP,CREATE,ALTER)都会导致隐式提交。 [/编辑]
但是,在SEQUENCE上授予的任何权限都将被删除,因此需要重新授予这些权限。引用序列的任何对象都将失效。为了更加通用化,您需要保存权限(在删除序列之前),然后重新授予它们。
第二种方法是改变现有的SEQUENCE,而不是丢弃并重新创建它。重置序列可以通过将INCREMENT值更改为负值(当前值与0之间的差值)来完成,然后只执行一次.NEXTVAL将当前值设置为0,然后将INCREMENT更改回1。我之前(手动地,在测试环境中)使用了相同的方法来将序列设置为更大的值。
当然,要使其正常工作,您需要确保在执行此操作时,没有其他会话引用该序列。在错误的瞬间额外的.NEXTVAL会搞砸重置。 (注意:如果应用程序是作为序列的所有者连接而不是作为单独的用户连接,那么在数据库端实现这一点将很困难。)
要让它每年都发生,你需要安排一份工作。序列重置必须与标识符的YYYY部分重置相协调。
以下是一个例子:
http://www.jaredstill.com/content/reset-sequence.html
[编辑]
UNTESTED 占位符,用于重置序列的PL / SQL块的一种可能设计
declare pragma autonomous_transaction; ln_increment number; ln_curr_val number; ln_reset_increment number; ln_reset_val number; begin -- save the current INCREMENT value for the sequence select increment_by into ln_increment from user_sequences where sequence_name = 'MY_SEQ'; -- determine the increment value required to reset the sequence -- from the next fetched value to 0 select -1 - MY_SEQ.nextval into ln_reset_increment from dual; -- fetch the next value (to make it the current value) select MY_SEQ.nextval into ln_curr from dual; -- change the increment value of the sequence to EXECUTE IMMEDIATE 'alter sequence MY_SEQ increment by ' || ln_reset_increment ||' minvalue 0'; -- advance the sequence to set it to 0 select MY_SEQ.nextval into ln_reset_val from dual; -- set increment back to the previous(ly saved) value EXECUTE IMMEDIATE 'alter sequence MY_SEQ increment by ' || ln_increment ; end; /
注意:
答案 1 :(得分:5)
把它扔出去作为一个想法:
如果你想要一个不需要持续DDL的解决方案(即没有丢弃和创建或重置序列),甚至任何工作,你可以考虑这样的事情(这只是原则上的,我没有测试过这种方法但是我相信它会起作用):
创建单个序列。
创建一个参考表,每年有一行,例如
年(年份数(4,0)PRIMARY KEY,starting_value NUMBER)
当您从序列中获得NEXTVAL
时,您必须从YEARS表中查询当前年份时减去starting_value
。如果未找到年份,则应插入新行(即,在任何给定年份中运行的第一个流程将插入新值)。
e.g。一个函数,例如get_year_starting_value (pn_year IN NUMBER) RETURN NUMBER
可以查询此表并返回给定年份的starting_value
;如果它得到NO_DATA_FOUND
,它可以调用一个过程来使用序列中的NEXTVAL
插入它(在自治事务中提交,以便新值立即可用于其他会话,以便函数由于副作用而不会失败)
可能不是所有案例的解决方案,但我认为这种方法至少在某些情况下可能会有所帮助。
答案 2 :(得分:4)
使用工作来完成这个任务。首先,创建一个存储过程来重置序列(我通常使用DROP / CREATE解决方案,但你可以使用spencer7593的技巧):
CREATE OR REPLACE PROCEDURE my_seq_reset AS
BEGIN
EXECUTE IMMEDIATE 'DROP SEQUENCE my_seq';
EXECUTE IMMEDIATE
'CREATE SEQUENCE my_seq' ||
' MINVALUE 1 ' ||
' MAXVALUE 999999 ' ||
' START WITH 1 ' ||
' INCREMENT BY 1 ' ||
' NOCACHE';
END;
然后创建作业(see here作为参考):
BEGIN
dbms_scheduler.create_job(
job_name => 'job$my_seq_reset',
job_type => 'STORED_PROCEDURE',
job_action => 'my_seq_reset',
start_date => TO_DATE('01-01-09', 'DD-MM-RR'),
repeat_interval => 'FREQ=YEARLY;BYDATE=0101',
enabled => TRUE,
auto_drop => FALSE,
comments => 'My sequence yearly reset job.'
);
END;
你已经完成了。
答案 3 :(得分:1)
我不确定是否有一个好方法,这不是真正的序列设计。它们只是纯粹增加了唯一的数字。
想到了2个想法。我倾向于赞成选项2,因为它没有尝试做任何花哨的事情并且总是能够正常工作,任何试图操纵序列的选项都必然会咬你。
答案 4 :(得分:0)
首先,它似乎不是每年自动重启序列的方法。阅读本文以供参考:
http://www.psoug.org/reference/OLD/sequences.html?PHPSESSID=5949da378678fa6d24b6fcc6eaae9888
我的方法是:
创建一个表格,其中包含该年份的年份和起始顺序(让我们将此表格称为year_seed)
创建一个接收年份的过程,检查year_seed表,如果是年份的第一次检查,则生成带有起始序列的寄存器。此过程还必须返回序列减去年份的起始序列。
也许这不是那么简单,但我认为这是最好的解决方案。祝你好运
答案 5 :(得分:0)
我发现最好创建一个触发器和一个表。 该表将包含该年份的年份和顺序。触发器获取当前年份,验证表,如果没有找到注册表,则从1开始插入新的。否则,选择最后一个并递增1,更新相应的表。
表:
create table GDRDOCUMENTOSEQ
(
noano NUMBER(4),
noseq NUMBER(6)
)
;
alter table GDRDOCUMENTOSEQ
add unique (NOANO);
触发器:
CREATE OR REPLACE TRIGGER "GDRGUIARESSARCIMENTONODOC_BIR"
BEFORE INSERT ON GDR.GDRGUIARESSARCIMENTO
FOR EACH ROW
DECLARE
lNoAno number;
lNoSeq number;
lQtd number;
begin
SELECT EXTRACT(YEAR FROM SYSDATE) into lNoAno FROM DUAL;
SELECT COUNT(0)
INTO lQtd
FROM gdr.gdrdocumentoseq ds
WHERE ds.noano = lNoAno;
IF lQtd = 0 then
lNoSeq := 1;
INSERT INTO GDR.GDRDOCUMENTOSEQ (NOANO, NOSEQ) VALUES (lNoAno, lNoSeq);
else
SELECT nvl(max(ds.noseq), 0) + 1
INTO lNoSeq
FROM gdr.gdrdocumentoseq ds
WHERE ds.noano = lNoAno;
UPDATE GDR.GDRDOCUMENTOSEQ ds
SET ds.noseq = lNoSeq
WHERE ds.noano = lNoAno;
end if;
:new.nodocumento := SUBSTR(lNoAno, 3) || lpad(lNoSeq, 6, '0');
end;
我从2016年开始在生产中运行此代码。表的当前状态是:
NOANO NOSEQ
2017 1411
2016 237
答案 6 :(得分:0)
#include <stdio.h>
#include <stdlib.h>
struct node {
int id;
struct node *next;
};
struct node *make_node(int id) {
struct node *n = malloc(sizeof(*n));
n->id = id;
n->next = NULL;
return n;
}
void free_linked_list(struct node *head) {
while (head) {
struct node *tmp = head;
head = head->next;
free(tmp);
}
}
int main() {
struct node *head = make_node(1);
head->next = make_node(2);
head->next->next = make_node(3);
printf("%d, ", head->id);
printf("%d, ", head->next->id);
printf("%d\n", head->next->next->id);
free_linked_list(head);
return 0;
}