¿Cómo crear una identificación con AUTO_INCREMENT en Oracle?

422

Parece que no hay un concepto de AUTO_INCREMENT en Oracle, hasta la versión 11g incluida.

¿Cómo puedo crear una columna que se comporte como incremento automático en Oracle 11g?

Sushan Ghimire
fuente
3
Puede crear un BEFORE INSERTactivador en la tabla y extraer valores de una secuencia para crear un incremento automático
Hunter McMillen, el
77
Las columnas de identidad ahora están disponibles en Oracle 12c docs.oracle.com/cd/E16655_01/gateways.121/e22508/…
David Aldridge
¿Está utilizando Oracle RAC? Usar CACHED al final de la declaración puede mejorar el rendimiento. Si está haciendo muchas inserciones en un período corto (y el orden no es importante para usted), considere la activación de inserción de secuencia escalonada para obtener beneficios de rendimiento adicionales. Ver: dba-oracle.com/t_rac_proper_sequence_usage.htm
Peeter Kokk

Respuestas:

596

A partir de Oracle 11g, no hay columnas de "aumento automático" o "identidad" en Oracle . Sin embargo, puede modelarlo fácilmente con una secuencia y un disparador:

Definición de tabla:

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;

Definición de disparador:

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

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

ACTUALIZAR:

IDENTITY La columna ahora está disponible en Oracle 12c:

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

o especifique valores iniciales y de incremento, evitando también cualquier inserción en la columna de identidad ( GENERATED ALWAYS) (nuevamente, solo Oracle 12c +)

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

Alternativamente, Oracle 12 también permite usar una secuencia como valor predeterminado:

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
fuente
55
Soy un n00b, ¿puedes decirme de dónde dept_seqvino?
J86
3
CREAR SECUENCIA dept_seq; crea dept_seq ... como una tabla ... pero en este caso es solo un número que puede aumentar con dept_seq.NEXTVAL ... vea el activador.
Benjamin Eckstein
Como se mencionó, el código original fallaría al encontrar una línea con ID especificada. Pero, ¿qué hay de este caso? El desencadenador asignaría la identificación (automáticamente) solo si no hubiera una identificación especificada explícitamente en INSERT. Esto fallaría, ¿verdad? ¿Y cuál es la forma correcta de hacer esto?
FanaticD
10
Para los novatos de Oracle como yo, la parte 'id' de 'new.id' se refiere a la columna 'id' en la tabla anterior. 'nuevo' es una palabra reservada que se refiere a la nueva fila creada
Hoppe
2
No es necesario usarlo SELECT .. INTOen el gatillo, solo puede hacerlo :new.id := dept_seq.NEXTVAL;.
MT0
90

SYS_GUIDdevuelve un GUID: una ID única global. A SYS_GUIDes a RAW(16). No genera un valor numérico incremental.

Si desea crear una clave numérica incremental, querrá crear una secuencia.

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

Entonces usaría esa secuencia en su INSERTdeclaración

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

O puede definir un activador que complete automáticamente el valor de la clave principal utilizando la secuencia

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 está utilizando Oracle 11.1 o posterior, puede simplificar un poco el disparador

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 realmente quieres usar SYS_GUID

CREATE TABLE table_name (
  primary_key_column raw(16) default sys_guid() primary key,
  <<other columns>>
)
Justin Cave
fuente
1
¿Qué CACHE 100; in CREATE SEQUENCE name_of_sequence START WITH 1 INCREMENT BY 1 CACHE 100;hacer?
Angelina
3
CACHE 100: la palabra clave recupera los siguientes 100 números en la memoria. Normalmente, una SECUENCIA se guarda en la base de datos cada vez que cambia su valor, si la almacena en caché, se guardará y recuperará solo si se agotaron las almacenadas en caché. Le brinda una ganancia de rendimiento significativa, pero si la base de datos falla, pierde todos los valores almacenados en caché que ni siquiera usó.
Ramazan Polat
2
A SYS_GUID()es un RAW(16), no 32.
turbante el
2
@turbanoff - Buena captura. Actualicé mi respuesta. La SYS_GUIDdocumentación declara raw(32)que me confundió.
Justin Cave
@JustinCave He usado su enfoque para crear la secuencia y el disparador. ¿Qué sucede si elimino una fila mediante programación (Java), ¿también se ajustará la ID (clave principal)?
kittu
52

En Oracle 12c en adelante, podría hacer 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)
);

Y en 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
fuente
2
@ JonHeller Yo personalmente digo que el IDENTITYejemplo es mucho más claro en esta respuesta.
EpicPandaForce
55
El WHEN (new.MAP_ID IS NULL)no está en la respuesta aceptada. Votado
dcsohl
1
@dcsohl, WHEN ( new.MAP_ID is null)no es un buen código en este caso y ya está explicado en la sección de comentarios por @ABCade debajo de la respuesta aceptada ... leer;)
ajmalmhd04
Cuando ejecuto esto de CREATE OR REPLACE TRIGGERa END;, aparece la ventana "Enter Binds". Si hago clic en "Aplicar" y no hago nada más en esa ventana, y luego ejecuto el ALTER TRIGGERcomando, todo está bien, pero desearía que hubiera una forma de deshacerse programáticamente de esa ventana emergente y ejecutar todo junto. Si lo intentas por completo, obtienes PLS-00103: Encountered the symbol 'ALTER'y tampoco le gusta EXECUTE IMMEDIATE(el mismo error, solo lo dice Encountered the symbol 'EXECUTE').
vapcguy
Obtuve [42000][907] ORA-00907: missing right parenthesisal ejecutar la versión para Oracle 12c en adelante. Alguna idea ?
belgoros
32

Aquí hay tres sabores:

  1. numérica . Valor numérico creciente simple, p. Ej. 1,2,3, ....
  2. GUID . Identificador universal universal, como un RAWtipo de datos.
  3. GUID (cadena) . Igual que el anterior, pero como una cadena que podría ser más fácil de manejar en algunos idiomas.

xes la columna de identidad Sustituya FOOcon el nombre de su tabla en cada uno de los ejemplos.

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

actualizar:

Oracle 12c presenta estas dos variantes que no dependen de los desencadenantes:

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

El primero usa una secuencia de la manera tradicional; el segundo gestiona el valor internamente.

Mark Harrison
fuente
7

¿Asumiendo que se refiere a una columna como la columna de identidad de SQL Server?

En Oracle, usa una SECUENCIA para lograr la misma funcionalidad. Veré si puedo encontrar un buen enlace y publicarlo aquí.

Actualización: parece que lo encontraste tú mismo. Aquí está el enlace de todos modos: http://www.techonthenet.com/oracle/sequences.php

Phil Sandler
fuente
7

Oracle Database 12c introdujo Identity, una columna auto-incremental (generada por el sistema). En las versiones anteriores de la base de datos (hasta 11g), generalmente implementa una identidad creando una secuencia y un activador. A partir de 12c, puede crear su propia tabla y definir la columna que debe generarse como identidad.

El siguiente artículo explica cómo usarlo:

Columnas de identidad: una nueva entrada en Oracle Database 12c

Corrado Piola
fuente
55
Si bien este enlace puede responder la pregunta, es mejor incluir aquí las partes esenciales de la respuesta y proporcionar el enlace como referencia. Las respuestas de solo enlace pueden volverse inválidas si la página vinculada cambia.
Puente
5

Triggery Sequencepuede usarse cuando desea un número de serie que cualquiera pueda leer / recordar / comprender fácilmente. Pero si no desea administrar la columna de ID (como emp_id) de esta manera, y el valor de esta columna no es muy considerable, puede usarlo SYS_GUID()en Creación de tabla para obtener el Incremento automático de esta manera.

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

Ahora su emp_idcolumna aceptará "valor de identificador único global". puede insertar valor en la tabla ignorando la columna emp_id de esta manera.

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

Por lo tanto, insertará un valor único en su emp_idcolumna.

124
fuente
¿Qué sucede cuando se elimina una fila? ¿Los SYS_GUID()valores de su id también?
kittu
5

A partir de Oracle 12c, hay soporte para las columnas de identidad en una de dos formas:

  1. Secuencia + Tabla : en esta solución aún crea una secuencia como lo haría normalmente, luego usa el siguiente DDL:

    CREAR TABLA MyTable (NÚMERO DE ID POR DEFECTO MyTable_Seq.NEXTVAL , ...)

  2. Solo tabla : en esta solución no se especifica explícitamente ninguna secuencia. Usaría el siguiente DDL:

    CREATE TABLE MyTable (NÚMERO DE IDENTIFICACIÓN GENERADO COMO IDENTIDAD , ...)

Si usa la primera forma, es compatible con la forma existente de hacer las cosas. El segundo es un poco más directo y está más en línea con el resto de los sistemas RDMS.

Nate Zaugg
fuente
5

se llama Identity Columnsy solo está disponible en Oracle Oracle 12c

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

ejemplo de inserción en Identity Columnscomo a continuación

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

1 fila creada.

NO puedes insertar como a continuación

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

ERROR en la línea 1: ORA-32795: no se puede insertar en una columna de identidad generada siempre

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

ERROR en la línea 1: ORA-32795: no se puede insertar en una columna de identidad generada siempre

enlace útil

sam
fuente
1

Aquí hay una solución completa para el manejo de excepciones / errores para el incremento automático, esta solución es compatible con versiones anteriores y funcionará en 11g y 12c, específicamente si la aplicación está en producción.

Reemplace 'TABLE_NAME' con su nombre de tabla apropiado

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

Así es como hice esto en una tabla y columna existentes (nombre de identificación):

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
fuente
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
fuente
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
fuente
-1
  create trigger t1_trigger
  before insert on AUDITLOGS
  for each row
   begin
     select t1_seq.nextval into :new.id from dual;
   end;

solo tengo que cambiar el nombre de la tabla (AUDITLOGS) con el nombre de su tabla y new.id con new.column_name

abhishek ringsia
fuente
-2

Quizás solo intente este simple script:

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

El resultado es:

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
fuente
3
¿Cómo es esto diferente a la respuesta de eugnio? Además: no necesita las selectversiones modernas de Oracle. Simplemente puede usar:new.pk := TABLE_PK_SEQ.NEXTVAL
a_horse_with_no_name