Come creare un ID con AUTO_INCREMENT su Oracle?


422

Sembra che non vi sia alcun concetto di AUTO_INCREMENT in Oracle, fino alla versione 11g inclusa.

Come posso creare una colonna che si comporta come l'incremento automatico in Oracle 11g?


3
Puoi creare un BEFORE INSERTtrigger sul tavolo ed estrarre i valori da una sequenza per creare l'auto-incremento
Hunter McMillen,

7
Le colonne di identità sono ora disponibili in Oracle 12c docs.oracle.com/cd/E16655_01/gateways.121/e22508/…
David Aldridge


Stai utilizzando Oracle RAC? L'uso di CACHED alla fine dell'istruzione può migliorare le prestazioni. Se si eseguono molti inserti in un breve periodo (e l'ordinamento non è importante per te), considerare il trigger di inserimento sequenza sfalsato per ulteriori vantaggi in termini di prestazioni. Vedi: dba-oracle.com/t_rac_proper_sequence_usage.htm
Peeter Kokk,

Risposte:


596

Non esistono colonne "auto_increment" o "identity" in Oracle a partire da Oracle 11g . Tuttavia, puoi modellarlo facilmente con una sequenza e un trigger:

Definizione della tabella:

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;

Definizione del trigger:

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

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

AGGIORNARE:

IDENTITY la colonna è ora disponibile su Oracle 12c:

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

o specificare i valori iniziali e incrementali, impedendo anche qualsiasi inserimento nella colonna identità ( GENERATED ALWAYS) (di nuovo, solo Oracle 12c +)

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

In alternativa, Oracle 12 consente anche di utilizzare una sequenza come valore predefinito:

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));

5
Sono un n00b, puoi per favore dirmi da dove dept_seqviene!
J86,

3
CREA SEQUENZA dept_seq; crea dept_seq ... come una tabella .. ma in questo caso è solo un numero che puoi aumentare con dept_seq.NEXTVAL ... vedi il trigger.
Benjamin Eckstein,

Come accennato, il codice originale avrebbe esito negativo quando si incontra una riga con ID specificato. Ma che dire di questo caso: il trigger assegnerebbe l'id (automaticamente) solo se non fosse stato specificato esplicitamente un ID in INSERT. Ciò fallirebbe, vero? E qual è il modo corretto per farlo?
FanaticD

10
Per i neofiti di Oracle come me, la parte 'id' di 'new.id' si riferisce alla colonna 'id' nella tabella sopra. 'nuovo' è una parola riservata che si riferisce alla nuova riga creata
Hoppe,

2
Non è necessario utilizzare SELECT .. INTOnel trigger che si può semplicemente fare :new.id := dept_seq.NEXTVAL;.
MT0,

90

SYS_GUIDrestituisce un GUID: un ID univoco globale. A SYS_GUIDè a RAW(16). Non genera un valore numerico incrementale.

Se vuoi creare un tasto numerico incrementale, ti consigliamo di creare una sequenza.

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

Dovresti quindi utilizzare quella sequenza nella tua INSERTdichiarazione

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

Oppure puoi definire un trigger che popola automaticamente il valore della chiave primaria usando la sequenza

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 si utilizza Oracle 11.1 o versioni successive, è possibile semplificare un po 'il trigger

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 vuoi davvero usarlo SYS_GUID

CREATE TABLE table_name (
  primary_key_column raw(16) default sys_guid() primary key,
  <<other columns>>
)

1
Che cosa significa CACHE 100; in CREATE SEQUENCE name_of_sequence START WITH 1 INCREMENT BY 1 CACHE 100;fare?
Angelina,

3
CACHE 100: la parola chiave recupera i successivi 100 numeri in memoria. Normalmente una SEQUENZA viene salvata nel database ogni volta che il suo valore viene modificato, se lo si memorizza nella cache, verrà salvato e recuperato solo se quelli memorizzati nella cache sono esauriti. Ti dà un significativo miglioramento delle prestazioni, ma se il database fallisce, perdi tutti i valori memorizzati nella cache che non hai nemmeno usato.
Ramazan Polat,

2
A SYS_GUID()è a RAW(16), non 32.
turbante

2
@turbanoff - Buona cattura. Aggiornato la mia risposta. La SYS_GUIDdocumentazione dichiara raw(32)che mi ha confuso.
Grotta di Giustino,

@JustinCave Ho usato il tuo approccio per creare la sequenza e il trigger. Cosa succede se elimino una riga a livello di codice (java), anche l'ID (chiave primaria) viene regolato?
Kittu,

52

In Oracle 12c in poi potresti fare qualcosa del tipo,

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 in Oracle (pre 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;
/

2
@JonHeller Dico personalmente che l' IDENTITYesempio è molto più chiaro in questa risposta.
EpicPandaForce,

5
Il WHEN (new.MAP_ID IS NULL)non è nella risposta accettata. Upvoted.
dcsohl,

1
@dcsohl, WHEN ( new.MAP_ID is null)in questo caso non è un buon codice ed è già spiegato nella sezione commenti da @ABCade sotto la risposta accettata .. leggi;)
ajmalmhd04

Quando eseguo questo da CREATE OR REPLACE TRIGGERa END;, ottengo una finestra "Enter Binds". Se faccio clic su "Applica" e non faccio nient'altro in quella finestra, e quindi eseguo il ALTER TRIGGERcomando, tutto va bene, ma vorrei che ci fosse un modo per eliminare programmaticamente quel pop-up ed eseguire tutto insieme. Se lo provi del tutto, ottieni PLS-00103: Encountered the symbol 'ALTER'e non piace EXECUTE IMMEDIATEneanche (lo stesso errore, Encountered the symbol 'EXECUTE'invece lo dice solo ).
vapcguy,

Ho ottenuto [42000][907] ORA-00907: missing right parenthesiseseguendo la versione per Oracle 12c in poi. Qualche idea ?
belgoros,

32

Ecco tre gusti:

  1. numerico . Valore numerico crescente semplice, ad es. 1,2,3, ....
  2. GUID . identificatore universale globale, come RAWtipo di dati.
  3. GUID (stringa) . Come sopra, ma come una stringa che potrebbe essere più facile da gestire in alcune lingue.

xè la colonna identità. Sostituisci FOOcon il nome della tabella in ciascuno degli esempi.

-- 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;
/

aggiornare:

Oracle 12c introduce queste due varianti che non dipendono dai trigger:

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

Il primo usa una sequenza in modo tradizionale; il secondo gestisce il valore internamente.


7

Supponendo che intendi una colonna come la colonna dell'identità di SQL Server?

In Oracle, si utilizza una SEQUENZA per ottenere la stessa funzionalità. Vedrò se riesco a trovare un buon link e pubblicarlo qui.

Aggiornamento: sembra che tu l'abbia trovato tu stesso. Ecco comunque il link: http://www.techonthenet.com/oracle/sequences.php


7

Oracle Database 12c ha introdotto Identity, una colonna auto-incrementale (generata dal sistema). Nelle versioni precedenti del database (fino a 11g), di solito si implementa un'identità creando una sequenza e un trigger. Dal 12c in poi, puoi creare la tua tabella e definire la colonna che deve essere generata come identità.

Il seguente articolo spiega come usarlo:

Colonne identità: una nuova voce nel database Oracle 12c


5
Sebbene questo collegamento possa rispondere alla domanda, è meglio includere qui le parti essenziali della risposta e fornire il collegamento come riferimento. Le risposte di solo collegamento possono diventare non valide se la pagina collegata cambia.
Ponte

5

Triggere Sequencepuò essere utilizzato quando si desidera un numero seriale che chiunque può facilmente leggere / ricordare / comprendere. Ma se non vuoi gestire la colonna ID (come emp_id) in questo modo e il valore di questa colonna non è molto considerevole, puoi usare la SYS_GUID()Creazione tabella per ottenere l'incremento automatico in questo modo.

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

Ora la tua emp_idcolonna accetterà "valore identificatore univoco globale". puoi inserire valore nella tabella ignorando la colonna emp_id in questo modo.

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

Quindi, inserirà un valore univoco nella emp_idcolonna.


Cosa succede quando viene eliminata una riga? Anche i SYS_GUID()suoi valori ID saranno ?
Kittu,

5

A partire da Oracle 12c esiste il supporto per le colonne Identity in due modi:

  1. Sequenza + Tabella - In questa soluzione crei comunque una sequenza come faresti normalmente, quindi usi il seguente DDL:

    CREATE TABLE MyTable (ID NUMBER DEFAULT MyTable_Seq.NEXTVAL , ...)

  2. Solo tabella : in questa soluzione non viene specificata esplicitamente alcuna sequenza. Dovresti usare il seguente DDL:

    CREATE TABLE MyTable (NUMERO ID GENERATO COME IDENTITÀ , ...)

Se usi il primo modo, è retrocompatibile con il modo esistente di fare le cose. Il secondo è un po 'più semplice ed è più in linea con il resto dei sistemi RDMS disponibili.


5

si chiama Identity Columnsed è disponibile solo da Oracle Oracle 12c

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

esempio di inserimento Identity Columnscome di seguito

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

1 riga creata.

NON è possibile inserire come di seguito

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

ERRORE alla riga 1: ORA-32795: impossibile inserire in una colonna di identità sempre generata

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

ERRORE alla riga 1: ORA-32795: impossibile inserire in una colonna di identità sempre generata

link utile


1

Ecco una soluzione completa per la gestione di eccezioni / errori per l'incremento automatico, questa soluzione è retrocompatibile e funzionerà su 11g e 12c, in particolare se l'applicazione è in produzione.

Sostituisci "TABLE_NAME" con il nome della tabella appropriato

--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;
/

0

Ecco come l'ho fatto su una tabella e una colonna esistenti (denominata ID):

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;

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;

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;


-1
  create trigger t1_trigger
  before insert on AUDITLOGS
  for each row
   begin
     select t1_seq.nextval into :new.id from dual;
   end;

devo solo cambiare il nome della tabella (AUDITLOGS) con il nome della tua tabella e new.id con new.column_name


-2

Forse prova questo semplice script:

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

Il risultato è:

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;

3
In che modo differisce dalla risposta di eugnio? Inoltre: non è necessario selectnelle moderne versioni Oracle. Puoi semplicemente usare:new.pk := TABLE_PK_SEQ.NEXTVAL
a_horse_with_no_name
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.