La meilleure façon de faire une insertion multi-lignes dans Oracle?

262

Je recherche un bon moyen d'effectuer des insertions sur plusieurs lignes dans une base de données Oracle 9. Les éléments suivants fonctionnent dans MySQL mais ne semblent pas être pris en charge dans 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
la source

Réponses:

165

Cela fonctionne dans 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

La chose à retenir ici est d'utiliser la from dualdéclaration.

( source )

Espo
la source
6
Il y a aussi quelque chose appelé "Insérer tout" à partir du 9i (?)
mlathe
4
Être pointilleux, mais le formatage a plus de sens si vous mettez "union all" à la fin de chaque ligne de sélection (sauf pour la dernière).
Jamie
Un inconvénient à cela est que nous ne pouvons pas utiliser un sequnce.nextvalcar il est interdit dans unionde select. Au lieu de cela, nous pouvons aller avec INSERT ALL.
sql_dummy
5
@Jamie: le formatage d'Espo est légèrement plus intelligent dans le sens où vous n'avez pas à vous soucier de savoir si vous êtes sur la dernière ligne ou non, lorsque vous ajoutez de nouvelles lignes. Par conséquent, une fois que vous avez vos 2 premières sélections, vous pouvez facilement copier / coller la dernière ligne (ou une ligne du milieu), en se concentrant uniquement sur les valeurs que vous devez modifier. C'est une astuce courante pour de nombreux autres cas dans toutes les langues (virgule, opérateurs logiques, plus ...). C'est juste une question d'habitude, beaucoup d'anciennes pratiques ont été révisées pour se concentrer sur la responsabilité du code plus que sur l'intuitivité.
Laurent.B
quel est le maximum pour 12c?
Boîte à outils
363

Dans Oracle, pour insérer plusieurs lignes dans le tableau t avec les colonnes col1, col2 et col3, vous pouvez utiliser la syntaxe suivante:

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
la source
54
Je ne comprends pas ce que SELECT 1 FROM DUALça fait.
jameshfisher
55
INSERT ALLnécessite une SELECTsous - requête. Pour contourner cela, SELECT 1 FROM DUALest utilisé pour donner une seule ligne de données fictives.
Markus Jarderot
40
En quoi cela diffère-t-il de plusieurs instructions d'insertion? Vous avez toujours la répétition sur les noms de colonne, donc ne semblez pas gagner beaucoup.
Burhan Ali
28
Environ 10 à 12 instructions INSERT multiples sont terminées en 2 secondes sur mon PC, tandis que la syntaxe ci-dessus peut INSÉRER 1000 enregistrements par seconde! Impressionné! Notez que je ne m'engage qu'à la fin.
Kent Pawar du
13
Cela fonctionne très bien, cependant si vous insérez en utilisant une séquence, par exemple user.NEXTVAL, il retournera la même valeur pour chaque insert. Vous pouvez l'incrémenter manuellement dans l'insert tout, puis mettre à jour la séquence en dehors de l'insert.
user1412523
33

Utilisez SQL * Loader. Cela prend un peu de mise en place, mais si ce n'est pas un coup, ça vaut le coup.

Créer une table

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

Créer un CSV

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

Créer un fichier de contrôle du chargeur

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% 

Exécuter la commande 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

Confirmer l'insertion

SQL> select * from ldr_test;

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

SQL>

SQL * Loader a beaucoup d'options et peut prendre à peu près n'importe quel fichier texte en entrée. Vous pouvez même incorporer les données de votre fichier de contrôle si vous le souhaitez.

Voici une page avec plus de détails -> SQL * Loader

Matthew Watson
la source
Cela devrait être la meilleure réponse à
mon humble avis
La colonne ID de ma table est générée automatiquement. Puis-je simplement ignorer le champ ID dans le fichier de contrôle du chargeur?
Thom DeCarlo
@Thom, utilisez le sequence.nextval par exemple fruit_id "fruit_seq.nextval"dans la définition de colonne
roblogic
50 millions d'enregistrements en quelques minutes. Way to go
Toolkit
20

Chaque fois que je dois le faire, je crée un simple bloc PL / SQL avec une procédure locale comme celle-ci:

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

la source
12

Si vous avez déjà les valeurs que vous souhaitez insérer dans une autre table, vous pouvez insérer à partir d'une instruction select.

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

Sinon, vous pouvez répertorier un tas d'instructions d'insertion sur une seule ligne et soumettre plusieurs requêtes en bloc pour gagner du temps pour quelque chose qui fonctionne à la fois dans Oracle et MySQL.

La solution de @Espo est également une bonne solution qui fonctionnera à la fois dans Oracle et MySQL si vos données ne sont pas déjà dans une table.

Ryan Ahearn
la source
4

vous pouvez insérer en utilisant une boucle si vous souhaitez insérer des valeurs aléatoires.

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

Voici un guide étape par étape très utile pour insérer plusieurs lignes dans Oracle:

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

La dernière étape:

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

Dans mon cas, j'ai pu utiliser une simple instruction d'insertion pour insérer en vrac de nombreuses lignes dans TABLE_A en utilisant une seule colonne de TABLE_B et en obtenant les autres données ailleurs (séquence et une valeur codée en dur):

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

Résultat:

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

etc

java-addict301
la source