Wie erstelle ich eine ID mit AUTO_INCREMENT unter Oracle?

422

Es scheint, dass es in Oracle bis einschließlich Version 11g kein Konzept für AUTO_INCREMENT gibt.

Wie kann ich eine Spalte erstellen, die sich in Oracle 11g wie ein automatisches Inkrementieren verhält?

Sushan Ghimire
quelle
3
Sie können einen BEFORE INSERTTrigger für die Tabelle erstellen und Werte aus einer Sequenz ziehen, um ein automatisches Inkrement zu erstellen
Hunter McMillen
7
Identitätsspalten sind jetzt in Oracle 12c docs.oracle.com/cd/E16655_01/gateways.121/e22508/…
David Aldridge
Verwenden Sie Oracle RAC? Die Verwendung von CACHED am Ende der Anweisung kann die Leistung verbessern. Wenn Sie in kurzer Zeit viele Einfügungen vornehmen (und die Reihenfolge für Sie nicht wichtig ist), sollten Sie den Auslöser für gestaffelte Sequenzeinfügungen in Betracht ziehen, um zusätzliche Leistungsvorteile zu erzielen. Siehe: dba-oracle.com/t_rac_proper_sequence_usage.htm
Peeter Kokk

Antworten:

596

In Oracle gibt es keine Spalten "auto_increment" oder "identity" Oracle 11g gibt es . Sie können es jedoch einfach mit einer Sequenz und einem Trigger modellieren:

Tabellendefinition:

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;

Triggerdefinition:

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

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

AKTUALISIEREN:

IDENTITY Die Spalte ist jetzt in Oracle 12c verfügbar:

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

oder geben Sie Start- und Inkrementwerte an und verhindern Sie so das Einfügen in die Identitätsspalte ( GENERATED ALWAYS) (wiederum nur Oracle 12c +).

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

Alternativ kann in Oracle 12 auch eine Sequenz als Standardwert verwendet werden:

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
quelle
5
Ich bin ein n00b, können Sie mir bitte sagen , wo dept_seqkam!
J86
3
CREATE SEQUENCE dept_seq; erstellt dept_seq ... wie eine Tabelle ... aber in diesem Fall ist es nur eine Zahl, die Sie mit dept_seq.NEXTVAL erhöhen können ... siehe den Auslöser.
Benjamin Eckstein
Wie bereits erwähnt, schlägt der ursprüngliche Code fehl, wenn eine Zeile mit der angegebenen ID gefunden wird. Aber wie wäre es mit diesem Fall: Der Trigger würde die ID (automatisch) nur zuweisen, wenn in INSERT keine ID explizit angegeben wurde. Das würde scheitern, oder? Und wie geht das richtig?
FanaticD
10
Für Orakel-Neulinge wie mich bezieht sich der 'id'-Teil von' new.id 'auf die Spalte' id 'in der obigen Tabelle. 'neu' ist ein reserviertes Wort, das sich auf die neu erstellte Zeile bezieht
Hoppe
2
Sie müssen SELECT .. INTOden Trigger nicht verwenden, den Sie gerade ausführen können :new.id := dept_seq.NEXTVAL;.
MT0
90

SYS_GUIDGibt eine GUID zurück - eine global eindeutige ID. A SYS_GUIDist aRAW(16) . Es wird kein inkrementeller numerischer Wert generiert.

Wenn Sie eine inkrementelle Zifferntaste erstellen möchten, möchten Sie eine Sequenz erstellen.

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

Sie würden diese Sequenz dann entweder in Ihrer INSERTAnweisung verwenden

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

Oder Sie können einen Trigger definieren, der den Primärschlüsselwert mithilfe der Sequenz automatisch auffüllt

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;

Wenn Sie Oracle 11.1 oder höher verwenden, können Sie den Trigger etwas vereinfachen

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

Wenn Sie wirklich verwenden möchten SYS_GUID

CREATE TABLE table_name (
  primary_key_column raw(16) default sys_guid() primary key,
  <<other columns>>
)
Justin Cave
quelle
1
Was macht CACHE 100; in CREATE SEQUENCE name_of_sequence START WITH 1 INCREMENT BY 1 CACHE 100;das
Angelina
3
CACHE 100: Das Schlüsselwort ruft die nächsten 100 Zahlen in den Speicher ab. Normalerweise wird eine SEQUENCE bei jeder Änderung ihres Werts in der Datenbank gespeichert. Wenn Sie sie zwischenspeichern, wird sie nur gespeichert und abgerufen, wenn die zwischengespeicherten Dateien erschöpft sind. Verleiht Ihnen einen erheblichen Leistungsgewinn. Wenn die Datenbank jedoch ausfällt, verlieren Sie alle zwischengespeicherten Werte, die Sie nicht einmal verwendet haben.
Ramazan Polat
2
A SYS_GUID()ist ein RAW(16), nicht 32.
Turbanoff
2
@ Turbanoff - Guter Fang. Meine Antwort wurde aktualisiert. Die SYS_GUIDDokumentation erklärt eine, raw(32)die mich verwirrt hat.
Justin Cave
@ JustinCave Ich habe Ihren Ansatz bei der Erstellung der Sequenz und des Triggers verwendet. Was passiert, wenn ich eine Zeile programmgesteuert lösche (Java), wird auch die ID (Primärschlüssel) angepasst?
Satyadev
52

Ab Oracle 12c können Sie Folgendes tun:

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

Und 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;
/
Nisar
quelle
2
@ JonHeller Ich persönlich sage, dass das IDENTITYBeispiel in dieser Antwort viel klarer ist.
EpicPandaForce
5
Das WHEN (new.MAP_ID IS NULL)steht nicht in der akzeptierten Antwort. Upvoted.
dcsohl
1
@dcsohl, WHEN ( new.MAP_ID is null)ist in diesem Fall kein guter Code und wird bereits im Kommentarbereich von @ABCade unter akzeptierter Antwort erklärt .. habe gelesen;)
ajmalmhd04
Wenn ich dies von CREATE OR REPLACE TRIGGERbis aus END;starte, wird ein Fenster "Bindungen binden" angezeigt. Wenn ich auf "Übernehmen" klicke und in diesem Fenster nichts anderes mache und dann den ALTER TRIGGERBefehl ausführe, ist alles in Ordnung, aber ich wünschte, es gäbe eine Möglichkeit, dieses Popup programmgesteuert zu entfernen und alles zusammen auszuführen. Wenn Sie es insgesamt versuchen, erhalten Sie PLS-00103: Encountered the symbol 'ALTER'und es gefällt EXECUTE IMMEDIATEauch nicht (der gleiche Fehler, sagt es Encountered the symbol 'EXECUTE'stattdessen nur).
Vapcguy
Ich habe [42000][907] ORA-00907: missing right parenthesisbeim Ausführen der Version für Oracle 12c bekommen. Irgendeine Idee ?
Belgoros
32

Hier sind drei Geschmacksrichtungen:

  1. numerisch . Einfach ansteigender numerischer Wert, zB 1,2,3, ....
  2. GUID . global universelle Kennung alsRAW Datentyp.
  3. GUID (Zeichenfolge) . Wie oben, jedoch als Zeichenfolge, die in einigen Sprachen möglicherweise einfacher zu handhaben ist.

xist die Identitätsspalte. ErsatzFOO Sie in jedem der Beispiele Ihren Tabellennamen.

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

aktualisieren:

Oracle 12c führt diese beiden Varianten ein, die nicht von Triggern abhängen:

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

Der erste verwendet eine Sequenz auf traditionelle Weise; Der zweite verwaltet den Wert intern.

Mark Harrison
quelle
7

Angenommen, Sie meinen eine Spalte wie die SQL Server-Identitätsspalte?

In Oracle verwenden Sie eine SEQUENCE, um dieselbe Funktionalität zu erreichen. Ich werde sehen, ob ich einen guten Link finden und hier posten kann.

Update: Sieht so aus, als hätten Sie es selbst gefunden. Hier ist der Link trotzdem: http://www.techonthenet.com/oracle/sequences.php

Phil Sandler
quelle
7

In Oracle Database 12c wurde Identity eingeführt, eine automatisch inkrementelle (vom System generierte) Spalte. In den vorherigen Datenbankversionen (bis 11g) implementieren Sie normalerweise eine Identität, indem Sie eine Sequenz und einen Trigger erstellen. Ab 12c können Sie eine eigene Tabelle erstellen und die Spalte definieren, die als Identität generiert werden muss.

Der folgende Artikel erklärt die Verwendung:

Identitätsspalten - Ein neuer Eintrag in Oracle Database 12c

Corrado Piola
quelle
5
Während dieser Link die Frage beantworten kann, ist es besser, die wesentlichen Teile der Antwort hier aufzunehmen und den Link als Referenz bereitzustellen. Nur-Link-Antworten können ungültig werden, wenn sich die verknüpfte Seite ändert.
Brücke
5

Triggerund Sequencekann verwendet werden, wenn Sie eine Seriennummer wünschen, die jeder leicht lesen / merken / verstehen kann. Wenn Sie die ID-Spalte (wie emp_id) jedoch nicht auf diese Weise verwalten möchten und der Wert dieser Spalte nicht sehr hoch ist, können Sie sie SYS_GUID()bei der Tabellenerstellung verwenden, um eine solche automatische Inkrementierung zu erhalten.

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

Jetzt emp_idakzeptiert Ihre Spalte "global eindeutiger Bezeichnerwert". Sie können einen Wert in die Tabelle einfügen, indem Sie die Spalte emp_id wie folgt ignorieren.

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

Es wird also ein eindeutiger Wert in Ihre emp_idSpalte eingefügt.

124
quelle
Was passiert, wenn eine Zeile gelöscht wird? Werden die SYS_GUID()seine ID-Werte auch?
Satyadev
5

Ab Oracle 12c werden Identitätsspalten auf zwei Arten unterstützt:

  1. Sequenz + Tabelle - In dieser Lösung erstellen Sie immer noch eine Sequenz wie gewohnt und verwenden dann die folgende DDL:

    TABELLE ERSTELLEN MyTable (ID NUMBER DEFAULT MyTable_Seq.NEXTVAL , ...)

  2. Nur Tabelle - In dieser Lösung wird keine Sequenz explizit angegeben. Sie würden die folgende DDL verwenden:

    TABELLE ERSTELLEN MyTable (ID-NUMMER ALS IDENTITÄT ERZEUGT , ...)

Wenn Sie den ersten Weg verwenden, ist er abwärtskompatibel mit der vorhandenen Vorgehensweise. Die zweite ist etwas unkomplizierter und entspricht eher den übrigen RDMS-Systemen.

Nate Zaugg
quelle
5

es wird aufgerufen Identity Columnsund ist nur von Oracle Oracle 12c verfügbar

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

Beispiel für das Einfügen in Identity Columnswie unten

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

1 Zeile erstellt.

Sie können NICHT wie unten einfügen

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

FEHLER in Zeile 1: ORA-32795: Kann nicht in eine generierte Spalte mit immer Identität eingefügt werden

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

FEHLER in Zeile 1: ORA-32795: Kann nicht in eine generierte Spalte mit immer Identität eingefügt werden

nützlicher Link

Sam
quelle
1

Hier finden Sie eine vollständige Lösung für die Ausnahme- / Fehlerbehandlung für die automatische Inkrementierung. Diese Lösung ist abwärtskompatibel und funktioniert mit 11g und 12c, insbesondere wenn die Anwendung in Produktion ist.

Bitte ersetzen Sie 'TABLE_NAME' durch Ihren entsprechenden Tabellennamen

--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
quelle
0

So habe ich das für eine vorhandene Tabelle und Spalte (mit dem Namen id) gemacht:

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
quelle
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
quelle
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
quelle
-1

Orakel hat Sequenzen UND Identitätsspalten in 12c

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

Ich fand dies, bin mir aber nicht sicher, was rdb 7 ist http://www.oracle.com/technetwork/products/rdb/0307-identity-columns-128126.pdf

Kalpesh Soni
quelle
Hilfreiche Informationen und interessanter Leistungsvergleich. Die Verwendung von IDENTITY-Spalten ist besser als die Verwendung eines TRIGGER für eine SEQUENZ.
Ygoe
-1
  create trigger t1_trigger
  before insert on AUDITLOGS
  for each row
   begin
     select t1_seq.nextval into :new.id from dual;
   end;

Nur ich muss nur den Tabellennamen (AUDITLOGS) mit Ihrem Tabellennamen und new.id mit new.column_name ändern

Abhishek Ringsia
quelle
-2

Vielleicht versuchen Sie einfach dieses einfache Skript:

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

Ergebnis ist:

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
quelle
3
Wie unterscheidet sich das von Eugnios Antwort? Plus: Sie benötigen die selectin modernen Oracle-Versionen nicht. Sie können einfach:new.pk := TABLE_PK_SEQ.NEXTVAL
a_horse_with_no_name