Como criar um ID com AUTO_INCREMENT no Oracle?

422

Parece que não há conceito de AUTO_INCREMENT no Oracle, até e incluindo a versão 11g.

Como posso criar uma coluna que se comporte como incremento automático no Oracle 11g?

Sushan Ghimire
fonte
3
Você pode criar um BEFORE INSERTgatilho sobre os valores da tabela e sair de uma seqüência para criar auto-incremento
Hunter McMillen
7
As colunas de identidade agora estão disponíveis no Oracle 12c docs.oracle.com/cd/E16655_01/gateways.121/e22508/…
David Aldridge
Você está usando o Oracle RAC? Usar o CACHED no final da instrução pode melhorar o desempenho. Se você fizer muitas inserções em um curto período (e o pedido não for importante para você), considere o gatilho de inserção de sequência escalonado para obter benefícios adicionais de desempenho. Veja: dba-oracle.com/t_rac_proper_sequence_usage.htm
Peeter Kokk

Respostas:

596

Não existem colunas "auto_increment" ou "identity" no Oracle a partir do Oracle 11g . No entanto, você pode modelá-lo facilmente com uma sequência e um gatilho:

Definição da tabela:

CREATE TABLE departments (
  ID           NUMBER(10)    NOT NULL,
  DESCRIPTION  VARCHAR2(50)  NOT NULL);

ALTER TABLE departments ADD (
  CONSTRAINT dept_pk PRIMARY KEY (ID));

CREATE SEQUENCE dept_seq START WITH 1;

Definição de gatilho:

CREATE OR REPLACE TRIGGER dept_bir 
BEFORE INSERT ON departments 
FOR EACH ROW

BEGIN
  SELECT dept_seq.NEXTVAL
  INTO   :new.id
  FROM   dual;
END;
/

ATUALIZAR:

IDENTITY A coluna está agora disponível no Oracle 12c:

create table t1 (
    c1 NUMBER GENERATED by default on null as IDENTITY,
    c2 VARCHAR2(10)
    );

ou especifique valores iniciais e de incremento, impedindo também qualquer inserção na coluna de identidade ( GENERATED ALWAYS) (novamente, apenas Oracle 12c +)

create table t1 (
    c1 NUMBER GENERATED ALWAYS as IDENTITY(START with 1 INCREMENT by 1),
    c2 VARCHAR2(10)
    );

Como alternativa, o Oracle 12 também permite usar uma sequência como um valor padrão:

CREATE SEQUENCE dept_seq START WITH 1;

CREATE TABLE departments (
  ID           NUMBER(10)    DEFAULT dept_seq.nextval NOT NULL,
  DESCRIPTION  VARCHAR2(50)  NOT NULL);

ALTER TABLE departments ADD (
  CONSTRAINT dept_pk PRIMARY KEY (ID));
Eugenio Cuevas
fonte
5
Eu sou um n00b, você pode me dizer de onde dept_seqveio!
J86
3
CRIAR SEQUÊNCIA dept_seq; cria dept_seq ... como uma tabela .. mas, neste caso, é apenas um número que você pode aumentar com dept_seq.NEXTVAL ... veja o gatilho.
Benjamin Eckstein 14/03
Como foi mencionado, o código original falharia ao encontrar uma linha com o ID especificado. Mas e o caso: o gatilho atribuiria o ID (automaticamente) apenas se não houvesse um ID especificado explicitamente em INSERT. Isso falharia, certo? E qual é a maneira correta de fazer isso?
precisa saber é o seguinte
10
Para iniciantes da Oracle como eu, a parte 'id' de 'new.id' refere-se à coluna 'id' na tabela acima. 'new' é uma palavra reservada referente à nova linha criada
Hoppe
2
Você não precisa usar SELECT .. INTOo gatilho que pode fazer :new.id := dept_seq.NEXTVAL;.
MT0 17/05/19
90

SYS_GUIDretorna um GUID - um ID globalmente exclusivo. A SYS_GUIDé a RAW(16). Não gera um valor numérico incremental.

Se você deseja criar uma chave numérica incremental, crie uma sequência.

CREATE SEQUENCE name_of_sequence
  START WITH 1
  INCREMENT BY 1
  CACHE 100;

Você usaria essa sequência em sua INSERTdeclaração

INSERT INTO name_of_table( primary_key_column, <<other columns>> )
  VALUES( name_of_sequence.nextval, <<other values>> );

Ou você pode definir um gatilho que preenche automaticamente o valor da chave primária usando a sequência

CREATE OR REPLACE TRIGGER trigger_name
  BEFORE INSERT ON table_name
  FOR EACH ROW
BEGIN
  SELECT name_of_sequence.nextval
    INTO :new.primary_key_column
    FROM dual;
END;

Se você estiver usando o Oracle 11.1 ou posterior, poderá simplificar um pouco o gatilho

CREATE OR REPLACE TRIGGER trigger_name
  BEFORE INSERT ON table_name
  FOR EACH ROW
BEGIN
  :new.primary_key_column := name_of_sequence.nextval;
END;

Se você realmente quer usar SYS_GUID

CREATE TABLE table_name (
  primary_key_column raw(16) default sys_guid() primary key,
  <<other columns>>
)
Justin Cave
fonte
1
O que CACHE 100; in CREATE SEQUENCE name_of_sequence START WITH 1 INCREMENT BY 1 CACHE 100;faz?
21913 Angelina
3
CACHE 100: a palavra-chave busca os próximos 100 números na memória. Normalmente, uma SEQUENCE é salva no banco de dados sempre que seu valor é alterado; se você o armazena em cache, ela será salva e recuperada apenas se os que estiverem em cache estiverem esgotados. Dá a você um ganho significativo de desempenho, mas se o banco de dados falhar, você perderá todos os valores em cache que você nem usou.
Ramazan Polat
2
Um SYS_GUID()é um RAW(16), não 32.
turbanoff
2
@turbanoff - Boa captura. Atualizei minha resposta. A SYS_GUIDdocumentação declara um raw(32)que me confundiu.
Justin Cave
@JustinCave Eu usei sua abordagem na criação da sequência e do gatilho. E se eu excluir uma linha programaticamente (java), o ID (chave primária) também será ajustado?
Kittu
52

No Oracle 12c em diante, você poderia fazer algo como:

CREATE TABLE MAPS
(
  MAP_ID INTEGER GENERATED ALWAYS AS IDENTITY (START WITH 1 INCREMENT BY 1) NOT NULL,
  MAP_NAME VARCHAR(24) NOT NULL,
  UNIQUE (MAP_ID, MAP_NAME)
);

E no Oracle (pré 12c).

-- create table
CREATE TABLE MAPS
(
  MAP_ID INTEGER NOT NULL ,
  MAP_NAME VARCHAR(24) NOT NULL,
  UNIQUE (MAP_ID, MAP_NAME)
);

-- create sequence
CREATE SEQUENCE MAPS_SEQ;

-- create tigger using the sequence
CREATE OR REPLACE TRIGGER MAPS_TRG 
BEFORE INSERT ON MAPS 
FOR EACH ROW
WHEN (new.MAP_ID IS NULL)
BEGIN
  SELECT MAPS_SEQ.NEXTVAL
  INTO   :new.MAP_ID
  FROM   dual;
END;
/
Nisar
fonte
2
@ JonHeller Pessoalmente, digo que o IDENTITYexemplo é muito mais claro nesta resposta.
EpicPandaForce 23/03
5
O WHEN (new.MAP_ID IS NULL)não está na resposta aceita. Votado.
precisa saber é o seguinte
1
@dcsohl, WHEN ( new.MAP_ID is null)não é um código bom, neste caso, e já é explicado na seção de comentários por @ABCade sob resposta aceita .. ter uma leitura;)
ajmalmhd04
Quando executo isso de CREATE OR REPLACE TRIGGERpara END;, recebo uma janela "Enter Binds". Se eu clicar em "Aplicar" e não fizer mais nada nessa janela, e executar o ALTER TRIGGERcomando, tudo ficará bem, mas gostaria que houvesse uma maneira de se livrar programaticamente desse pop-up e executar tudo junto. Se você tentar completamente, você obtém PLS-00103: Encountered the symbol 'ALTER'e também não gosta EXECUTE IMMEDIATE(mesmo erro, apenas diz isso Encountered the symbol 'EXECUTE').
vapcguy
Eu obtive [42000][907] ORA-00907: missing right parenthesisao executar a versão do Oracle 12c em diante. Qualquer ideia ?
belgoros
32

Aqui estão três sabores:

  1. numérico . Valor numérico crescente simples, por exemplo, 1,2,3, ....
  2. GUID . identificador universal universal, como um RAWtipo de dados.
  3. GUID (string) . O mesmo que acima, mas como uma string que pode ser mais fácil de manusear em alguns idiomas.

xé a coluna de identidade. Substitua FOOpelo nome da tabela em cada um dos exemplos.

-- numerical identity, e.g. 1,2,3...
create table FOO (
    x number primary key
);
create sequence  FOO_seq;

create or replace trigger FOO_trg
before insert on FOO
for each row
begin
  select FOO_seq.nextval into :new.x from dual;
end;
/

-- GUID identity, e.g. 7CFF0C304187716EE040488AA1F9749A
-- use the commented out lines if you prefer RAW over VARCHAR2.
create table FOO (
    x varchar(32) primary key        -- string version
    -- x raw(32) primary key         -- raw version
);

create or replace trigger FOO_trg
before insert on FOO
for each row
begin
  select cast(sys_guid() as varchar2(32)) into :new.x from dual;  -- string version
  -- select sys_guid() into :new.x from dual;                     -- raw version
end;
/

atualizar:

O Oracle 12c apresenta essas duas variantes que não dependem de gatilhos:

create table mytable(id number default mysequence.nextval);
create table mytable(id number generated as identity);

O primeiro usa uma sequência da maneira tradicional; o segundo gerencia o valor internamente.

Mark Harrison
fonte
7

Supondo que você queira dizer uma coluna como a coluna de identidade do SQL Server?

No Oracle, você usa uma SEQUENCE para obter a mesma funcionalidade. Vou ver se consigo encontrar um bom link e publicá-lo aqui.

Atualização: parece que você o encontrou. Aqui está o link de qualquer maneira: http://www.techonthenet.com/oracle/sequences.php

Phil Sandler
fonte
7

O Oracle Database 12c introduziu o Identity, uma coluna de incremento automático (gerado pelo sistema). Nas versões anteriores do banco de dados (até 11g), você geralmente implementa uma identidade criando uma sequência e um gatilho. A partir de 12c, você pode criar sua própria tabela e definir a coluna que deve ser gerada como uma identidade.

O artigo a seguir explica como usá-lo:

Colunas de identidade - Uma nova entrada no Oracle Database 12c

Corrado Piola
fonte
5
Embora esse link possa responder à pergunta, é melhor incluir aqui as partes essenciais da resposta e fornecer o link para referência. As respostas somente para links podem se tornar inválidas se a página vinculada for alterada.
Bridge
5

Triggere Sequencepode ser usado quando você deseja um número de série que qualquer pessoa possa ler / lembrar / entender facilmente. Mas se você não deseja gerenciar a Coluna de ID (como emp_id) dessa maneira, e o valor dessa coluna não for muito considerável, você pode usar SYS_GUID()na Criação de Tabela para obter o Incremento Automático como este.

CREATE TABLE <table_name> 
(emp_id RAW(16) DEFAULT SYS_GUID() PRIMARY KEY,
name VARCHAR2(30));

Agora sua emp_idcoluna aceitará "valor do identificador exclusivo globalmente". você pode inserir valor na tabela ignorando a coluna emp_id como esta.

INSERT INTO <table_name> (name) VALUES ('name value');

Portanto, ele inserirá um valor exclusivo na sua emp_idcoluna.

124
fonte
O que acontece quando uma linha é excluída? Os SYS_GUID()valores de seu id também?
Kittu
5

A partir do Oracle 12c, há suporte para colunas Identity de uma das duas maneiras:

  1. Sequência + Tabela - Nesta solução, você ainda cria uma sequência como faria normalmente, e usa o seguinte DDL:

    CREATE TABLE MyTable (ID NÚMERO PADRÃO MyTable_Seq.NEXTVAL , ...)

  2. Somente tabela - nesta solução, nenhuma sequência é especificada explicitamente. Você usaria o seguinte DDL:

    CREATE TABLE MyTable (NÚMERO DE ID GERADO COMO IDENTIDADE , ...)

Se você usar a primeira maneira, ela é compatível com a maneira existente de fazer as coisas. O segundo é um pouco mais direto e mais alinhado com o restante dos sistemas RDMS existentes.

Nate Zaugg
fonte
5

é chamado Identity Columnse está disponível apenas no Oracle Oracle 12c

CREATE TABLE identity_test_tab
(
   id            NUMBER GENERATED ALWAYS AS IDENTITY,
   description   VARCHAR2 (30)
);

exemplo de inserção Identity Columnscomo abaixo

INSERT INTO identity_test_tab (description) VALUES ('Just DESCRIPTION');

1 linha criada.

você NÃO pode inserir como abaixo

INSERT INTO identity_test_tab (id, description) VALUES (NULL, 'ID=NULL and DESCRIPTION');

ERRO na linha 1: ORA-32795: não é possível inserir uma coluna de identidade sempre gerada

INSERT INTO identity_test_tab (id, description) VALUES (999, 'ID=999 and DESCRIPTION');

ERRO na linha 1: ORA-32795: não é possível inserir uma coluna de identidade sempre gerada

link útil

sam
fonte
1

Aqui está a solução completa, com exceção de manipulação / erro para incremento automático, esta solução é compatível com versões anteriores e funcionará em 11g e 12c, especificamente se o aplicativo estiver em produção.

Substitua 'TABLE_NAME' pelo seu nome de tabela apropriado

--checking if table already exisits
BEGIN
    EXECUTE IMMEDIATE 'DROP TABLE TABLE_NAME';
    EXCEPTION WHEN OTHERS THEN NULL;
END;
/

--creating table
CREATE TABLE TABLE_NAME (
       ID NUMBER(10) PRIMARY KEY NOT NULL,
       .
       .
       .
);

--checking if sequence already exists
BEGIN
    EXECUTE IMMEDIATE 'DROP SEQUENCE TABLE_NAME_SEQ';
    EXCEPTION WHEN OTHERS THEN NULL;
END;

--creating sequence
/
CREATE SEQUENCE TABLE_NAME_SEQ START WITH 1 INCREMENT BY 1 MINVALUE 1 NOMAXVALUE NOCYCLE CACHE 2;

--granting rights as per required user group
/
GRANT SELECT, INSERT, UPDATE, DELETE ON TABLE_NAME TO USER_GROUP;

-- creating trigger
/
CREATE OR REPLACE TRIGGER TABLE_NAME_TS BEFORE INSERT OR UPDATE ON TABLE_NAME FOR EACH ROW
BEGIN    
    -- auto increment column
    SELECT TABLE_NAME_SEQ.NextVal INTO :New.ID FROM dual;

    -- You can also put some other required default data as per need of your columns, for example
    SELECT SYS_CONTEXT('USERENV', 'SESSIONID') INTO :New.SessionID FROM dual;
    SELECT SYS_CONTEXT('USERENV','SERVER_HOST') INTO :New.HostName FROM dual;
    SELECT SYS_CONTEXT('USERENV','OS_USER') INTO :New.LoginID FROM dual;    
    .
    .
    .
END;
/
emkays
fonte
0

Foi assim que fiz isso em uma tabela e coluna existentes (id nomeado):

UPDATE table SET id=ROWNUM;
DECLARE
  maxval NUMBER;
BEGIN
  SELECT MAX(id) INTO maxval FROM table;
  EXECUTE IMMEDIATE 'DROP SEQUENCE table_seq';
  EXECUTE IMMEDIATE 'CREATE SEQUENCE table_seq START WITH '|| TO_CHAR(TO_NUMBER(maxval)+1) ||' INCREMENT BY 1 NOMAXVALUE';
END;
CREATE TRIGGER table_trigger
  BEFORE INSERT ON table
  FOR EACH ROW
BEGIN
  :new.id := table_seq.NEXTVAL;
END;
ether6
fonte
0
FUNCTION GETUNIQUEID_2 RETURN VARCHAR2
AS
v_curr_id NUMBER;
v_inc NUMBER;
v_next_val NUMBER;
pragma autonomous_transaction;
begin 
CREATE SEQUENCE sequnce
START WITH YYMMDD0000000001
INCREMENT BY 1
NOCACHE
select sequence.nextval into v_curr_id from dual;
if(substr(v_curr_id,0,6)= to_char(sysdate,'yymmdd')) then
v_next_val := to_number(to_char(SYSDATE+1, 'yymmdd') || '0000000000');
v_inc := v_next_val - v_curr_id;
execute immediate ' alter sequence sequence increment by ' || v_inc ;
select sequence.nextval into v_curr_id from dual;
execute immediate ' alter sequence sequence increment by 1';
else
dbms_output.put_line('exception : file not found');
end if;
RETURN 'ID'||v_curr_id;
END;
kumar venkatesan
fonte
0
FUNCTION UNIQUE2(
 seq IN NUMBER
) RETURN VARCHAR2
AS
 i NUMBER := seq;
 s VARCHAR2(9);
 r NUMBER(2,0);
BEGIN
  WHILE i > 0 LOOP
    r := MOD( i, 36 );
    i := ( i - r ) / 36;
    IF ( r < 10 ) THEN
      s := TO_CHAR(r) || s;
    ELSE
      s := CHR( 55 + r ) || s;
    END IF;
  END LOOP;
  RETURN 'ID'||LPAD( s, 14, '0' );
END;
kumar venkatesan
fonte
-1
  create trigger t1_trigger
  before insert on AUDITLOGS
  for each row
   begin
     select t1_seq.nextval into :new.id from dual;
   end;

só preciso alterar o nome da tabela (AUDITLOGS) com o nome da tabela e new.id com new.column_name

anéis de abhishek
fonte
-2

Talvez apenas tente este script simples:

http://www.hlavaj.sk/ai.php

O resultado é:

CREATE SEQUENCE TABLE_PK_SEQ; 
CREATE OR REPLACE TRIGGER TR_SEQ_TABLE BEFORE INSERT ON TABLE FOR EACH ROW 

BEGIN
SELECT TABLE_PK_SEQ.NEXTVAL
INTO :new.PK
FROM dual;
END;
Martin Hlavaj
fonte
3
Como isso é diferente da resposta de eugnio? Plus: você não precisa das selectversões modernas do Oracle. Você pode simplesmente usar:new.pk := TABLE_PK_SEQ.NEXTVAL
a_horse_with_no_name