MERGE y ORA-30926

30 Octubre 2007

El error ORA-30926 es una extraño error que puede aparecer en sentencias ‘MERGE’ en las cuales la condición de ‘join’ puede dar lugar a inconsistencias. El problema es, como siempre, que el mensaje explicativo del error no clarifica demasiado las causas que lo motivan.

“ORA-30926: unable to get a stable set of rows in the source tables.
Cause: A stable set of rows could not be got because of large dml activity or a non-deterministic where clause.
Action: Remove any non-deterministic where clauses and reissue the dml.”

Veamos un ejemplo:

carlos@db01.xxxxxx> select * from prueba01;

      ID_N C_TXT
---------- -------------------------
         1 UNO

carlos@db01.xxxxxx> select * from prueba02;

      ID_N C_TXT
---------- -------------------------
         2 TWO
         1 RAS
         1 ONE

carlos@db01.xxxxxx> merge into prueba01 a
  2  using prueba02 b
  3  on (a.id_n = b.id_n)
  4  when matched then update set a.c_txt = b.c_txt;
using prueba02 b
      *
ERROR en línea 2:
ORA-30926: no se ha podido obtener un juego de filas estable en las tablas de origen

La verdadera causa del error no está en el “large dml activity“, sino en el “non-deterministic where clause“: como ocurría aquí, dos filas de la tabla PRUEBA02 actualizarían una misma fila de la tabla PRUEBA01.

Pero, como también vimos aquí, hay una pequeña trampa para soslayar el error:

carlos@db01.xxxxxx> UPDATE /*+ BYPASS_UJVC */ ( SELECT a.ID_N a_ID_N,
  2                      a.C_TXT a_C_TXT,
  3                      b.ID_N b_ID_N,
  4                      b.C_TXT b_C_TXT
  5                 FROM PRUEBA01 a,
  6                      PRUEBA02 b
  7                WHERE a.ID_N = b.ID_N)
  8          SET a_C_TXT = b_C_TXT;

2 filas actualizadas.

carlos@db01.xxxxxx> select * from prueba02;

      ID_N C_TXT
---------- -------------------------
         2 TWO
         1 RAS
         1 ONE

carlos@db01.xxxxxx> select * from prueba01;

      ID_N C_TXT
---------- -------------------------
         1 ONE

La pregunta es: ¿funcionará el ‘hint’ tramposo con el MERGE?

La respuesta es NO:

carlos@db01.xxxxxx> merge /*+ BYPASS_UJVC */ into prueba01 a
  2  using prueba02 b
  3     on (a.id_n = b.id_n)
  4     when matched then update set a.c_txt = b.c_txt;
using prueba02 b
      *
ERROR en línea 2:
ORA-30926: no se ha podido obtener un juego de filas estable en las tablas de origen

Finalmente, para los hombres -y mujeres- de poca fe: sin el HINT tramposo, el UPDATE tampoco funciona:

carlos@db01.xxxxxx> UPDATE  ( SELECT a.ID_N a_ID_N,
  2                      a.C_TXT a_C_TXT,
  3                      b.ID_N b_ID_N,
  4                      b.C_TXT b_C_TXT
  5                 FROM PRUEBA01 a,
  6                      PRUEBA02 b
  7                WHERE a.ID_N = b.ID_N)
  8          SET a_C_TXT = b_C_TXT;
        SET a_C_TXT = b_C_TXT
            *
ERROR en línea 8:
ORA-01779: no se puede modificar una columna que se corresponde con una tabla no reservada por clave

Saludos.

Carlos.


ORA-1555 y UNDO_RETENTION.

29 Octubre 2007

Llego a la oficina y lo primero que hago es ver el ‘log’ de un proceso que dejé corriendo el viernes en un sevidor de TEST. El proceso en cuestión es un conjunto de pesadísimos ‘MERGES’, ‘UPDATES’ e ‘INSERTS’ (una especie de proceso ETL). Descubro para mi desgracia que el viernes hubo un ORA-1555 a las 23:34:

Inicio Xxxxx x Xxxxx XXXX + Xxxxxxx: MERGE 1. 26/10/2007 13:44:11
Error en STAGE 3: ORA-12801: error signaled in parallel query server P006
ORA-01555: snapshot too old: rollback segment number 9 with name "_SYSSMU9$" too small 27/10/2007 23:34:16
Fin del proceso. 27/10/2007 23:34:16

Lo raro es que, según el ‘log’, el proceso llevaba corriendo 34 horas(!). Anteriormente, este mismo proceso se había completado en unas 17 horas (?) (Nótese que se ejecuta PARALLEL).

El caso es que viendo UNDO_RETENTION tenemos:

carlos@db01.xxxxxx> show parameter undo

NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
undo_management                      string      AUTO
undo_retention                       integer     900
undo_tablespace                      string      UNDOTBS1

lo que de hecho es claramente insuficiente. Los segmentos de ‘rollback’ se están reescribiendo demasiado pronto, y cuando Oracle ha querido hacer una vista ‘consistente’ de algun dato en el macro-proceso, se ha encontrado con que no podía, porque el segmento de ‘rollback’ (se llama “_SYSSMU9$”) había sido reutilizado (sobreescrito).

Miramos V$UNDOSTAT:

carlos@db01.xxxxxx> select BEGIN_TIME, END_TIME, MAXQUERYLEN, TUNED_UNDORETENTION
 2 from v$undostat order by maxquerylen;

BEGIN_TIME          END_TIME            MAXQUERYLEN TUNED_UNDORETENTION
------------------- ------------------- ----------- -------------------
...
26/10/2007 18:36:46 26/10/2007 18:46:46        9289               10188

Así que hay que poner un poco más de mimo en el UNDO. El tablespace ocupa 6 Gb. Teniendo en cuenta que la ocupación del ‘filesystem’ es del 56%:

[oracle@orael ~]$ df -h /bla/bla/bla/bla/undotbs01.dbf
Filesystem            Size  Used Avail Use% Mounted on
/dev/mapper/VolGroup01-LogVol02
                      289G  153G  122G  56% /bla

Nos podemos permitir:

carlos@db01.xxxxxx> alter system set undo_retention=10800 scope=both;

Sistema modificado.

… y volver a correr el monstruo de proceso ese.

Saludos.

Carlos.


Vistas actualizables, ‘key-preserved tables’ y ORA-01779

25 Octubre 2007

El concepto de vistas actualizables se apoya el el concepto de tablas reservadas (protegidas) por clave (‘key-preserved tables’). Este concepto de Vistas actualizables es uno de los primeros con los que se topan los que vienen de entornos SQLServer/Sybase, donde se permite la construcción UPDATE … FROM TABLA1, TABLA2…

Así es: en SQLServer/Sybase se pueden incorporar ‘joins’ de diferentes tablas a la hora de efectuar un UPDATE. En Oracle, en cambio, eso se hace mediante las mencionadas ‘vistas actualizables’.

Lo que en SQLServer/Sybase se escribe como

UPDATE
  FROM PRUEBA01 a,
       PRUEBA02 b
   SET b.C_TXT = a.C_TXT
 WHERE a.ID_N = b.ID_N
go

en Oracle se escribe (entre otras formas) como:

UPDATE ( SELECT a.ID_N a_ID_N,
                a.C_TXT a_C_TXT,
                b.ID_N b_ID_N,
                b.C_TXT b_C_TXT
           FROM PRUEBA01 a,
                PRUEBA02 b
          WHERE a.ID_N = b.ID_N)
    SET b_C_TXT = a_C_TXT;

¿Mejor? ¿Peor? Como siempre: simplemente distinto. El problema aparece cuando estas sentencias empiezan a arrojar los ‘ORA-01779: no se puede modificar una columna que se corresponde con una tabla no reservada por clave’ (‘ORA-01779: cannot modify a column which maps to a non key-preserved table’). Es aquí donde la mayoría de la gente se comienza a liar con esto de las ‘key-preserved tables’.

Lo primero que hay que decir es que el concepto de ‘key-preserved tables’ no tiene que ver con los datos, sino con la estructura de los objetos. No importa que la relación entre las filas de dos tablas sea ‘de facto’ 1:1, si el optimizador (que es quien decide) interpreta que las tablas no son ‘key-preserved’ porque su estructura no lo indica, saltará el error.

Veámoslo con un ejemplo:

SQL*Plus: Release 10.2.0.1.0 - Production on Jue Oct 25 14:31:19 2007

Copyright (c) 1982, 2005, Oracle.  All rights reserved.

Introduzca el nombre de usuario: carlos@db01.xxxxxx
Introduzca la contraseña:

Conectado a:
Oracle Database 10g Enterprise Edition Release 10.2.0.3.0 - Production
With the Partitioning, OLAP and Data Mining options

carlos@db01.xxxxxx> CREATE TABLE PRUEBA01(ID_N NUMBER NOT NULL,
 2  C_TXT VARCHAR2(25));

Tabla creada.

carlos@db01.xxxxxx> CREATE TABLE PRUEBA02(ID_N NUMBER NOT NULL,
 2  C_TXT VARCHAR2(25));

Tabla creada.

carlos@db01.xxxxxx> INSERT INTO PRUEBA01 VALUES (1,'UNO');

1 fila creada.

carlos@db01.xxxxxx> INSERT INTO PRUEBA02 VALUES (1,NULL);

1 fila creada.

carlos@db01.xxxxxx> COMMIT;

Confirmación terminada.

carlos@db01.xxxxxx> SELECT * FROM PRUEBA01;

      ID_N C_TXT
---------- -------------------------
         1 UNO

carlos@db01.xxxxxx> SELECT * FROM PRUEBA02;

      ID_N C_TXT
---------- -------------------------
         1

carlos@db01.xxxxxx> UPDATE ( SELECT a.ID_N a_ID_N,
  2                  a.C_TXT a_C_TXT,
  3                  b.ID_N b_ID_N,
  4                  b.C_TXT b_C_TXT
  5             FROM PRUEBA01 a,
  6                  PRUEBA02 b
  7            WHERE a.ID_N = b.ID_N)
  8      SET b_C_TXT = a_C_TXT;
    SET b_C_TXT = a_C_TXT
        *
ERROR en línea 8:
ORA-01779: no se puede modificar una columna que se corresponde con una tabla no reservada por clave

Lo segundo que hay que entender es que una tabla es ‘key-preserved’ si todas las claves de dicha tabla podrían ser claves del resultado del ‘join’. Esto, dicho así, puede no parecer tan fácil de entender, pero la traducción a ‘román paladino’ sería que una fila de una tabla no puede ser actualizada más de una vez como resultado de la condición del ‘join’ (esto es: que coincida con más de una fila de la segunda tabla).

¿Y dónde mira el optimizador? A la estructura de la tabla (las PRIMARY KEYS):

carlos@db01.xxxxxx> ALTER TABLE PRUEBA01
 2  ADD CONSTRAINT PRUEBA01_PK PRIMARY KEY(ID_N);

Tabla modificada.

carlos@db01.xxxxxx> UPDATE ( SELECT a.ID_N a_ID_N,
  2                  a.C_TXT a_C_TXT,
  3                  b.ID_N b_ID_N,
  4                  b.C_TXT b_C_TXT
  5             FROM PRUEBA01 a,
  6                  PRUEBA02 b
  7            WHERE a.ID_N = b.ID_N)
  8      SET b_C_TXT = a_C_TXT;

1 fila actualizada.

Y, por último: el mensaje de error es un poco confuso ya que:

carlos@db01.xxxxxx> ALTER TABLE PRUEBA01 DROP CONSTRAINT PRUEBA01_PK;

Tabla modificada.

carlos@db01.xxxxxx> ALTER TABLE PRUEBA02
 2  ADD CONSTRAINT PRUEBA02_PK PRIMARY KEY(ID_N);

Tabla modificada.

carlos@db01.xxxxxx> UPDATE ( SELECT a.ID_N a_ID_N,
  2                  a.C_TXT a_C_TXT,
  3                  b.ID_N b_ID_N,
  4                  b.C_TXT b_C_TXT
  5             FROM PRUEBA01 a,
  6                  PRUEBA02 b
  7            WHERE a.ID_N = b.ID_N)
  8      SET b_C_TXT = a_C_TXT;
    SET b_C_TXT = a_C_TXT
        *
ERROR en línea 8:
ORA-01779: no se puede modificar una columna que se corresponde con una tabla no reservada por clave

La columna que estamos modificando pertenece a una tabla que SÍ tiene PK, pero NO ES ‘key-preserved’ (ya que si hubiese dos filas en PRUEBA01 con ID_N=1 provocaría que la misma fila en PRUEBA02 fuese actualizada con valores de más de una fila de PRUEBA01).

Pero hay que tener en cuenta que una fila de una ‘key-preserved table’ SÍ PUEDE actualizar más de una fila de la otra tabla:

carlos@db01.xxxxxx> ALTER TABLE PRUEBA02 DROP CONSTRAINT PRUEBA02_PK;

Tabla modificada.

carlos@db01.xxxxxx> ALTER TABLE PRUEBA01
 2  ADD CONSTRAINT PRUEBA01_PK PRIMARY KEY(ID_N);

Tabla modificada.

carlos@db01.xxxxxx> INSERT INTO PRUEBA02 VALUES (1,'ONE');

1 fila creada.

carlos@db01.xxxxxx> SELECT * FROM PRUEBA02;

      ID_N C_TXT
---------- -------------------------
         1 UNO
         1 ONE

carlos@db01.xxxxxx> UPDATE ( SELECT a.ID_N a_ID_N,
  2                  a.C_TXT a_C_TXT,
  3                  b.ID_N b_ID_N,
  4                  b.C_TXT b_C_TXT
  5             FROM PRUEBA01 a,
  6                  PRUEBA02 b
  7            WHERE a.ID_N = b.ID_N)
  8      SET b_C_TXT = a_C_TXT;

2 filas actualizadas.

carlos@db01.xxxxxx> SELECT * FROM PRUEBA02;

      ID_N C_TXT
---------- -------------------------
         1 UNO
         1 UNO

Nota: El ‘hint’ BYPASS_UJVC evita toda la comprobación por parte del optimizador de las ‘key-preserved tables’, pero es un ‘hint’ indocumentado y -en teoría- los mortales no deberíamos utilizarlo, o hacerlo ‘bajo nuestra propia responsabilidad’…

carlos@db01.xxxxxx>  SELECT * FROM PRUEBA01;

      ID_N C_TXT
---------- -------------------------
         1 UNO

carlos@db01.xxxxxx>  SELECT * FROM PRUEBA02;

      ID_N C_TXT
---------- -------------------------
         1

carlos@db01.xxxxxx>  ALTER TABLE PRUEBA01 DROP CONSTRAINT PRUEBA01_PK;

Tabla modificada.

carlos@db01.xxxxxx>  ALTER TABLE PRUEBA02 DROP CONSTRAINT PRUEBA02_PK;

Tabla modificada.

carlos@db01.xxxxxx> UPDATE /*+ BYPASS_UJVC */ ( SELECT a.ID_N a_ID_N,
  2                  a.C_TXT a_C_TXT,
  3                  b.ID_N b_ID_N,
  4                  b.C_TXT b_C_TXT
  5             FROM PRUEBA01 a,
  6                  PRUEBA02 b
  7            WHERE a.ID_N = b.ID_N)
  8      SET b_C_TXT = a_C_TXT;

1 fila actualizada.

Saludos.

Carlos.


Oracle 11g para Windows

24 Octubre 2007

Después de aparecer Oracle 11g para Linux 32 bits y, mas tarde, para Linux 64, llega Oracle 11g para Windows.

Nota: A día de hoy, en metalink Oracle 11g presenta las certificaciones con Status ‘projected’ para Windows XP, Windows 2003 / 2003 SR2 y Windows Vista.

Habrá que echarle un vistazo, pero es que se me acumula el trabajo

Saludos.

Carlos.


Ubuntu 7.10 “Gutsy Gibbon”

18 Octubre 2007

Hoy se lanza Ubuntu 7.10 “Gutsy Gibbon” después de que terminase la ‘cuenta atrás’ que venía apareciendo en el ’site’ de Ubuntu.

Al ir a descargármelo me indicó los sitios disponibles en España. Elegí RedIris y, cuando iba a empezar, me dí cuenta que los ficheros .iso tenían unas sospechosas ‘rc’ en el nombre (rc=‘Release Candidate‘, para los que no ‘chanen’ la lengua de Shakespeare). Así que me fui a otro site: CICA, desde el que estoy en este momento descargándome el .iso. Por cierto, por la tasa de descarga que estoy consiguiendo, debe de haber muchos ‘ubunteros’ en España…

Entre las ‘new features’ anunciadas, me llaman la atención Gnome 2.0 (aunque dicen que KDE 4 es muuuucho mejor), Compiz Fusion (Beryl) incorporado, mejoras en la configuración de X, y la interacción con ‘filesystems’ NTFS (se pueden leer y escribir).

En fin, que cuando termine de bajarme el .iso me pondré a trastear con ello…

Saludos.

Carlos.


Ubuntu al rescate… de Windows XP!

16 Octubre 2007

Un compañero de trabajo llega a nuestro despacho muy nervioso diciendo que su ‘Windows no arranca’… Tras un primer análisis parece que algún error en el disco duro ha inutilizado ficheros del sistema. Resultado: efectivamente, el equipo no arranca.

Por supuesto, no había copia de seguridad y en ese disco duro había ‘archivos importantísimos’… Ante los gestos de preocupación y el acojone, alguien de Sistemas intenta reparar el XP con la consola de reparación… sin resultados visibles. Se intenta arrancar con un CD de Windows XP para recuperar toda la información posible… tampoco.

Así que llego yo con el live CD de Ubuntu 7.04. Lo inserto y arranco el equipo. Ubuntu immediatamente reconoce el disco duro con la partición NTFS. Inserto un ‘pendrive’ USB y le digo al interfecto que vaya salvando toda la información importante que necesite, lo cual hace en unos pocos minutos.

Tras reinstalar XP, se restauran los ficheros salvados sin mayor problema…

Saludos.

Carlos.


¡Qué miedo! (Un redolog a la porra y…)

8 Octubre 2007

Después de montar una instancia intento abrir la base de datos y:

SQL> alter database open;

alter database open

*

ERROR at line 1:

ORA-00322: log 5 of thread 1 is not current copy

ORA-00312: online log 5 thread 1: 'blablabla/blablabla/db01/redo/redo05.log'

Bueno. No importa. Acabo de hacer un backup en frío, así que:

SQL> alter database drop logfile group 5;

alter database drop logfile group 5

*

ERROR at line 1:

ORA-00350: log 5 of instance db01 (thread 1) needs to be archived

ORA-00312: online log 5 thread 1: 'blablabla/blablabla/db01/redo/redo05.log'

¡Coño! Pues quito el archive y ya está:

SQL> alter system archive log stop;
System altered.

SQL>  alter database drop logfile group 5;

 alter database drop logfile group 5

*

ERROR at line 1:

ORA-00350: log 5 of instance db01 (thread 1) needs to be archived

ORA-00312: online log 5 thread 1: 'blablabla/blablabla/db01/redo/redo05.log'

A ver si archivando a mano…

SQL> archive log all

ORA-16038: log 5 sequence# 1875 cannot be archived

ORA-00312: online log 5 thread 1: 'blablabla/blablabla/db01/redo/redo05.log'SQL> ALTER DATABASE OPEN;

ALTER DATABASE OPEN

*

ERROR at line 1:

ORA-00322: log 5 of thread 1 is not current copy

ORA-00312: online log 5 thread 1: 'blablabla/blablabla/db01/redo/redo05.log'

¡Joder! Esto se pone feo… ¿Cómo me cepillo el redolog file pocho sin necesidad de recuperar desde RMAN?

A ver si así…

SQL> SHUTDOWN IMMEDIATE;

ORA-01109: database not open
Database dismounted.

ORACLE instance shut down.

SQL> startup mount;

ORA-32004: obsolete and/or deprecated parameter(s) specified

ORACLE instance started.

Total System Global Area 1610612736 bytes

Fixed Size                  1262068 bytes

Variable Size             201330188 bytes

Database Buffers         1392508928 bytes

Redo Buffers               15511552 bytes

Database mounted.

SQL> recover database until cancel;

Media recovery complete.

SQL> alter database open resetlogs;

Database altered.

SQL> alter system archive log start;

System altered.

SQL> alter system switch logfile;

System altered.

SQL>  alter system switch logfile;

System altered.

SQL>  alter system switch logfile;

System altered.

SQL>  alter system switch logfile;

System altered.

SQL>  alter system switch logfile;

System altered.

SQL>  alter system switch logfile;

System altered.

SQL> select sysdate from dual;

SYSDATE

--------

08/10/07

A ver el sistema operativo:

[oracle@oracel]$ ls /blablabla/blablabla/redo -la

total 615056

drwxr-xr-x 2 oracle oinstall      4096 Oct  8 17:27 .

drwxr-xr-x 4 oracle oinstall      4096 Oct  8 09:34 ..

-rw-r----- 1 oracle oinstall 104858112 Oct  8 17:30 redo01.log

-rw-r----- 1 oracle oinstall 104858112 Oct  8 17:27 redo02.log

-rw-r----- 1 oracle oinstall 104858112 Oct  8 17:27 redo03.log

-rw-r----- 1 oracle oinstall 104858112 Oct  8 17:30 redo04.log

-rw-r----- 1 oracle oinstall 104858112 Oct  8 17:27 redo05.log
[oracle@oracel]$ ls /blablabla/blablabla/archive -la

total 55912

drwxr-xr-x 2 oracle oinstall     4096 Oct  8 17:31 .

drwxr-xr-x 4 oracle oinstall     4096 Oct  8 09:34 ..

-rw-r----- 1 oracle oinstall  1148928 Oct  8 17:30 1_1_635448430.dbf

-rw-r----- 1 oracle oinstall 55247872 Oct  8 17:27 1_1876_632569953.dbf

-rw-r----- 1 oracle oinstall   318976 Oct  8 17:27 1_1877_632569953.dbf

-rw-r----- 1 oracle oinstall     1024 Oct  8 17:27 1_1878_632569953.dbf

-rw-r----- 1 oracle oinstall   417280 Oct  8 17:27 1_1879_632569953.dbf

-rw-r----- 1 oracle oinstall     4096 Oct  8 17:31 1_2_635448430.dbf

-rw-r----- 1 oracle oinstall     1024 Oct  8 17:31 1_3_635448430.dbf

-rw-r----- 1 oracle oinstall     2048 Oct  8 17:31 1_4_635448430.dbf

-rw-r----- 1 oracle oinstall    11776 Oct  8 17:31 1_5_635448430.dbf

-rw-r----- 1 oracle oinstall     2048 Oct  8 17:31 1_6_635448430.dbf

[oracle@oracel]$

Uuuuuffffff!!!!! ¡No gana uno para sustos! ¡Esto es vivir peligrosamente y no ‘La Isla de los Famosos’ esa…

Saludos.

Carlos.


Inserciones en más de una tabla con ‘INSERT’ (‘INSERTs’ multitablas).

4 Octubre 2007

Existe una funcionalidad bastante desconocida en Oracle que permite hacer inserciones en más de una tabla con una única sentencia ‘INSERT’ (Es curioso como ciertas funcionalidades utilísimas son casi desconocidas para la mayoría de la gente, pero esa es otra historia…)

El caso es que podemos hacer que un mismo ‘INSERT’ inserte filas en dos o más tablas, y además podemos hacer que esto se haga de forma condicional (si las filas cumplen ciertas condiciones) o incondicional (todas las filas).

También podemos hacer que si una fila es insertada en una tabla no se inserte en ninguna otra o, por el contrario, que todas las filas se inserten en todas las tablas.

Vamos con un ejemplo de inserción incondicional:

carlos@db01.joe> SELECT * FROM V$VERSION;

BANNER
----------------------------------------------------------------
Oracle Database 10g Enterprise Edition Release 10.2.0.3.0 - Prod
PL/SQL Release 10.2.0.3.0 - Production
CORE    10.2.0.3.0      Production
TNS for 32-bit Windows: Version 10.2.0.3.0 - Production
NLSRTL Version 10.2.0.3.0 - Production

carlos@db01.joe> DESC PRUEBA01;
 Nombre                                    ¿Nulo?   Tipo
 ----------------------------------------- -------- -------------------------
 ID_N                                      NOT NULL NUMBER(38)
 C_TXT1                                             VARCHAR2(10)
 C_TXT2                                             VARCHAR2(10)

carlos@db01.joe> DESC PRUEBA02;
 Nombre                                    ¿Nulo?   Tipo
 ----------------------------------------- -------- -------------------------
 ID_N                                      NOT NULL NUMBER
 C_TXT                                              VARCHAR2(10)

carlos@db01.joe> DESC PRUEBA03;
 Nombre                                    ¿Nulo?   Tipo
 ----------------------------------------- -------- -------------------------
 ID_N                                      NOT NULL NUMBER
 C_TXT                                              VARCHAR2(10)

carlos@db01.joe> SELECT * FROM PRUEBA01;

      ID_N C_TXT1     C_TXT2
---------- ---------- ----------
         1 UNO        ONE
         2 DOS        TWO
         3 TRES       THREE

carlos@db01.joe> INSERT ALL
  2  INTO PRUEBA02 (ID_N, C_TXT)
  3  VALUES (ID_N, C_TXT1)
  4  INTO PRUEBA03 (ID_N, C_TXT)
  5  VALUES (ID_N, C_TXT2)
  6  SELECT ID_N, C_TXT1, C_TXT2
  7  FROM PRUEBA01;

6 filas creadas.

carlos@db01.joe> SELECT * FROM PRUEBA02;

      ID_N C_TXT
---------- ----------
         1 UNO
         2 DOS
         3 TRES

carlos@db01.joe> SELECT * FROM PRUEBA03;

      ID_N C_TXT
---------- ----------
         1 ONE
         2 TWO
         3 THREE

carlos@db01.joe>

Y ahora vamos con un ejemplo de inserción condicional (los pares a una tabla, los impares a otra):

carlos@db01.joe> SELECT ID_N, C_TXT1 FROM PRUEBA01;

      ID_N C_TXT1
---------- ----------
         1 UNO
         2 DOS
         3 TRES

carlos@db01.joe> INSERT ALL
  2  WHEN MOD(ID_N,2) = 0 THEN
  3       INTO PRUEBA02 (ID_N, C_TXT)
  4       VALUES (ID_N, C_TXT1)
  5  WHEN MOD(ID_N,2) != 0 THEN
  6       INTO PRUEBA03 (ID_N, C_TXT)
  7       VALUES (ID_N, C_TXT1)
  8  SELECT ID_N, C_TXT1
  9  FROM PRUEBA01;

3 filas creadas.

carlos@db01.joe> SELECT * FROM PRUEBA02;

      ID_N C_TXT
---------- ----------
         2 DOS

carlos@db01.joe> SELECT * FROM PRUEBA03;

      ID_N C_TXT
---------- ----------
         1 UNO
         3 TRES

carlos@db01.joe>

Una cosa importante a tener en cuenta es que los INSERTs multitabla tienen restricciones. Quizá la más importante es que no se pueden hacer este tipo de inserciones sobre tablas remotas (vía db-links). Lo curioso es que, si lo intentamos, el mensaje que aparece dista mucho de arrojar luz sobre el asunto:

ERROR en línea 3:
ORA-02021: las operaciones DDL no están permitidas en una base de datos remota

¡El analizador sintáctico de SQL toma estos comandos por DDL!

Saludos.

Carlos.