¿La mejor manera de insertar múltiples filas en Oracle?

262

Estoy buscando una buena manera de realizar inserciones de varias filas en una base de datos Oracle 9. Lo siguiente funciona en MySQL pero no parece ser compatible con Oracle.

INSERT INTO TMP_DIM_EXCH_RT 
(EXCH_WH_KEY, 
 EXCH_NAT_KEY, 
 EXCH_DATE, EXCH_RATE, 
 FROM_CURCY_CD, 
 TO_CURCY_CD, 
 EXCH_EFF_DATE, 
 EXCH_EFF_END_DATE, 
 EXCH_LAST_UPDATED_DATE) 
VALUES
    (1, 1, '28-AUG-2008', 109.49, 'USD', 'JPY', '28-AUG-2008', '28-AUG-2008', '28-AUG-2008'),
    (2, 1, '28-AUG-2008', .54, 'USD', 'GBP', '28-AUG-2008', '28-AUG-2008', '28-AUG-2008'),
    (3, 1, '28-AUG-2008', 1.05, 'USD', 'CAD', '28-AUG-2008', '28-AUG-2008', '28-AUG-2008'),
    (4, 1, '28-AUG-2008', .68, 'USD', 'EUR', '28-AUG-2008', '28-AUG-2008', '28-AUG-2008'),
    (5, 1, '28-AUG-2008', 1.16, 'USD', 'AUD', '28-AUG-2008', '28-AUG-2008', '28-AUG-2008'),
    (6, 1, '28-AUG-2008', 7.81, 'USD', 'HKD', '28-AUG-2008', '28-AUG-2008', '28-AUG-2008');
Jamey
fuente

Respuestas:

165

Esto funciona en Oracle:

insert into pager (PAG_ID,PAG_PARENT,PAG_NAME,PAG_ACTIVE)
          select 8000,0,'Multi 8000',1 from dual
union all select 8001,0,'Multi 8001',1 from dual

Lo que hay que recordar aquí es usar la from dualdeclaración.

( fuente )

Espo
fuente
66
También hay algo llamado "Insertar todo" a partir del 9i (?)
mlathe
44
Ser exigente, pero el formato tiene más sentido si coloca "union all" al final de cada línea de selección (excepto la última).
Jamie
Una desventaja de esto es que puedo usar una sequnce.nextval, ya que está prohibido en unionde select. En cambio podemos ir con INSERT ALL.
sql_dummy
55
@Jamie: el formato de Espo es un poco más inteligente en el sentido de que no tiene que preocuparse de si está en la última línea o no, al agregar nuevas líneas. Por lo tanto, una vez que tenga sus 2 primeras selecciones, puede copiar / pegar fácilmente la última línea (o una en el medio), centrándose solo en los valores que tiene que cambiar. Es un truco común para muchos otros casos en cualquier idioma (coma, operadores lógicos, más ...). Es solo una cuestión de costumbre, se han revisado muchas prácticas anteriores para centrarse en la responsabilidad del código más que en la intuición.
Laurent.B
¿Cuál es el máximo para 12c?
Toolkit
363

En Oracle, para insertar varias filas en la tabla t con las columnas col1, col2 y col3, puede usar la siguiente sintaxis:

INSERT ALL
   INTO t (col1, col2, col3) VALUES ('val1_1', 'val1_2', 'val1_3')
   INTO t (col1, col2, col3) VALUES ('val2_1', 'val2_2', 'val2_3')
   INTO t (col1, col2, col3) VALUES ('val3_1', 'val3_2', 'val3_3')
   .
   .
   .
SELECT 1 FROM DUAL;
Myto
fuente
54
No entiendo lo que SELECT 1 FROM DUALhace.
jameshfisher
55
INSERT ALLrequiere una SELECTsubconsulta. Para evitar eso, SELECT 1 FROM DUALse usa para dar una sola fila de datos ficticios.
Markus Jarderot
40
¿Cómo difiere esto de las declaraciones de inserción múltiple? Todavía tiene la repetición en los nombres de columna, por lo que no parece ganar mucho.
Burhan Ali
28
¡Alrededor de 10-12 múltiples declaraciones INSERT se completan en 2 segundos en mi PC, mientras que la sintaxis anterior puede INSERTAR 1000 registros por segundo! ¡Impresionado! Tenga en cuenta que me comprometo solo al final.
Kent Pawar
13
Esto funciona bien, sin embargo, si está insertando usando una secuencia, diga user.NEXTVAL devolverá el mismo valor para cada inserción. Puede incrementarlo manualmente en el inserto todo, luego actualizar la secuencia fuera del inserto.
user1412523
33

Use SQL * Loader. Se necesita un poco de configuración, pero si esto no es único, vale la pena.

Crear mesa

SQL> create table ldr_test (id number(10) primary key, description varchar2(20));
Table created.
SQL>

Crear CSV

oracle-2% cat ldr_test.csv
1,Apple
2,Orange
3,Pear
oracle-2% 

Crear archivo de control del cargador

oracle-2% cat ldr_test.ctl 
load data

 infile 'ldr_test.csv'
 into table ldr_test
 fields terminated by "," optionally enclosed by '"'              
 ( id, description )

oracle-2% 

Ejecute el comando SQL * Loader

oracle-2% sqlldr <username> control=ldr_test.ctl
Password:

SQL*Loader: Release 9.2.0.5.0 - Production on Wed Sep 3 12:26:46 2008

Copyright (c) 1982, 2002, Oracle Corporation.  All rights reserved.

Commit point reached - logical record count 3

Confirmar inserción

SQL> select * from ldr_test;

        ID DESCRIPTION
---------- --------------------
         1 Apple
         2 Orange
         3 Pear

SQL>

SQL * Loader tiene muchas opciones y puede tomar casi cualquier archivo de texto como entrada. Incluso puede incluir los datos en su archivo de control si lo desea.

Aquí hay una página con más detalles -> SQL * Loader

Matthew Watson
fuente
Esta debería ser la respuesta principal en mi humilde opinión, cualquier otra cosa (para tareas a gran escala) está pidiendo problemas
roblogic
La columna de ID en mi tabla se autogenera. ¿Puedo simplemente omitir el campo ID en el archivo de control del cargador?
Thom DeCarlo
@Thom, use la secuencia.nextval, por ejemplo, fruit_id "fruit_seq.nextval"en la definición de columna
roblogic
50 millones de registros en pocos minutos. Way to go
Toolkit
20

Siempre que necesito hacer esto, construyo un bloque PL / SQL simple con un procedimiento local como este:

declare
   procedure ins
   is
      (p_exch_wh_key INTEGER, 
       p_exch_nat_key INTEGER, 
       p_exch_date DATE, exch_rate NUMBER, 
       p_from_curcy_cd VARCHAR2, 
       p_to_curcy_cd VARCHAR2, 
       p_exch_eff_date DATE, 
       p_exch_eff_end_date DATE, 
       p_exch_last_updated_date DATE);
   begin
      insert into tmp_dim_exch_rt 
      (exch_wh_key, 
       exch_nat_key, 
       exch_date, exch_rate, 
       from_curcy_cd, 
       to_curcy_cd, 
       exch_eff_date, 
       exch_eff_end_date, 
       exch_last_updated_date) 
      values
      (p_exch_wh_key, 
       p_exch_nat_key, 
       p_exch_date, exch_rate, 
       p_from_curcy_cd, 
       p_to_curcy_cd, 
       p_exch_eff_date, 
       p_exch_eff_end_date, 
       p_exch_last_updated_date);
   end;
begin
   ins (1, 1, '28-AUG-2008', 109.49, 'USD', 'JPY', '28-AUG-2008', '28-AUG-2008', '28-AUG-2008'),
   ins (2, 1, '28-AUG-2008', .54, 'USD', 'GBP', '28-AUG-2008', '28-AUG-2008', '28-AUG-2008'),
   ins (3, 1, '28-AUG-2008', 1.05, 'USD', 'CAD', '28-AUG-2008', '28-AUG-2008', '28-AUG-2008'),
   ins (4, 1, '28-AUG-2008', .68, 'USD', 'EUR', '28-AUG-2008', '28-AUG-2008', '28-AUG-2008'),
   ins (5, 1, '28-AUG-2008', 1.16, 'USD', 'AUD', '28-AUG-2008', '28-AUG-2008', '28-AUG-2008'),
   ins (6, 1, '28-AUG-2008', 7.81, 'USD', 'HKD', '28-AUG-2008', '28-AUG-2008', '28-AUG-2008');
end;
/

fuente
12

Si ya tiene los valores que desea insertar en otra tabla, puede Insertar desde una instrucción select.

INSERT INTO a_table (column_a, column_b) SELECT column_a, column_b FROM b_table;

De lo contrario, puede enumerar un montón de declaraciones de inserción de una sola fila y enviar varias consultas en masa para ahorrar tiempo para algo que funcione tanto en Oracle como en MySQL.

La solución de @Espo también es buena y funcionará tanto en Oracle como en MySQL si sus datos aún no están en una tabla.

Ryan Ahearn
fuente
4

puede insertar usando el bucle si desea insertar algunos valores aleatorios.

BEGIN 
    FOR x IN 1 .. 1000 LOOP
         INSERT INTO MULTI_INSERT_DEMO (ID, NAME)
         SELECT x, 'anyName' FROM dual;
    END LOOP;
END;
Girdhar Singh Rathore
fuente
0

Aquí hay una guía paso a paso muy útil para insertar múltiples filas en Oracle:

https://livesql.oracle.com/apex/livesql/file/content_BM1LJQ87M5CNIOKPOWPV6ZGR3.html

El último paso:

INSERT ALL
/* Everyone is a person, so insert all rows into people */
WHEN 1=1 THEN
INTO people (person_id, given_name, family_name, title)
VALUES (id, given_name, family_name, title)
/* Only people with an admission date are patients */
WHEN admission_date IS NOT NULL THEN
INTO patients (patient_id, last_admission_date)
VALUES (id, admission_date)
/* Only people with a hired date are staff */
WHEN hired_date IS NOT NULL THEN
INTO staff (staff_id, hired_date)
VALUES (id, hired_date)
  WITH names AS (
    SELECT 4 id, 'Ruth' given_name, 'Fox' family_name, 'Mrs' title,
           NULL hired_date, DATE'2009-12-31' admission_date
    FROM   dual UNION ALL
    SELECT 5 id, 'Isabelle' given_name, 'Squirrel' family_name, 'Miss' title ,
           NULL hired_date, DATE'2014-01-01' admission_date
    FROM   dual UNION ALL
    SELECT 6 id, 'Justin' given_name, 'Frog' family_name, 'Master' title,
           NULL hired_date, DATE'2015-04-22' admission_date
    FROM   dual UNION ALL
    SELECT 7 id, 'Lisa' given_name, 'Owl' family_name, 'Dr' title,
           DATE'2015-01-01' hired_date, NULL admission_date
    FROM   dual
  )
  SELECT * FROM names
akasha
fuente
0

En mi caso, pude usar una simple instrucción de inserción para insertar en masa muchas filas en TABLE_A usando solo una columna de TABLE_B y obtener los otros datos en otra parte (secuencia y un valor codificado):

INSERT INTO table_a (
    id,
    column_a,
    column_b
)
    SELECT
        table_a_seq.NEXTVAL,
        b.name,
        123
    FROM
        table_b b;

Resultado:

ID: NAME: CODE:
1, JOHN, 123
2, SAM, 123
3, JESS, 123

etc.

java-addict301
fuente