Comment créer un identifiant avec AUTO_INCREMENT sur Oracle?

422

Il semble qu'il n'y ait pas de concept AUTO_INCREMENT dans Oracle, jusqu'à la version 11g incluse.

Comment puis-je créer une colonne qui se comporte comme l'incrémentation automatique dans Oracle 11g?

Sushan Ghimire
la source
3
Vous pouvez créer un BEFORE INSERTdéclencheur sur la table et extraire des valeurs d'une séquence pour créer une incrémentation automatique
Hunter McMillen
7
Les colonnes d'identité sont désormais disponibles dans Oracle 12c docs.oracle.com/cd/E16655_01/gateways.121/e22508/…
David Aldridge
Utilisez-vous Oracle RAC? L'utilisation de CACHED à la fin de l'instruction peut améliorer les performances. Si vous effectuez un grand nombre d'insertions sur une courte période (et l'ordre n'est pas important pour vous), envisagez un déclencheur d'insertion de séquence échelonné pour des avantages de performances supplémentaires. Voir: dba-oracle.com/t_rac_proper_sequence_usage.htm
Peeter Kokk

Réponses:

596

Il n'y a pas de colonnes "auto_increment" ou "identité" dans Oracle à partir d'Oracle 11g . Cependant, vous pouvez le modéliser facilement avec une séquence et un déclencheur:

Définition du tableau:

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;

Définition du déclencheur:

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

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

MISE À JOUR:

IDENTITY est désormais disponible sur Oracle 12c:

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

ou spécifiez des valeurs de début et d'incrément, empêchant également toute insertion dans la colonne d'identité ( GENERATED ALWAYS) (encore une fois, Oracle 12c + uniquement)

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

Alternativement, Oracle 12 permet également d'utiliser une séquence comme valeur par défaut:

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
la source
5
Je suis un n00b, pouvez-vous me dire d'où ça dept_seqvient!
J86
3
CREATE SEQUENCE dept_seq; crée dept_seq ... comme une table .. mais dans ce cas, ce n'est qu'un nombre que vous pouvez augmenter avec dept_seq.NEXTVAL ... voir le déclencheur.
Benjamin Eckstein
Comme cela a été mentionné, le code d'origine échouait lorsqu'il rencontrait une ligne avec l'ID spécifié. Mais qu'en est-il de ce cas: le déclencheur attribuerait l'ID (automatiquement) uniquement s'il n'y avait pas d'ID spécifié explicitement dans INSERT. Cela échouerait, non? Et quelle est la bonne façon de procéder?
FanaticD
10
Pour les débutants d'Oracle comme moi, la partie 'id' de 'new.id' fait référence à la colonne 'id' dans le tableau ci-dessus. 'nouveau' est un mot réservé faisant référence à la nouvelle ligne créée
Hoppe
2
Vous n'avez pas besoin d'utiliser SELECT .. INTOle déclencheur que vous pouvez simplement faire :new.id := dept_seq.NEXTVAL;.
MT0
90

SYS_GUIDrenvoie un GUID - un ID unique au monde. A SYS_GUIDest un RAW(16). Il ne génère pas de valeur numérique incrémentielle.

Si vous souhaitez créer une clé numérique incrémentielle, vous souhaiterez créer une séquence.

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

Vous utiliseriez alors cette séquence dans votre INSERTdéclaration

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

Ou vous pouvez définir un déclencheur qui remplit automatiquement la valeur de la clé primaire à l'aide de la séquence

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;

Si vous utilisez Oracle 11.1 ou une version ultérieure, vous pouvez simplifier un peu le déclencheur

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

Si vous voulez vraiment utiliser SYS_GUID

CREATE TABLE table_name (
  primary_key_column raw(16) default sys_guid() primary key,
  <<other columns>>
)
Justin Cave
la source
1
Que fait CACHE 100; in CREATE SEQUENCE name_of_sequence START WITH 1 INCREMENT BY 1 CACHE 100;-il?
Angelina
3
CACHE 100: le mot-clé récupère les 100 numéros suivants en mémoire. Normalement, une SEQUENCE est enregistrée dans la base de données chaque fois que sa valeur change, si vous la cachez, elle ne sera enregistrée et récupérée que si celles mises en cache sont épuisées. Vous donne un gain de performances significatif, mais si la base de données échoue, vous perdez toutes les valeurs mises en cache que vous n'avez même pas utilisées.
Ramazan Polat
2
A SYS_GUID()est un RAW(16), pas 32.
turbanoff
2
@turbanoff - Bonne prise. Mis à jour ma réponse. La SYS_GUIDdocumentation déclare un raw(32)qui m'a dérouté.
Justin Cave
@JustinCave J'ai utilisé votre approche pour créer la séquence et le déclencheur. Et si je supprime une ligne par programme (java), l'ID (clé primaire) sera-t-il également ajusté?
kittu
52

Dans Oracle 12c, vous pourriez faire quelque chose comme,

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

Et dans 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;
/
Nisar
la source
2
@ JonHeller Je dis personnellement que l' IDENTITYexemple est beaucoup plus clair dans cette réponse.
EpicPandaForce
5
Le WHEN (new.MAP_ID IS NULL)n'est pas dans la réponse acceptée. A voté.
dcsohl
1
@dcsohl, WHEN ( new.MAP_ID is null)n'est pas un bon code dans ce cas et est déjà expliqué dans la section commentaire par @ABCade sous réponse acceptée .. lisez;)
ajmalmhd04
Lorsque je lance ceci de CREATE OR REPLACE TRIGGERà END;, j'obtiens une fenêtre "Enter Binds". Si je clique sur "Appliquer" et que je ne fais rien d'autre dans cette fenêtre, puis que j'exécute la ALTER TRIGGERcommande, tout va bien, mais j'aimerais qu'il y ait un moyen de se débarrasser par programme de cette fenêtre contextuelle et de tout exécuter ensemble. Si vous l'essayez tout à fait, vous obtenez PLS-00103: Encountered the symbol 'ALTER'et il n'aime pas non EXECUTE IMMEDIATEplus (même erreur, dit simplement à la Encountered the symbol 'EXECUTE'place).
vapcguy
J'ai obtenu [42000][907] ORA-00907: missing right parenthesislors de l'exécution de la version pour Oracle 12c. Une idée ?
belgoros
32

Voici trois saveurs:

  1. numérique . Valeur numérique simple croissante, par exemple 1,2,3, ....
  2. GUID . identifiant universel universel, en tant que RAWtype de données.
  3. GUID (chaîne) . Comme ci-dessus, mais comme une chaîne qui pourrait être plus facile à gérer dans certaines langues.

xest la colonne d'identité. Remplacez FOOpar le nom de votre table dans chacun des exemples.

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

mise à jour:

Oracle 12c présente ces deux variantes qui ne dépendent pas des déclencheurs:

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

Le premier utilise une séquence de manière traditionnelle; le second gère la valeur en interne.

Mark Harrison
la source
7

En supposant que vous voulez dire une colonne comme la colonne d'identité SQL Server?

Dans Oracle, vous utilisez une SEQUENCE pour obtenir la même fonctionnalité. Je vais voir si je peux trouver un bon lien et le poster ici.

Mise à jour: on dirait que vous l'avez trouvée vous-même. Voici quand même le lien: http://www.techonthenet.com/oracle/sequences.php

Phil Sandler
la source
7

Oracle Database 12c a introduit Identity, une colonne auto-incrémentielle (générée par le système). Dans les versions de base de données précédentes (jusqu'à 11g), vous implémentez généralement une identité en créant une séquence et un déclencheur. À partir de 12c, vous pouvez créer votre propre table et définir la colonne qui doit être générée en tant qu'identité.

L'article suivant explique comment l'utiliser:

Colonnes d'identité - Une nouvelle entrée dans Oracle Database 12c

Corrado Piola
la source
5
Bien que ce lien puisse répondre à la question, il est préférable d'inclure les parties essentielles de la réponse ici et de fournir le lien de référence. Les réponses de lien uniquement peuvent devenir invalides si la page liée change.
Pont du
5

Triggeret Sequencepeut être utilisé lorsque vous voulez un numéro de série que n'importe qui peut facilement lire / mémoriser / comprendre. Mais si vous ne souhaitez pas gérer la colonne ID (comme emp_id) de cette manière, et que la valeur de cette colonne n'est pas très considérable, vous pouvez l'utiliser SYS_GUID()lors de la création de la table pour obtenir l'incrémentation automatique comme ceci.

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

Votre emp_idcolonne acceptera désormais la "valeur d'identifiant unique au monde". vous pouvez insérer de la valeur dans la table en ignorant la colonne emp_id comme celle-ci.

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

Ainsi, il insérera une valeur unique dans votre emp_idcolonne.

124
la source
Que se passe-t-il lorsqu'une ligne est supprimée? Est-ce que SYS_GUID()ses valeurs d'identification seront également?
kittu
5

À partir d'Oracle 12c, les colonnes d'identité sont prises en charge de deux manières:

  1. Sequence + Table - Dans cette solution, vous créez toujours une séquence comme vous le feriez normalement, puis vous utilisez le DDL suivant:

    CRÉER LA TABLE MyTable (ID NUMBER DEFAULT MyTable_Seq.NEXTVAL , ...)

  2. Table uniquement - Dans cette solution, aucune séquence n'est explicitement spécifiée. Vous utiliseriez le DDL suivant:

    CRÉER TABLE MyTable (NUMÉRO D'IDENTIFIÉ GÉNÉRÉ COMME IDENTITÉ , ...)

Si vous utilisez la première manière, elle est rétrocompatible avec la manière existante de faire les choses. Le second est un peu plus simple et est plus en ligne avec le reste des systèmes RDMS.

Nate Zaugg
la source
5

il est appelé Identity Columnset n'est disponible que depuis Oracle Oracle 12c

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

exemple d'insertion dans Identity Columnscomme ci-dessous

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

1 ligne créée.

vous ne pouvez PAS insérer comme ci-dessous

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

ERREUR à la ligne 1: ORA-32795: impossible d'insérer dans une colonne d'identité toujours générée

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

ERREUR à la ligne 1: ORA-32795: impossible d'insérer dans une colonne d'identité toujours générée

lien utile

sam
la source
1

Voici une solution complète pour la gestion des exceptions / erreurs pour l'incrémentation automatique, cette solution est rétrocompatible et fonctionnera sur 11g et 12c, en particulier si l'application est en production.

Veuillez remplacer «TABLE_NAME» par votre nom de table approprié

--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
la source
0

Voici comment je l'ai fait sur une table et une colonne existantes (nommées 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;
éther6
la source
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
la source
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
la source
-1

oracle a des séquences ET des colonnes d'identité en 12c

http://www.oracle-base.com/articles/12c/identity-columns-in-oracle-12cr1.php#identity-columns

J'ai trouvé cela mais je ne sais pas ce qu'est rdb 7 http://www.oracle.com/technetwork/products/rdb/0307-identity-columns-128126.pdf

Kalpesh Soni
la source
Informations utiles et comparaison intéressante des performances. Il est préférable d'utiliser des colonnes IDENTITY que d'utiliser un TRIGGER pour une SEQUENCE.
ygoe
-1
  create trigger t1_trigger
  before insert on AUDITLOGS
  for each row
   begin
     select t1_seq.nextval into :new.id from dual;
   end;

seulement je dois juste changer le nom de la table (AUDITLOGS) avec votre nom de table et new.id avec new.column_name

abhishek ringsia
la source
-2

Peut-être essayez simplement ce script simple:

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

Le résultat est:

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
la source
3
En quoi est-ce différent de la réponse d'Eugnio? De plus: vous n'avez pas besoin des selectversions Oracle modernes. Vous pouvez simplement utiliser:new.pk := TABLE_PK_SEQ.NEXTVAL
a_horse_with_no_name