Mostrando postagens com marcador Performance. Mostrar todas as postagens
Mostrando postagens com marcador Performance. Mostrar todas as postagens

domingo, 24 de janeiro de 2021

VM Guest lenta e com elevado percentual de CPU roubada (steal)

Acompanhando a performance de algumas máquinas virtualizadas em um ODA X7-2 HA foi observado durante o período alto percentual de CPU steal (%st) que mostra a porcentagem de tempo gasto (roubado)  em esperas involuntárias da CPU enquanto o hypervisor está atendendo outro processador virtual.

Para dar ênfase ao problema e posteriormente demonstrar a mudança de comportamento junto aos ajustes em uma VM com 8 vCPU, forcei a utilização de CPU disparando comandos "yes" em nohup onde dentro de alguns segundos o percentual de CPU roubada já atingiu 68%

[root@vm04 ~]# cat /proc/cpuinfo | egrep 'processor' | wc -l
8
[root@vm04 ~]#
[root@vm04 ~]# yes > /dev/null &
[1] 26080
[root@vm04 ~]# yes > /dev/null &
[2] 26135
[root@vm04 ~]# yes > /dev/null &
[3] 26137
[root@vm04 ~]# yes > /dev/null &
[4] 26138
[root@vm04 ~]#














Tratando-se de um Oracle engineered systems, busquei como referência o MOS (My Oracle Support) onde encontrei a Doc ID 1928868.1 falando exatamente sobre este comportamento destacando em seu título "Guest VM Running Slow and is not Able to Use All the CPUs Assigned to it on ODA"

Este comportamento ocorre devido ao parâmetro CPU_CAP estar definido como 100 que independente da quantidade de CPUs associados a máquina virtual ela utilizará apenas 1 CPU.

Cancelando o stress de CPU na VM

[root@vm04 ~]# pkill yes
[1]   Terminated              yes > /dev/null
[2]-  Terminated              yes > /dev/null
[3]-  Terminated              yes > /dev/null
[4]+  Terminated              yes > /dev/null

No dom0 confirme o valor do CPU CAP e ajuste dinamicamente seu valor para 0. 

[root@dom0 ~]# xm sched-credit
Name                                ID Weight  Cap
Domain-0                             0  65535    0
oakDom1                              1    256    0
vm03                                 3    256  100
vm04                                 4    256  100


[root@dom0 ~]# xm sched-credit -d vm04 -c 0
[root@dom0 ~]# xm sched-credit
Name                                ID Weight  Cap
Domain-0                             0  65535    0
oakDom1                              1    256    0
vm03                                 3    256  100
vm04                                 4    256    0
[root@dom0 ~]#

Ao ser criado uma VM, o valor padrão para o parâmetro é 100%. Esse valor é então convertido em um limite de utilização da CPU no arquivo vm.cfg para a máquina virtual. 

O valor definido no arquivo vm.cfg limita a quantidade de CPU que um convidado (VM Guest) pode consumir. Se o Processor Cap for definido como 100% no Oracle VM, o valor definido em vm.cfg será 0, o que significa que não há limite para a utilização da CPU.

Retornando a VM vou proceder com um novo teste forçando a utilização das CPUs da mesma forma que a anterior

[root@vm04 ~]# yes > /dev/null &
[1] 31926
[root@vm04 ~]# yes > /dev/null &
[2] 31928
[root@vm04 ~]# yes > /dev/null &
[3] 31929
[root@vm04 ~]# yes > /dev/null &
[4] 31976

Com alguns segundos já foi perceptível que o percentual de CPU roubada não sofria oscilações, estava de forma constante em 0%















Comparando o antes e depois do ajuste

















Referência:

Guest VM Running Slow and is not Able to Use All the CPUs Assigned to it on ODA (Doc ID 1928868.1)

Mais informações →

segunda-feira, 15 de janeiro de 2018

Movendo AWR Snapshots para outro database


As tabelas do AWR (Automatic Workload Repository) contém uma grande quantidade de dados de desempenho extremamente úteis para análises de performance e detecção de problemas. Os dados do AWR são armazenados em tabelas WRH$ e DBA_HIST alimentadas através de snapshots regulares e armazenados por um período de tempo definido na SYSAUX tablespace.

Algumas vezes é necessário que estes dados sejam salvos para utilizações futuras ou mesmo importados em outras bases de dados para comparações, maior tempo de armazenamento em relação ao definido na base origem, etc. A Oracle fornece dois scripts para realizar o export e import dos dados do AWR, são eles: awrextr.sql e awrload.sql, ambos localizados em $ORACLE_HOME/rdbms/admin


O export dos dados é realizado através do script awrextr.sql que gera um arquivo data pump contendo o intervalo de snapshots desejado e que posteriormente pode ser importado em outra base de dados.


1 – Criando o diretório que será gerado o arquivo de dump:

SQL> create directory awr_directory as '/orabackup';
 
Directory created.

2 - Executando o script de export:

SQL> @?/rdbms/admin/awrextr.sql
~~~~~~~~~~~~~
AWR EXTRACT
~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~  This script will extract the AWR data for a range of snapshots  ~
~  into a dump file.  The script will prompt users for the         ~
~  following information:                                          ~
~     (1) database id                                              ~
~     (2) snapshot range to extract                                ~
~     (3) name of directory object                                 ~
~     (4) name of dump file                                        ~
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


Databases in this Workload Repository schema
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

   DB Id     DB Name      Host
------------ ------------ ------------
* 3274873056 DBPRD        tst1

The default database id is the local one: '3274873056'.  To use this
database id, press  to continue, otherwise enter an alternative.

Quando executado o script precisa de 4 interações para gerar o export dos snapshots.

2.A - Informar o DBID da base de dados desejada. O script irá listar todos os dbids disponíveis, se o dbid desejado for o default(*) basta pressionar ENTER ou então primeiramente informar o dbid necessário.

Enter value for dbid: 3274873056

Using 3274873056 for Database ID

2.B - O segundo passo é especificar a quantidade de dias para que a listagem dos snapshots (snap_ids) seja retornada e o período (begin_snap/end_snap) da extração de dados definida.

Specify the number of days of snapshots to choose from
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Entering the number of days (n) will result in the most recent
(n) days of snapshots being listed.  Pressing  without
specifying a number lists all completed snapshots.


Enter value for num_days: 1

Listing the last day's Completed Snapshots

DB Name        Snap Id    Snap Started
------------ --------- ------------------
DBPRD                1 05 Nov 2017 09:50
                     2 05 Nov 2017 11:00
                     3 05 Nov 2017 12:00
                     4 05 Nov 2017 13:00
                     5 05 Nov 2017 14:00
                     6 05 Nov 2017 15:00
                     7 05 Nov 2017 16:00


Specify the Begin and End Snapshot Ids
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Enter value for begin_snap: 2
Begin Snapshot Id specified: 2

Enter value for end_snap: 6
End   Snapshot Id specified: 6

2.C - Especificar o diretório que será utilizado para gerar o arquivo de dump (expdp).

Specify the Directory Name
~~~~~~~~~~~~~~~~~~~~~~~~~~

Directory Name                 Directory Path
------------------------------ -------------------------------------------------
AWR_DIRECTORY                  /orabackup
DATA_PUMP_DIR                  /u01/app/oracle/product/11.2.0.4/dbhome1/rdbms/lo
                               g/

ORACLE_OCM_CONFIG_DIR          /u01/app/oracle/product/11.2.0.4/dbhome1/ccr/host
                               s/tst1/state

ORACLE_OCM_CONFIG_DIR2         /u01/app/oracle/product/11.2.0.4/dbhome1/ccr/stat
                               e


Choose a Directory Name from the above list (case-sensitive).

Enter value for directory_name: AWR_DIRECTORY

Using the dump directory: AWR_DIRECTORY

2.D - Por fim, informar o nome do arquivo data pump que será gerado. O nome não deve conter a extensão .dmp, ele será acrescentado automaticamente pelo script.

Specify the Name of the Extract Dump File
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The prefix for the default dump file name is awrdat_2_6.
To use this name, press  to continue, otherwise enter
an alternative.

Enter value for file_name: awrdata_dbprd_2_6

Using the dump file prefix: awrdata_dbprd_2_6
|
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|  The AWR extract dump file will be located
|  in the following directory/file:
|   /orabackup
|   awrdata_dbprd_2_6.dmp
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|  *** AWR Extract Started ...
|
|  This operation will take a few moments. The
|  progress of the AWR extract operation can be
|  monitored in the following directory/file:
|   /orabackup
|   awrdata_dbprd_2_6.log
|

End of AWR Extract

Dependendo da quantidade de dados AWR que precisa ser extraído a operação de extração pode demorar um pouco para ser concluída. Uma vez concluída você pode levar o arquivo para o local ou servidor desejado.

O import de dados é realizado através do script awrload.sql utilizando o dump gerado anteriormente pelo script awrextr.sql, conforme demonstrado abaixo:

1 - Criando o diretório em outra base de dados com o caminho onde se encontra o arquivo de dump:

SQL> create directory awr_directory as '/orabackup';

Directory created.

2 - Executando o script de import:

SQL> @?/rdbms/admin/awrload.sql
~~~~~~~~~~
AWR LOAD
~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~  This script will load the AWR data from a dump file. The   ~
~  script will prompt users for the following information:    ~
~     (1) name of directory object                            ~
~     (2) name of dump file                                   ~
~     (3) staging schema name to load AWR data into           ~
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Da mesma forma que no script de export, o script de import também requer algumas interações.

2.A - Informar o nome do diretório que contem o arquivo.

Specify the Directory Name
~~~~~~~~~~~~~~~~~~~~~~~~~~

Directory Name                 Directory Path
------------------------------ -------------------------------------------------
AWR_DIRECTORY                  /orabackup
DATA_PUMP_DIR                  /u01/app/oracle/product/11.2.0.4/dbhome1/rdbms/lo
                               g/

ORACLE_OCM_CONFIG_DIR          /u01/app/oracle/product/11.2.0.4/dbhome1/ccr/host
                               s/tst1/state

ORACLE_OCM_CONFIG_DIR2         /u01/app/oracle/product/11.2.0.4/dbhome1/ccr/stat
                               e


Choose a Directory Name from the list above (case-sensitive).

Enter value for directory_name: AWR_DIRECTORY

Using the dump directory: AWR_DIRECTORY

2.B - Informar o nome do arquivo contendo os dados AWR extraídos anteriormente. Não é preciso incluir junto ao nome do arquivo sua extensão (.dmp), ela será adicionada automaticamente pelo script.

Specify the Name of the Dump File to Load
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Please specify the prefix of the dump file (.dmp) to load:

Enter value for file_name: awrdata_dbprd_2_6

Loading from the file name: awrdata_dbprd_2_6.dmp

2.C - Nesta etapa deve ser informado o nome de um schema temporário para que os dados sejam importados e posteriormente transferidos para as tabelas do AWR. Após a transferencia dos dados para as tabelas do AWR o schema é automaticamente removido.

Informações como a tablespace permanente e temporária do schema também serão solicitadas nesta etapa e ao fim os dados serão finalmente importados.

Staging Schema to Load AWR Snapshot Data
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The next step is to create the staging schema
where the AWR snapshot data will be loaded.
After loading the data into the staging schema,
the data will be transferred into the AWR tables
in the SYS schema.


The default staging schema name is AWR_STAGE.
To use this name, press  to continue, otherwise enter
an alternative.

Enter value for schema_name: AWR_STAGE

Using the staging schema name: AWR_STAGE

Choose the Default tablespace for the AWR_STAGE user
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Choose the AWR_STAGE users's default tablespace.  This is the
tablespace in which the AWR data will be staged.

TABLESPACE_NAME                CONTENTS  DEFAULT TABLESPACE
------------------------------ --------- ------------------
SYSAUX                         PERMANENT *

Pressing  will result in the recommended default
tablespace (identified by *) being used.

Enter value for default_tablespace: SYSAUX

Using tablespace SYSAUX as the default tablespace for the AWR_STAGE


Choose the Temporary tablespace for the AWR_STAGE user
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Choose the AWR_STAGE user's temporary tablespace.

TABLESPACE_NAME                CONTENTS  DEFAULT TEMP TABLESPACE
------------------------------ --------- -----------------------
TEMP                           TEMPORARY *

Pressing  will result in the database's default temporary
tablespace (identified by *) being used.

Enter value for temporary_tablespace: TEMP

Using tablespace TEMP as the temporary tablespace for AWR_STAGE


... Creating AWR_STAGE user

|
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|  Loading the AWR data from the following
|  directory/file:
|   /orabackup
|   awrdata_dbprd_2_6.dmp
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|  *** AWR Load Started ...
|
|  This operation will take a few moments. The
|  progress of the AWR load operation can be
|  monitored in the following directory/file:
|   /orabackup
|   awrdata_dbprd_2_6.log
|
... Dropping AWR_STAGE user

End of AWR Load

Dependendo da quantidade de dados AWR que serão importadas a operação pode demorar um pouco para ser concluída.

3 - Verificando se os snapshots do DBID desejado foram importados:

SQL> select distinct dbid from DBA_HIST_SNAPSHOT;

      DBID
----------
3274873056
3149615557

Referências:

How to Export and Import the AWR Repository From One Database to Another (Doc ID 785730.1)
Mais informações →

quinta-feira, 7 de dezembro de 2017

Exadata - Monitorando a temperatura ambiente dos servidores

Manter a temperatura dos servidores dentro das especificações de desing do Oracle Exadata Rack ajuda a alcançar a máxima eficiência e vida útil dos equipamentos, desta forma, temperaturas ambiente fora da faixa de 21 a 23 graus Celsius acabam afetando todos os componentes dentro do Oracle Exadata causando possíveis problemas de desempenho, maior consumo de energia e tempo de vida reduzido.

Para os servidores do Oracle Exadata (database servers e storage servers) a temperatura registrada pelo sensor deve ficar na faixa de 5 a 32 graus Celsius, estando fora desta faixa significa que o sistema Exadata está operando em um ambiente com um potencial problema de resfriamento que precisa ser investigado e solucionado.

Os Exadata Storage Servers (cell nodes), por exemplo, possuem um threshold configurado e geram um alerta via e-mail caso a temperatura ultrapasse os 32 graus Celsius (logicamente se as configurações para envio estiverem corretas).

Para realizar a configuração e validação de envio de e-mail basta utilizar a Doc ID 2078602.1 - How to set up Exadata storage cell to send notifications about alerts, no My Oracle Support (MOS) como referência.

Além do envio de e-mail supracitado é possível realizar validações e/ou monitoramentos rotineiros das temperaturas do servidores de forma a garantir que o ambiente esta operando dentro das condições recomendadas.

Conectado em um dbnode (database server), crie o arquivo all_group (se ainda não existir) contendo o ip de todos os database servers e storage servers do Exadata.

[root@dbnode1 ~]# cat all_group
170.10.0.10
170.10.0.11
170.10.0.12
170.10.0.13
170.10.0.14

Para validar se já existe equivalência (chave pública) configurada entre os servidores execute o comando abaixo que irá retorna o hostname dos servidores:

[root@dbnode1 ~]# dcli -g all_group -l root 'hostname'
170.10.0.10: dbnode1
170.10.0.11: dbnode2
170.10.0.12: cellnode1
170.10.0.13: cellnode2
170.10.0.14: cellnode3

Caso ocorra erros informando a necessidade de password, basta executar o procedimento abaixo para criar a equivalência:

[root@dbnode1 ~]# dcli -g all_group -l root -k
root@dbnode1's password:
root@dbnode2's password:
root@cellnode1's password:
root@cellnode2's password:
root@cellnode3's password:
dbnode1: ssh key added
dbnode2: ssh key added
cellnode1: ssh key added
cellnode2: ssh key added
cellnode3: ssh key added

Verificando a temperatura dos servidores:

[root@dbnode1 ~]# dcli -g all_group -l root ipmitool sensor list | grep degree | grep T_AMB
170.10.0.10: T_AMB            | 21.000     | degrees C  | ok    | 3.000     | na        | na        | na        | na        | na
170.10.0.11: T_AMB            | 21.000     | degrees C  | ok    | 3.000     | na        | na        | na        | na        | na
170.10.0.12: T_AMB            | 22.000     | degrees C  | ok    | 3.000     | na        | na        | na        | na        | na
170.10.0.13: T_AMB            | 23.000     | degrees C  | ok    | 3.000     | na        | na        | na        | na        | na
170.10.0.14: T_AMB            | 24.000     | degrees C  | ok    | 3.000     | na        | na        | na        | na        | na

[root@dbnode1 ~]# dcli -g all_group -l root 'ipmitool sunoem cli "show /SYS/T_AMB" | grep value'
170.10.0.10: value = 21.250 degree C
170.10.0.11: value = 21.250 degree C
170.10.0.12: value = 21.500 degree C
170.10.0.13: value = 22.500 degree C
170.10.0.14: value = 23.500 degree C

Estando a saida fora do intervalo de temperatura recomendado, investigue e corrija o problema.

Os seguintes itens devem ser analisados:


  • Fluxo de ar suficiente no rack;
  • A temperatura ambiente está dentro do intervalo especificado;
  • A parte traseira da prateleira está livre de obstruções.
Referências:


https://docs.oracle.com/cd/E80920_01/DBMMN/exadata-general-maintenance.htm#DBMMN21919
EXADATA HW: High Temperature (CL_TEMP) Alert From One Of The Cell Nodes (Doc ID 1544182.1)
Oracle Exadata Database Machine Setup/Configuration Best Practices (Doc ID 1274318.1)
How to set up Exadata storage cell to send notifications about alerts (Doc ID 2078602.1)
Mais informações →

quinta-feira, 26 de outubro de 2017

Copiando arquivos do ASM para uma instance ASM remota

Uma feature muito útil existente desde a versão 11.1 do Automatic Storage Management (ASM) Command-Line Utility (ASMCMD) é o cp, além de permitir a cópia local de arquivos permite também a cópia remota entre instances ASM (coisa que nem todos sabem). 

Realizando a cópia remota de arquivos do ASM para ASM, descarta-se a necessidade de uma unidade auxilar (NFS, file system, ..) para a transição do arquivo, além de realizar uma conversão transparente entre Little-Endian e Big-Endian caso necessário. Para copias de arquivos non-ASM ou para um diskgroup ASM não existe a conversão direta do endian sendo necessário a utilização de ferramentas complementares para isto (RMAN).

Sintaxe para cópia remota: (caso o password não seja informado no comando ele será solicitado posteriormente, conforme exemplo abaixo)

cp [FILE NAME] [REMOTE ASM USER]/[PASSWORD]@[SERVER NAME].[ASM INSTANCE]:[REMOTE DESTINTATION]


ASMCMD> cp TOOLS.732.947846879 sys@server2.+ASM:+DGDATA/TESTE/TOOLS01.dbf
Enter password: **********
copying +DGDATA/ORCL1/datafile/TOOLS.732.947846879 -> server2:+DGDATA/TESTE/TOOLS01.dbf

ASMCMD> pwd
+DGDATA/TESTE
ASMCMD> ls -lt
Type      Redund  Striped  Time             Sys  Name
                                            N    TOOLS01.dbf => +DGDATA/ASM/DATAFILE/TOOLS01.dbf.10182.958218431
ASMCMD>

Caso a instance ASM no servidor remoto possua uma porta diferente da default 1521, especifique a porta através da opção --port

ASMCMD> cp --port 1525 TOOLS.732.947846879 sys@server2.+ASM:+DGDATA/TESTE/TOOLS01.dbf

Limitações do cp:


  • Não permite a cópia de arquivos como OCR ou SPFILE;
  • Não pode copiar arquivos entre duas instâncias remotas. A instância local do Oracle ASM deve ser a origem ou o destino da operação. 

Além do cp do ASMCMD existe o DBMS_FILE_TRANSFER que também permite a cópia de arquivos de forma local e remota realizando automaticamente (por padrão) a conversão de endian (11.2.0.4 e posterior).

"From 12c and in 11.2.0.4 DBMS_FILE_TRANSFER does the conversion by default. Using DBMS_FILE_TRANSFER the destination database converts each block when it receives a file from a platform with different endianness. Datafiles can be imported after they are moved to the destination database as part of a transportable operation without RMAN conversion."



PUT_FILE:


-- remote database
SQL> create directory dir2 as '+DGDATA/TESTE2';

Directory created.

-- local database
SQL> create directory dir1 as '+DGDATA/TESTE2';

Directory created.

SQL> create database link remote connect to user_copy identified by pwd1 using 'ORCL1';

Database link created.

BEGIN
  DBMS_FILE_TRANSFER.put_file(
   source_directory_object      => 'DIR1',
   source_file_name             => 'EXEMPLO.DBF',
   destination_directory_object => 'DIR2',
   destination_file_name        => 'EXEMPLO.DBF',
   destination_database         => 'REMOTE');
END;
/

PL/SQL procedure successfully completed.

-- remote ASM
ASMCMD> ls -lt
Type        Redund  Striped  Time             Sys  Name
                                              N    EXEMPLO.DBF => +DGDATA/ORCL1/DATAFILE/FILE_TRANSFER/EXEMPLO.DBF.10195.958231667
GET_FILE:

-- remote database
SQL> create directory dir1 as '/u01/orcl1/datafile';

Directory created.

-- local database
SQL> create directory dir2 as '+DGDATA/TESTE2';

Directory created.

SQL> create database link remote connect to user_copy identified by pwd1 using 'ORCL1';

Database link created.

BEGIN
  DBMS_FILE_TRANSFER.get_file(
   source_directory_object      => 'DIR1',
   source_file_name             => 'EXEMPLO.DBF',
   source_database              => 'REMOTE',
   destination_directory_object => 'DIR2',
   destination_file_name        => 'EXEMPLO.DBF');
END;
/

PL/SQL procedure successfully completed.

-- local ASM
ASMCMD> ls -lt
Type        Redund  Striped  Time             Sys  Name
                                              N    EXEMPLO.DBF => +DGDATA/ORCL2/DATAFILE/FILE_TRANSFER/EXEMPLO.DBF.1774.958232287


Referências:

https://docs.oracle.com/cd/B28359_01/server.111/b31107/asm_util.htm#OSTMG94243
https://docs.oracle.com/database/122/OSTMG/asmcmd-file-commands.htm#OSTMG94462
How to Migrate to different Endian Platform Using Transportable Tablespaces With RMAN (Doc ID 371556.1)
Mais informações →

terça-feira, 29 de agosto de 2017

Row Locks (TX) - Como identificar a linha bloqueada

Em um banco de dados multiusuários como o Oracle database, é necessário que exista algum tipo de bloqueio de dados para que possam ser resolvidos os problemas associados à simultaneidade, consistência e integridade de dados. Estes bloqueios são mecanismos que impedem a interação destrutiva entre as transações que acessam o mesmo recurso.

Um dos tipos de bloqueios existentes é o bloqueio (lock) de linha identificado pelo evento de espera (wait event) "enq: TX - row lock contention".

Um lock do tipo linha ocorre naturalmente sobre a linha de uma tabela, ou seja, enquanto uma linha (registro) está "alocado", nenhuma outra sessão conseguirá alterá-lo até que a transação que originou o lock chegue ao seu fim, seja por commit ou rollback.


A identificação de um lock transacional (TX) pode ser realizada de diferentes formas, aqui utilizaremos o script utllockt.sql distribuído pela própria Oracle e encontrado abaixo do $ORACLE_HOME/rdbms/admin

Montando o cenário:

SESSÃO 1:


SQL> select distinct sid from v$mystat;

       SID
----------
        58

SQL> create table teste (id number);

Table created.

SQL> insert into teste values (1);

1 row created.

SQL> insert into teste values (2);

1 row created.

SQL> insert into teste values (3);

1 row created.

SQL> insert into teste values (4);

1 row created.

SQL> commit;

Commit complete.

SQL> update teste set id=5 where id=1;

1 row updated.

Observe que ao final do UPDATE não foi executado commit ou rollback;

SESSÃO 2:


SQL> select distinct sid from v$mystat;

       SID
----------
        51

SQL> update teste set id=6 where id=2;

1 row updated.

SQL> update teste set id=5 where id=1;


A sessão 2 (sid=51) ao tentar realizar a alteração da mesma linha da sessão 1 (sid=58) ficou "travada" pois a linha id=1 está bloqueada até que a sessão 1 realize um commit ou rollback do registro.


SESSÃO 3:


SQL> select distinct sid from v$mystat;

       SID
----------
        56

SQL> update teste set id=6 where id=2;

A sessão 3 (sid=56) ao tentar alterar a mesma linha que a sessão 2 (sid=51) também ficou presa, pois a sessão 2 ainda não concluiu sua transação, desta forma existe a sessão 2 aguardando a sessão 1 e a sessão 3 aguardando a sessão 2 conforme árvore de bloqueio abaixo:

SESSÃO 4:



SQL> set lines 200
SQL> @?/rdbms/admin/utllockt.sql
drop table lock_holders
           *
ERROR at line 1:
ORA-00942: table or view does not exist



Table created.

drop   table dba_locks_temp
             *
ERROR at line 1:
ORA-00942: table or view does not exist



Table created.


2 rows created.


Commit complete.


Table dropped.


1 row created.


Commit complete.


WAITING_SESSION   LOCK_TYPE         MODE_REQUESTED MODE_HELD      LOCK_ID1          LOCK_ID2
----------------- ----------------- -------------- -------------- ----------------- -----------------
58                None
   51             Transaction       Exclusive      Exclusive      196619            958
      56          Transaction       Exclusive      Exclusive      327712            947


Table dropped.

Cenário de bloqueio:



Identificando as linhas bloqueadas:

Sempre que uma sessão fica aguardando um lock transacional (row lock – TX), algumas informações adicionais são pobuladas na v$session:




Utilizando-se destes dados é possível identificar o ROWID da linha bloqueada.

O ROWID é uma pseudocolumn que representa o endereço de cada linha de uma tabela. Os valores desta pseudocoluna são strings que podem conter os caracteres A-Z, a-z, 0-9 e o sinal de mais (+) e a barra (/).

Os rowids são formados pelas seguintes informações:

  •          Data block: Bloco de dados que contém a linha;
  •          Row: Linha no bloco de dados;
  •          Database file: Arquivo de dados que a linha;
  •         Data object number: Número de identificação do objeto.


Agora basta utilizar o pacote DBMS_ROWID para interpretar os conteúdos de rowid. As funções do pacote extraem e fornecem informações sobre os quatro elementos rowid listados acima.

Como as colunas ROW_WAIT_* da v$session são populadas apenas quando uma sessão está aguardando alguma transação, podemos identificar a rowid (endereço da linha) que as sessões 2 (sid=51) e 3 (sid=56) estão aguardando.


Linha bloqueada da sessão 2:


SQL> set lines 200
SQL> col object_name for a30
SQL> select o.object_name,
  2  s.row_wait_obj#,
  3  s.row_wait_file#,
  4  s.row_wait_block#,
  5  s.row_wait_row#,
  6  dbms_rowid.rowid_create ( 1, s.ROW_WAIT_OBJ#, s.ROW_WAIT_FILE#, s.ROW_WAIT_BLOCK#, s.ROW_WAIT_ROW# ) as "ROWID"
  7  from v$session s, dba_objects o
  8  where s.sid=51
  9  and s.ROW_WAIT_OBJ# = o.OBJECT_ID;


OBJECT_NAME                    ROW_WAIT_OBJ# ROW_WAIT_FILE# ROW_WAIT_BLOCK# ROW_WAIT_ROW# ROWID
------------------------------ ------------- -------------- --------------- ------------- ------------------
TESTE                                  57927              1           83065             0 AAAOJHAABAAAUR5AAA


SQL> select * from teste where rowid='AAAOJHAABAAAUR5AAA';

        ID
----------
         1

 Linha bloqueada da sessão 3:



SQL> select o.object_name,
  2  s.row_wait_obj#,
  3  s.row_wait_file#,
  4  s.row_wait_block#,
  5  s.row_wait_row#,
  6  dbms_rowid.rowid_create ( 1, s.ROW_WAIT_OBJ#, s.ROW_WAIT_FILE#, s.ROW_WAIT_BLOCK#, s.ROW_WAIT_ROW# ) as "ROWID"
  7  from v$session s, dba_objects o
  8  where s.sid=56
  9  and s.ROW_WAIT_OBJ# = o.OBJECT_ID;


OBJECT_NAME                    ROW_WAIT_OBJ# ROW_WAIT_FILE# ROW_WAIT_BLOCK# ROW_WAIT_ROW# ROWID
------------------------------ ------------- -------------- --------------- ------------- ------------------
TESTE                                  57927              1           83065             1 AAAOJHAABAAAUR5AAB


SQL> select * from teste where rowid='AAAOJHAABAAAUR5AAB';

        ID
----------
         2

Neste exemplo (cenário), como não foi utilizado bind variables nas operações DML é possível identificar também o valor bloqueado pelo próprio texto sql da instrução:



SQL> select sql_id from v$session where sid=51;

SQL_ID
-------------
g7tsgdb2thq5t

SQL> select sql_fulltext from v$sql where sql_id='g7tsgdb2thq5t';

SQL_FULLTEXT
--------------------------------------------------------------------------------
update teste set id=5 where id=1

SQL> select sql_id from v$session where sid=56;

SQL_ID
-------------
7r7ugkhm1bnaz

SQL> select sql_fulltext from v$sql where sql_id='7r7ugkhm1bnaz';

SQL_FULLTEXT
--------------------------------------------------------------------------------
update teste set id=6 where id=2


Com a utilização de bind variables a instrução retornada seria similar a:



update teste set id=:b2 where id=:b1


A identificação da instrução também é importante para saber quais colunas estão sofrendo alterações, pois a linha retornada pelo rowid pode conter várias colunas diferentemente do exemplo onde a tabela possuía apenas uma coluna.


Referências:

https://docs.oracle.com/database/122/CNCPT/data-concurrency-and-consistency.htm#CNCPT1313
https://docs.oracle.com/database/122/REFRN/V-SESSION.htm#REFRN30223

https://docs.oracle.com/database/122/SQLRF/Data-Types.htm#SQLRF50998
Mais informações →

quarta-feira, 28 de junho de 2017

Reproduzindo SQL problems com o Oracle SQL Test Case Builder

Para a maioria dos problemas envolvendo um comando SQL, uma das principais causas que compromete a rápida resolução do problema é sua replicação para outro ambiente, seja para uma nova base de dados interna, onde testes e alterações poderiam ser conduzidas sem afetar diretamente o ambiente produtivo ou mesmo para encaminha-lo ao suporte da Oracle em busca de apoio junto a resolução do problema.

Uma das novas características do banco de dados Oracle introduzida desde a versão 11g Release 1 (com backporting para a versão 10.2.0.4) é o SQL Test Case Builder (TCB), seu objetivo é reunir de forma automática o máximo de informações possíveis relacionadas a um SQL problemático/incident e permitir a criação um ambiente totalmente reproduzível para diagnostico e reparo do problema.

As informações coletadas automaticamente pelo SQL Test Case Builder incluem:

·           Definições de todos objetos envolvidos (tabelas, índices, funções, procedures e packages);
·           Estatísticas do otimizador;
·           Plano de execução;
·           Definições de parâmetros de inicialização;
·           SQL profiles, stored outlines e outros SQL Management Objects;
·     Dados da(s) tabela(s) sendo utilizada(s) no processo (por padrão não exporta os dados) - utiliza o expdp; entre outras.

A saída (output) do TCB consiste em uma coleção de arquivos e scripts contendo os comandos necessários para se recriar todos os objetos e configurações do ambiente envolvidos no processo e que podem ser posteriormente importados em outra base de dados ou enviados ao suporte Oracle (MOS).

Todo o funcionamento do SQL Test Case Builder está abaixo do pacote DBMS_SQLDIAG onde existem as opções para exportar e importar os SQL testcases. O export do SQL testcase pode ser realizado através de um texto SQL (sql_text), sql_id ou número de um incidente gerado na base de dados (incident_id).


Para que os arquivo sejam gerados em disco, em todos os casos será necessário a utilização de um diretório (directory).


SQL> create or replace directory TCB_DIR as '/u01/tcbcase';

Directory created.

Abaixo estará sendo criado um conjunto de arquivos do Test Case Builder (TCB) relativo ao comando SQL especificado no parâmetro sql_text. Está sendo utilizado o testcase_name que incluirá o prefixo informado no parâmetro no início do nome dos arquivos criados e também é especificado o parâmetro exportData como TRUE de modo que seja exportado os dados dos segmentos envolvidos e não apenas a estrutura.

O parâmetro user_name representa o schema dos objetos envolvidos no processo. É utilizado apenas quando se exporta o test case através de um texto SQL (sql_text).


SQL> DECLARE
 result clob;
 BEGIN
 dbms_sqldiag.export_sql_testcase(
        directory     => 'TCB_DIR',
        user_name     => 'ANDERSON',
        sql_text      => 'select * from colaboradores',
        exportData    => TRUE,
        testcase_name => 'tcb_file_',
        testcase      => result);
 END;
/

PL/SQL procedure successfully completed.

Conjunto de arquivos criados:


[oracle@db3 ~]$ cd /u01/tcbcase
[oracle@db3 tcbcase]$ ls -lrt
total 396
-rw-r--r--. 1 oracle oinstall   2783 Mar 31 15:50 tcb_file_README.txt
-rw-r--r--. 1 oracle oinstall    256 Mar 31 15:50 tcb_file_ol.xml
-rw-r--r--. 1 oracle oinstall    204 Mar 31 15:50 tcb_file_sql.xml
-rw-r--r--. 1 oracle oinstall    402 Mar 31 15:50 tcb_file_prmimp.sql
-rw-r--r--. 1 oracle oinstall     63 Mar 31 15:50 tcb_file_ts.xml
-rw-r--r--. 1 oracle oinstall   4684 Mar 31 15:50 tcb_file_dpexp.sql
-rw-r-----. 1 oracle oinstall 335872 Mar 31 16:05 tcb_file_dpexp.dmp
-rw-r-----. 1 oracle oinstall    936 Mar 31 16:05 tcb_file_dpexp.log
-rw-r--r--. 1 oracle oinstall   4271 Mar 31 16:05 tcb_file_dpimp.sql
-rw-r--r--. 1 oracle oinstall    847 Mar 31 16:05 tcb_file_ssimp.sql
-rw-r--r--. 1 oracle oinstall   1066 Mar 31 16:05 tcb_file_smrpt.html
-rw-r--r--. 1 oracle oinstall    408 Mar 31 16:05 tcb_file_xpls.sql
-rw-r--r--. 1 oracle oinstall    699 Mar 31 16:05 tcb_file_xplo.sql
-rw-r--r--. 1 oracle oinstall    434 Mar 31 16:05 tcb_file_xplf.sql
-rw-r--r--. 1 oracle oinstall   2103 Mar 31 16:05 tcb_file_xpl.txt
-rw-r--r--. 1 oracle oinstall   2137 Mar 31 16:05 tcb_file_main.xml

O arquivo xxxxxxmain.xml, neste caso tcb_file_main.xml, é o arquivo de controle do SQL TCB, nele contém todas as descrições do test case e será utilizado como base para o processo de importação dos arquivos em outra base de dados.

Para criar o TCB através de um sql_id ou incident_id basta substituir o parâmetro sql_text por sql_id/incident_id especificando o identificador único do comando SQL (sql_id) ou o número do incidente SQL (incident_id).


SQL> select sql_id from v$sql where sql_text = 'select * from colaboradores';

SQL_ID
-------------
fnvr0vjw6f4ah

SQL> host rm -f /u01/tcbcase/*

SQL> DECLARE
 result clob;
 BEGIN
 dbms_sqldiag.export_sql_testcase(
        directory     => 'TCB_DIR',
        sql_id        => 'fnvr0vjw6f4ah',
        exportData    => TRUE,
        testcase_name => 'tcb_file_',
        testcase      => result);
 END;
/

PL/SQL procedure successfully completed.

Um SQL incident é gerado quando existe um SQL problemático em que múltiplos erros críticos são registrados (ORA-xxxxx). Através do Automatic Diagnostic Repository (ADR) a base de dados cria automaticamente um incidente para cada ocorrência do problema, sua localização é semelhante ao demonstrado abaixo onde o incnum representa o número do incident.

$ADR_HOME/incident/incdir_incnum

Exemplo de um incidente válido:


/orabin/app/oracle/diag/rdbms/cdb1/cdb1/incident/incdir_2557

SQL> host rm -f /u01/tcbcase/*

SQL> DECLARE
 result clob;
 BEGIN
 dbms_sqldiag.export_sql_testcase(
        directory     => 'TCB_DIR',
        incident_id   => 2557,
        exportData    => TRUE,
        testcase_name => 'tcb_file_',
        testcase      => result);
 END;
/

PL/SQL procedure successfully completed.

Afim de demonstração do processo de importação do SQL Test Case Builder em outra base de dados, foi gerado novamente o test case com base na consulta da tabela colaboradores (sql_text/sql_id).

Base de dados onde a estrutura ainda não existe.



SQL> desc anderson.colaboradores;
ERROR:
ORA-04043: object anderson.colaboradores does not exist

SQL> select username from dba_users where username='ANDERSON';

no rows selected

Criação do diretório (directory) apontando o caminho onde estão os arquivos:

SQL> create or replace directory TCB_DIR as '/u01/tcbcase';

Directory created.

Criação do usuário (schema) conforme a base de dados source:

SQL> create user anderson identified by anderson;

User created.

SQL> grant dba to anderson;

Grant succeeded.

Importando o test case:

SQL> conn anderson/anderson@pdb2;
Connected.

O parâmetro filename recebe como entrada o nome do arquivo de controle XML citado anteriormente que é identificado como *main.xml


SQL> BEGIN
 dbms_sqldiag.import_sql_testcase(
  directory     => 'TCB_DIR',
  filename      => 'tcb_file_main.xml');
 END;
/

PL/SQL procedure successfully completed.

Estando concluído o processo de importação a base de dados com o test case está pronta, basta agora executar os procedimentos/comandos problemáticos para que o impasse ocorra e que o diagnóstico e reparo possa ser empregado.

Algumas considerações importantes presentes no README gerado pelo TCB:


-- Note:
--
--      !!! You should not run TCB under user SYS !!!
--      Use another user, such as tcb, who can be granted dba role
--
--     .The <DIRECTORY_PATH_4_TCB_IMPORT> is the CURRENT directory where
--      all the TCB files have resided. It must be an OS path on local
--      machine, such as '/tmp/bug8010101'. It cannot be a path to other
--      machine, for example by mounting over a network file system.
--
--     .By default for TCB, the data is NOT exported
--      In some case data is required, for example, to diagnose wrong
--      result problem.
--        To export data, call export_sql_testcase() with
--           exportData=>TRUE
--
--      Note the data will be imported by default, unless turned OFF by
--           importData=>FALSE
--
--     .TCB includes PL/SQL package spec by default , but not
--      the PL/SQL package body.
--      You may need to have the package body as well, for example,
--      to invoke the PL/SQL functions.
--        To export PL/SQL package body, call export_sql_testcase() with
--           exportPkgbody=>TRUE
--        To import PL/SQL package body, call import_sql_testcase() with
--           importPkgbody=>TRUE
--
--     .An example that you need to include PL/SQL package (body) is
--      you have VPD function defined in a package


Referências:
https://docs.oracle.com/cd/B28359_01/server.111/b28274/sql_overview.htm#PFGRF16001
https://docs.oracle.com/database/122/TGSQL/sql-test-case-builder.htm#TGSQL94870
How To Use SQL Test Case Builder [Video] (Doc ID 1208143.1)
Mais informações →

segunda-feira, 24 de abril de 2017

Importando dados com o Oracle SQL*Loader

O Oracle SQL*Loader é um utilitário presente tanto na instalação do Oracle Server quanto na instalação do Oracle client e sua função é basicamente ler arquivos textos e inserir os dados no banco de dados.

Sua vantagem é de ser extremamente flexível e rápido em relação aos tradicionais scripts de INSERT o que permite a inserção de milhões de linhas em poucos segundos.

 Características do SQL*Loader:

·      Permite carregar dados via rede, ou seja, o arquivo de dados pode estar em um sistema diferente do banco de dados (userid=username/password@instance);

·      Permite carregar dados de vários arquivos de dados durante a mesma sessão de carga;

·      Permite Carregar dados em várias tabelas durante a mesma sessão de carga;

·      Permite especificar o conjunto de caracteres (CHARACTERSET) dos dados;

·      Permite carregar dados de forma seletiva (pode carregar registos com base nos valores dos registos) - "when" categoria='Bebida';

·      Permite a manipulação dos dados antes de carregá-los, usando funções SQL;

·      Permite gerar valores sequencias para uma determinada coluna, entre outras.

A inserção dos dados é realizada com base em um arquivo de controle (control file) onde estão previamente configuradas informações como a localização do arquivo de dados (data file), como analisar e interpretar os dados, onde inserir os dados, entre outras configurações possíveis.

Além do arquivo de controle (control file) e arquivo de dados (data file), a estrutura do SQL*Loader é composta ainda pelo arquivo de log (log file) que contém um detalhamento do processo de carga, arquivo de dados rejeitados (bad file) contendo os registros rejeitados durante o processo de carga de dados e o arquivo de dados descartados (discard file) que é criado apenas se for especificado no arquivo de controle e que registra os dados descartados do processo de carga por não atenderem os critérios de seleção dos dados.


O SQL*Loader, assim como diversos outros utilitários Oracle, está abaixo do $ORACLE_HOME/bin sendo identificado pelo nome sqlldr.

Fazendo uma simples chamada, sem nenhum parâmetro, podemos ver diversas opções/comandos de utilização.



[oracle@db2 ~]$ sqlldr

SQL*Loader: Release 12.1.0.2.0 - Production on Thu Mar 16 15:02:07 2017

Copyright (c) 1982, 2014, Oracle and/or its affiliates.  All rights reserved.


Usage: SQLLDR keyword=value [,keyword=value,...]

Valid Keywords:

    userid -- ORACLE username/password
   control -- control file name
       log -- log file name
       bad -- bad file name
      data -- data file name
   discard -- discard file name
discardmax -- number of discards to allow          (Default all)
      skip -- number of logical records to skip    (Default 0)
      load -- number of logical records to load    (Default all)
    errors -- number of errors to allow            (Default 50)
      rows -- number of rows in conventional path bind array or between direct path data saves
               (Default: Conventional path 64, Direct path all)
  bindsize -- size of conventional path bind array in bytes  (Default 256000)
    silent -- suppress messages during run (header,feedback,errors,discards,partitions)
    direct -- use direct path                      (Default FALSE)
   parfile -- parameter file: name of file that contains parameter specifications
  parallel -- do parallel load                     (Default FALSE)
      file -- file to allocate extents from
skip_unusable_indexes -- disallow/allow unusable indexes or index partitions  (Default FALSE)
skip_index_maintenance -- do not maintain indexes, mark affected indexes as unusable  (Default FALSE)
commit_discontinued -- commit loaded rows when load is discontinued  (Default FALSE)
  readsize -- size of read buffer                  (Default 1048576)
external_table -- use external table for load; NOT_USED, GENERATE_ONLY, EXECUTE
columnarrayrows -- number of rows for direct path column array  (Default 5000)
streamsize -- size of direct path stream buffer in bytes  (Default 256000)
multithreading -- use multithreading in direct path
 resumable -- enable or disable resumable for current session  (Default FALSE)
resumable_name -- text string to help identify resumable statement
resumable_timeout -- wait time (in seconds) for RESUMABLE  (Default 7200)
date_cache -- size (in entries) of date conversion cache  (Default 1000)
no_index_errors -- abort load on any index errors  (Default FALSE)
partition_memory -- direct path partition memory limit to start spilling (kb)  (Default 0)
     table -- Table for express mode load
date_format -- Date format for express mode load
timestamp_format -- Timestamp format for express mode load
terminated_by -- terminated by character for express mode load
enclosed_by -- enclosed by character for express mode load
optionally_enclosed_by -- optionally enclosed by character for express mode load
characterset -- characterset for express mode load
degree_of_parallelism -- degree of parallelism for express mode load and external table load
      trim -- trim type for express mode load and external table load
       csv -- csv format data files for express mode load
    nullif -- table level nullif clause for express mode load
field_names -- field names setting for first record of data files for express mode load
dnfs_enable -- option for enabling or disabling Direct NFS (dNFS) for input data files  (Default FALSE)
dnfs_readbuffers -- the number of Direct NFS (dNFS) read buffers  (Default 4)

PLEASE NOTE: Command-line parameters may be specified either by
position or by keywords.  An example of the former case is 'sqlldr
scott/tiger foo'; an example of the latter is 'sqlldr control=foo
userid=scott/tiger'.  One may specify parameters by position before
but not after parameters specified by keywords.  For example,
'sqlldr scott/tiger control=foo logfile=log' is allowed, but
'sqlldr scott/tiger control=foo log' is not, even though the
position of the parameter 'log' is correct.

Para Exemplificar alguns casos de uso do SQL*Loader foi criado a seguinte tabela:



SQL> create table produtos (
2  codigo   number,
3  nome     varchar2(100),
4  categoria varchar2(50),
5  data_inclusao date );

Table created.

Arquivo de dados que será utilizado em alguns exemplos:


[oracle@db2 ~]$ cat dados1.txt
1,Shampoo,Higiene,16-MAR-17
2,Creme Dental,Higiene,16-MAR-17
3,Detergente,Limpeza,16-MAR-17
4,Alvejante,Limpeza,16-MAR-17
5,Amaciante,Limpeza,16-MAR-17
6,Refrigerante,Bebida,16-MAR-17
7,Agua,Bebida,16-MAR-17
8,Cerveja,Bebida,16-MAR-17
9,Suco,Bebida,16-MAR-17
10,Whisky,Bebida,16-MAR-17

1.    Carregando os dados (“normal”):

Arquivo de controle:

[oracle@db2 ~]$ cat exemplo1.ctl
options (errors=9999999, rows=5)
load data
 characterset WE8ISO8859P1
 infile '/home/oracle/dados1.txt'
 badfile '/home/oracle/exemplo1.bad'
 discardfile '/home/oracle/exemplo1.dsc'
 into table produtos
 fields terminated by ","
( codigo, nome, categoria, data_inclusao )

ERRORS: Independente da quantidade de erros que ocorram, o processo de carga deve continuar, por isso o número alto. Caso nenhum erro seja permitido ajuste o valor para 0. O valor padrão deste parâmetro é 50;

ROWS: Frequência de commits, ou seja, no exemplo a cada 5 linhas inseridas irá ocorrer um commit;

CHARACTERSET: Character set utilizado;

INFILE: Nome do arquivo texto que contém os dados que serão importados baseado nas configurações do control file;

BADFILE: Arquivo texto que será criado dos os registros rejeitados;

DISCARDFILE: Arquivo texto que será criado com os registros descartados mediante as condições de carga;

FIELDS TERMINATED BY: É o delimitador das colunas, ou seja, sempre que o caractere definido for encontrado significa que uma nova coluna se inicia.

Executando o SQL*Loader para carregar os registros contidos no arquivo dados1.txt baseado nas configurações do arquivo de controle (control file).




[oracle@db2 ~]$ sqlldr anderson/anderson@pdb1 control=exemplo1.ctl

SQL*Loader: Release 12.1.0.2.0 - Production on Thu Mar 16 15:04:08 2017

Copyright (c) 1982, 2014, Oracle and/or its affiliates.  All rights reserved.

Path used:      Conventional
Commit point reached - logical record count 5
Commit point reached - logical record count 10

Table PRODUTOS:
  10 Rows successfully loaded.

Check the log file:
  exemplo1.log
for more information about the load.

Finalizada a execução, a saída do comando irá informar o nome do log gerado ( Check the log file: exemplo1.log ), nele existirá detalhes adicionais da carga.


[oracle@db2 ~]$ cat exemplo1.log

SQL*Loader: Release 12.1.0.2.0 - Production on Thu Mar 16 15:04:08 2017

Copyright (c) 1982, 2014, Oracle and/or its affiliates.  All rights reserved.

Control File:   exemplo1.ctl
Character Set WE8ISO8859P1 specified for all input.

Data File:      /home/oracle/dados1.txt
  Bad File:     /home/oracle/exemplo1.bad
  Discard File: /home/oracle/exemplo1.dsc
 (Allow all discards)

Number to load: ALL
Number to skip: 0
Errors allowed: 9999999
Bind array:     5 rows, maximum of 256000 bytes
Continuation:    none specified
Path used:      Conventional

Table PRODUTOS, loaded from every logical record.
Insert option in effect for this table: INSERT

   Column Name                  Position   Len  Term Encl Datatype
------------------------------ ---------- ----- ---- ---- ---------------------
CODIGO                              FIRST     *   ,       CHARACTER
NOME                                 NEXT     *   ,       CHARACTER
CATEGORIA                            NEXT     *   ,       CHARACTER
DATA_INCLUSAO                        NEXT     *   ,       CHARACTER


Table PRODUTOS:
  10 Rows successfully loaded.
  0 Rows not loaded due to data errors.
  0 Rows not loaded because all WHEN clauses were failed.
  0 Rows not loaded because all fields were null.


Space allocated for bind array:                   5160 bytes(5 rows)
Read   buffer bytes: 1048576

Total logical records skipped:          0
Total logical records read:            10
Total logical records rejected:         0
Total logical records discarded:        0

Run began on Thu Mar 16 15:04:08 2017
Run ended on Thu Mar 16 15:04:09 2017

Elapsed time was:     00:00:00.40
CPU time was:         00:00:00.01

Registros na tabela PRODUTOS do banco de dados:


SQL> set lines 200
SQL> col nome for a30
SQL> col categoria for a20
SQL> select * from produtos;

    CODIGO NOME                           CATEGORIA            DATA_INCL
---------- ------------------------------ -------------------- ---------
         1 Shampoo                        Higiene              16-MAR-17
         2 Creme Dental                   Higiene              16-MAR-17
         3 Detergente                     Limpeza              16-MAR-17
         4 Alvejante                      Limpeza              16-MAR-17
         5 Amaciante                      Limpeza              16-MAR-17
         6 Refrigerante                   Bebida               16-MAR-17
         7 Agua                           Bebida               16-MAR-17
         8 Cerveja                        Bebida               16-MAR-17
         9 Suco                           Bebida               16-MAR-17
        10 Whisky                         Bebida               16-MAR-17

10 rows selected.

2.    Inserindo dados adicionais:


Após a inserção dos registros do arquivo dados1.txt se tornou necessário inserir alguns registros adicionais, desta forma, o SQL*Loader foi novamente executado informando um segundo arquivo de dados chamado dados2.txt, contudo ocorreu ocorre um erro informando que a tabela PROTUDOS precisa estar vazia.


[oracle@db2 ~]$ sqlldr anderson/anderson@pdb1 control=exemplo2.ctl

SQL*Loader: Release 12.1.0.2.0 - Production on Thu Mar 16 15:20:37 2017

Copyright (c) 1982, 2014, Oracle and/or its affiliates.  All rights reserved.

Path used:      Conventional
SQL*Loader-601: For INSERT option, table must be empty.  Error on table PRODUTOS


Como o arquivo dados2.txt contém apenas registros adicionais, será utilizado o comando APPEND que instrui o SQL*Loader a "acrescentar" os registros do arquivo dados2 na tabela PRODUTOS.


[oracle@db2 ~]$ cat dados2.txt
11,Trigo,Cereal,16-MAR-17
12,Arroz,Cereal,16-MAR-17

Arquivo de controle contendo o comando APPEND:


[oracle@db2 ~]$ cat exemplo2.ctl
options (errors=9999999, rows=5)
load data
 characterset WE8ISO8859P1
 infile '/home/oracle/dados2.txt'
 badfile '/home/oracle/exemplo2.bad'
 discardfile '/home/oracle/exemplo2.dsc'
 append
 into table produtos
 fields terminated by ","
( codigo, nome, categoria, data_inclusao )


Execução do SQL*Loader para carregar os dados adicionais:



[oracle@db2 ~]$ sqlldr anderson/anderson@pdb1 control=exemplo2.ctl

SQL*Loader: Release 12.1.0.2.0 - Production on Thu Mar 16 15:28:12 2017

Copyright (c) 1982, 2014, Oracle and/or its affiliates.  All rights reserved.

Path used:      Conventional
Commit point reached - logical record count 2

Table PRODUTOS:
  2 Rows successfully loaded.

Check the log file:
  exemplo2.log
for more information about the load.

Verificando se os registros foram acrescentados a tabela com sucesso:


SQL> select * from produtos;

    CODIGO NOME                           CATEGORIA            DATA_INCL
---------- ------------------------------ -------------------- ---------
         1 Shampoo                        Higiene              16-MAR-17
         2 Creme Dental                   Higiene              16-MAR-17
         3 Detergente                     Limpeza              16-MAR-17
         4 Alvejante                      Limpeza              16-MAR-17
         5 Amaciante                      Limpeza              16-MAR-17
         6 Refrigerante                   Bebida               16-MAR-17
         7 Agua                           Bebida               16-MAR-17
         8 Cerveja                        Bebida               16-MAR-17
         9 Suco                           Bebida               16-MAR-17
        10 Whisky                         Bebida               16-MAR-17
        11 Trigo                          Cereal               16-MAR-17

    CODIGO NOME                           CATEGORIA            DATA_INCL
---------- ------------------------------ -------------------- ---------
        12 Arroz                          Cereal               16-MAR-17

12 rows selected.

Caso os novos registros tenham sido adicionados no mesmo arquivo de dados anterior (dados1.txt), utilize o parâmetro TRUNCATE em vez de APPEND, desta forma, a tabela será primeiramente truncada e posteriormente os dados inseridos.


[oracle@db2 ~]$ cat exemplo3.ctl
options (errors=9999999, rows=5)
load data
 characterset WE8ISO8859P1
 infile '/home/oracle/dados1.txt'
 badfile '/home/oracle/exemplo3.bad'
 discardfile '/home/oracle/exemplo3.dsc'
 truncate
 into table produtos
 fields terminated by ","
( codigo, nome, categoria, data_inclusao )

Execução do SQL*Loader com o novo arquivo de controle (control file):


[oracle@db2 ~]$ sqlldr anderson/anderson@pdb1 control=exemplo3.ctl

SQL*Loader: Release 12.1.0.2.0 - Production on Thu Mar 16 15:37:06 2017

Copyright (c) 1982, 2014, Oracle and/or its affiliates.  All rights reserved.

Path used:      Conventional
Commit point reached - logical record count 5
Commit point reached - logical record count 10
Commit point reached - logical record count 12

Table PRODUTOS:
  12 Rows successfully loaded.

Check the log file:
  exemplo3.log


3.    Diferentes delimitadores:


Em alguns cenários os delimitadores dos registros poderão ser diferentes, como no arquivo de dados abaixo:


[oracle@db2 ~]$ cat dados3.txt
1,Shampoo|Higiene^16-MAR-17
2,Creme Dental|Higiene^16-MAR-17
3,Detergente|Limpeza^16-MAR-17
4,Alvejante|Limpeza^16-MAR-17
5,Amaciante|Limpeza^16-MAR-17
6,Refrigerante|Bebida^16-MAR-17
7,Agua|Bebida^16-MAR-17
8,Cerveja|Bebida^16-MAR-17
9,Suco|Bebida^16-MAR-17
10,Whisky|Bebida^16-MAR-17
11,Trigo|Cereal^16-MAR-17
12,Arroz|Cereal^16-MAR-17

Para carregar estes dados, além do já configurado terminated by "," será adicionado após cada coluna no arquivo de controle um novo terminated by especificando o caractere necessário para quebrar cada coluna.


[oracle@db2 ~]$ cat exemplo4.ctl
options (errors=9999999, rows=5)
load data
 characterset WE8ISO8859P1
 infile '/home/oracle/dados3.txt'
 badfile '/home/oracle/exemplo4.bad'
 discardfile '/home/oracle/exemplo4.dsc'
 truncate
 into table produtos
 fields terminated by ","
( codigo, nome terminated by "|", categoria terminated by "^", data_inclusao )


Execução:

[oracle@db2 ~]$ sqlldr anderson/anderson@pdb1 control=exemplo4.ctl

Verificando os registros:

SQL> select * from produtos;

    CODIGO NOME                           CATEGORIA            DATA_INCL
---------- ------------------------------ -------------------- ---------
         1 Shampoo                        Higiene              16-MAR-17
         2 Creme Dental                   Higiene              16-MAR-17
         3 Detergente                     Limpeza              16-MAR-17
         4 Alvejante                      Limpeza              16-MAR-17
         5 Amaciante                      Limpeza              16-MAR-17
         6 Refrigerante                   Bebida               16-MAR-17
         7 Agua                           Bebida               16-MAR-17
         8 Cerveja                        Bebida               16-MAR-17
         9 Suco                           Bebida               16-MAR-17
        10 Whisky                         Bebida               16-MAR-17
        11 Trigo                          Cereal               16-MAR-17

    CODIGO NOME                           CATEGORIA            DATA_INCL
---------- ------------------------------ -------------------- ---------
        12 Arroz                          Cereal               16-MAR-17

12 rows selected.

4.    Formatando datatype DATE e definindo "tamanho fixo" para os dados:

Da mesma forma que um arquivo de dados pode ter diferentes delimitadores, também é possível que ele não tenha qualquer delimitador. Utilizando o comando "position(start:end)" é possível delimitar onde começa e onde termina cada coluna/registro.

Na coluna DATA_INCLUSAO, datatype DATE, também é permitido aplicar mascaras, transformando-a conforme necessidade.


Arquivo de dados:


[oracle@db2 ~]$ cat dados4.txt
1Shampoo1Higiene16/03/2017 16:18:00
2Shampoo2Higiene16/03/2017 16:18:00
3Shampoo3Higiene16/03/2017 16:18:00
4Shampoo4Higiene16/03/2017 16:18:00

Arquivo de controle contendo a máscara para a data_inclusao e com as posições de início:fim de cada coluna/registro:


[oracle@db2 ~]$ cat exemplo5.ctl
options (errors=9999999, rows=5)
load data
 characterset WE8ISO8859P1
 infile '/home/oracle/dados4.txt'
 badfile '/home/oracle/exemplo5.bad'
 discardfile '/home/oracle/exemplo5.dsc'
 truncate
 into table produtos
 fields terminated by ","
( codigo position(1:1), nome position(2:9), categoria position(10:16), data_inclusao position(17:35) "to_date(trim(:data_inclusao),'DD/MM/YYYY HH24:MI:SS')" )

Registros inseridos:

SQL> select * from produtos;

    CODIGO NOME                           CATEGORIA            DATA_INCL
---------- ------------------------------ -------------------- ---------
         1 Shampoo1                       Higiene              16-MAR-17
         2 Shampoo2                       Higiene              16-MAR-17
         3 Shampoo3                       Higiene              16-MAR-17
         4 Shampoo4                       Higiene              16-MAR-17

5.    Alterando os dados durante a carga:

Nem sempre os dados de entrada representam o formato ou nome que é necessário que eles tenham no banco de dados. Para tanto podemos usar funções SQL para “transformar” estes dados durante o processo de carga.


No exemplo será utilizado o arquivo de dados dados1.txt, onde cada código será incrementado em +10, o nome convertido para UPPERCASE (Maiúsculo) e a categoria além do UPPERCASE será substituída por DESCONHECIDA quando a categoria de entrada (arquivo de dados) for Limpeza.


[oracle@db2 ~]$ cat exemplo6.ctl
options (errors=9999999, rows=5)
load data
 characterset WE8ISO8859P1
 infile '/home/oracle/dados1.txt'
 badfile '/home/oracle/exemplo6.bad'
 discardfile '/home/oracle/exemplo6.dsc'
 truncate
 into table produtos
 fields terminated by ","
( codigo ":codigo+10", nome "upper(:nome)", categoria "upper(decode(:categoria,'Limpeza','Desconhecida', :categoria))", data_inclusao )

Verificando se os registros foram alterados durante a carga de dados conforme especificado:


SQL> select * from produtos;

    CODIGO NOME                           CATEGORIA            DATA_INCL
---------- ------------------------------ -------------------- ---------
        11 SHAMPOO                        HIGIENE              16-MAR-17
        12 CREME DENTAL                   HIGIENE              16-MAR-17
        13 DETERGENTE                     DESCONHECIDA         16-MAR-17
        14 ALVEJANTE                      DESCONHECIDA         16-MAR-17
        15 AMACIANTE                      DESCONHECIDA         16-MAR-17
        16 REFRIGERANTE                   BEBIDA               16-MAR-17
        17 AGUA                           BEBIDA               16-MAR-17
        18 CERVEJA                        BEBIDA               16-MAR-17
        19 SUCO                           BEBIDA               16-MAR-17
        20 WHISKY                         BEBIDA               16-MAR-17
        21 TRIGO                          CEREAL               16-MAR-17

    CODIGO NOME                           CATEGORIA            DATA_INCL
---------- ------------------------------ -------------------- ---------
        22 ARROZ                          CEREAL               16-MAR-17

12 rows selected.

6.    Carregando dados de forma seletiva:


Se nem todos os dados precisam ser carregados, basta inserir a clausula WHEN logo após o "into table" onde apenas os registros que satisfazerem a condição serão carregados para a tabela no banco de dados.


[oracle@db2 ~]$ cat exemplo7.ctl
options (errors=9999999, rows=5)
load data
 characterset WE8ISO8859P1
 infile '/home/oracle/dados1.txt'
 badfile '/home/oracle/exemplo7.bad'
 discardfile '/home/oracle/exemplo7.dsc'
 truncate
 into table produtos
 when categoria = 'Bebida'
 fields terminated by ","
( codigo, nome, categoria, data_inclusao )

Dados que foram inseridos na tabela PRODUTOS:

SQL> select * from produtos;

    CODIGO NOME                           CATEGORIA            DATA_INCL
---------- ------------------------------ -------------------- ---------
         6 Refrigerante                   Bebida               16-MAR-17
         7 Agua                           Bebida               16-MAR-17
         8 Cerveja                        Bebida               16-MAR-17
         9 Suco                           Bebida               16-MAR-17
        10 Whisky                         Bebida               16-MAR-17

Como existem dados descartados em função da condição WHEN, o arquivo discardfile foi criado contendo os registros que foram descartados no processo de carga por não atenderem a condição imposta.


[oracle@db2 ~]$ cat exemplo7.dsc
1,Shampoo,Higiene,16-MAR-17
2,Creme Dental,Higiene,16-MAR-17
3,Detergente,Limpeza,16-MAR-17
4,Alvejante,Limpeza,16-MAR-17
5,Amaciante,Limpeza,16-MAR-17
11,Trigo,Cereal,16-MAR-17
12,Arroz,Cereal,16-MAR-17

7.    Utilizando valores sequenciais:

Nos exemplos até aqui utilizados, sempre existiu no arquivo de dados o código do produto, mas e se não existe? Pois bem, lembra da flexibilidade do SQL*Loader supracitada?

SEQUENCE(MAX): Especificado após o nome da coluna no arquivo de controle (control file) faz com que o SQL*Loader gere de forma sequencial os valores para a determinada coluna.

Arquivo de dados criado sem os códigos:


[oracle@db2 ~]$ cat dados5.txt
Shampoo,Higiene,16-MAR-17
Creme Dental,Higiene,16-MAR-17
Detergente,Limpeza,16-MAR-17
Alvejante,Limpeza,16-MAR-17
Amaciante,Limpeza,16-MAR-17
Refrigerante,Bebida,16-MAR-17
Agua,Bebida,16-MAR-17
Cerveja,Bebida,16-MAR-17
Suco,Bebida,16-MAR-17
Whisky,Bebida,16-MAR-17
Trigo,Cereal,16-MAR-17
Arroz,Cereal,16-MAR-17

Arquivo de controle com o sequence(max) na coluna código:

[oracle@db2 ~]$ cat exemplo8.ctl
options (errors=9999999, rows=5)
load data
 characterset WE8ISO8859P1
 infile '/home/oracle/dados5.txt'
 badfile '/home/oracle/exemplo8.bad'
 discardfile '/home/oracle/exemplo8.dsc'
 truncate
 into table produtos
 fields terminated by ","
( codigo sequence(max), nome, categoria, data_inclusao )

Após a execução da carga, foram inseridos os seguintes registros na tabela PRODUTOS:


SQL> select * from produtos;

    CODIGO NOME                           CATEGORIA            DATA_INCL
---------- ------------------------------ -------------------- ---------
         1 Shampoo                        Higiene              16-MAR-17
         2 Creme Dental                   Higiene              16-MAR-17
         3 Detergente                     Limpeza              16-MAR-17
         4 Alvejante                      Limpeza              16-MAR-17
         5 Amaciante                      Limpeza              16-MAR-17
         6 Refrigerante                   Bebida               16-MAR-17
         7 Agua                           Bebida               16-MAR-17
         8 Cerveja                        Bebida               16-MAR-17
         9 Suco                           Bebida               16-MAR-17
        10 Whisky                         Bebida               16-MAR-17
        11 Trigo                          Cereal               16-MAR-17

    CODIGO NOME                           CATEGORIA            DATA_INCL
---------- ------------------------------ -------------------- ---------
        12 Arroz                          Cereal               16-MAR-17

12 rows selected.

Caso seja necessário carregar mais dados, não tem problema. Altere o arquivo de controle substituindo o comando TRUNCATE por APPEND. O sequence(max) irá continuar a sequência sem problemas.


[oracle@db2 ~]$ cat dados6.txt
Trigo,Cereal,16-MAR-17
Arroz,Cereal,16-MAR-17
[oracle@db2 ~]$ cat exemplo9.ctl
options (errors=9999999, rows=5)
load data
 characterset WE8ISO8859P1
 infile '/home/oracle/dados6.txt'
 badfile '/home/oracle/exemplo9.bad'
 discardfile '/home/oracle/exemplo9.dsc'
 append
 into table produtos
 fields terminated by ","
( codigo sequence(max), nome, categoria, data_inclusao )

Registros na tabela PRODUTOS após a carga de dados adicional:


SQL> select * from produtos;

    CODIGO NOME                           CATEGORIA            DATA_INCL
---------- ------------------------------ -------------------- ---------
        13 Trigo                          Cereal               16-MAR-17
        14 Arroz                          Cereal               16-MAR-17
         1 Shampoo                        Higiene              16-MAR-17
         2 Creme Dental                   Higiene              16-MAR-17
         3 Detergente                     Limpeza              16-MAR-17
         4 Alvejante                      Limpeza              16-MAR-17
         5 Amaciante                      Limpeza              16-MAR-17
         6 Refrigerante                   Bebida               16-MAR-17
         7 Agua                           Bebida               16-MAR-17
         8 Cerveja                        Bebida               16-MAR-17
         9 Suco                           Bebida               16-MAR-17

    CODIGO NOME                           CATEGORIA            DATA_INCL
---------- ------------------------------ -------------------- ---------
        10 Whisky                         Bebida               16-MAR-17
        11 Trigo                          Cereal               16-MAR-17
        12 Arroz                          Cereal               16-MAR-17

14 rows selected.

Além da opção nativa do SQL*Loader ainda é possível utilizar sequences criadas na própria base de dados, contudo a antiga coluna de código ainda deve existir no arquivo de dados, caso contrário será gerado erro.

Será utilizado o arquivo de dados - dados1.txt para a simulação.


SQL> create sequence seq_tst1 start with 5 increment by 5;

Sequence created.

Arquivo de controle especificando a sequência criada no banco de dados:

[oracle@db2 ~]$ cat exemplo10.ctl
options (errors=9999999, rows=5)
load data
 characterset WE8ISO8859P1
 infile '/home/oracle/dados1.txt'
 badfile '/home/oracle/exemplo10.bad'
 discardfile '/home/oracle/exemplo10.dsc'
 truncate
 into table produtos
 fields terminated by ","
( codigo "SEQ_TST1.nextval", nome, categoria, data_inclusao )

Realizada a carga, seguem os dados carregados:


SQL> select * from produtos;


    CODIGO NOME                           CATEGORIA            DATA_INCL
---------- ------------------------------ -------------------- ---------
         5 Shampoo                        Higiene              16-MAR-17
        10 Creme Dental                   Higiene              16-MAR-17
        15 Detergente                     Limpeza              16-MAR-17
        20 Alvejante                      Limpeza              16-MAR-17
        25 Amaciante                      Limpeza              16-MAR-17
        30 Refrigerante                   Bebida               16-MAR-17
        35 Agua                           Bebida               16-MAR-17
        40 Cerveja                        Bebida               16-MAR-17
        45 Suco                           Bebida               16-MAR-17
        50 Whisky                         Bebida               16-MAR-17
        55 Trigo                          Cereal               16-MAR-17

    CODIGO NOME                           CATEGORIA            DATA_INCL
---------- ------------------------------ -------------------- ---------
        60 Arroz                          Cereal               16-MAR-17

12 rows selected.

Outra opção para trabalhar com uma sequence da base de dados é via trigger, ou seja, a coluna código deve ser removida do arquivo de controle da mesma forma que no arquivo de dados também não é mais necessário.

Será utilizado o arquivo de dados - dados5.txt para a simulação, onde não existem valores "codigo".


Trigger criada na base de dados:


SQL> create trigger trg_seq_tst1 before insert on produtos
2 for each row
3 begin
4  select seq_tst1.nextval into :new.codigo from dual;
5 end;
6 /

Trigger created.

Arquivo de controle onde foi removido a coluna "codigo":

[oracle@db2 ~]$ cat exemplo11.ctl
options (errors=9999999, rows=5)
load data
 characterset WE8ISO8859P1
 infile '/home/oracle/dados5.txt'
 badfile '/home/oracle/exemplo11.bad'
 discardfile '/home/oracle/exemplo11.dsc'
 truncate
 into table produtos
 fields terminated by ","
( nome, categoria, data_inclusao )

Registros após a nova carga dos dados utilizando o arquivo de controle exemplo11.ctl:


SQL> select * from produtos;

    CODIGO NOME                           CATEGORIA            DATA_INCL
---------- ------------------------------ -------------------- ---------
        65 Shampoo                        Higiene              16-MAR-17
        70 Creme Dental                   Higiene              16-MAR-17
        75 Detergente                     Limpeza              16-MAR-17
        80 Alvejante                      Limpeza              16-MAR-17
        85 Amaciante                      Limpeza              16-MAR-17
        90 Refrigerante                   Bebida               16-MAR-17
        95 Agua                           Bebida               16-MAR-17
       100 Cerveja                        Bebida               16-MAR-17
       105 Suco                           Bebida               16-MAR-17
       110 Whisky                         Bebida               16-MAR-17
       115 Trigo                          Cereal               16-MAR-17

    CODIGO NOME                           CATEGORIA            DATA_INCL
---------- ------------------------------ -------------------- ---------
       120 Arroz                          Cereal               16-MAR-17

12 rows selected.

8.    Carregando dados de múltiplos arquivos de dados:


Para carregar dados de múltiplos arquivos, insira múltiplos infile dentro do arquivo de controle.


[oracle@db2 ~]$ cat exemplo12.ctl
options (errors=9999999, rows=5)
load data
 characterset WE8ISO8859P1
 infile '/home/oracle/dados1.txt'
 infile '/home/oracle/dados2.txt'
 badfile '/home/oracle/exemplo12.bad'
 discardfile '/home/oracle/exemplo12.dsc'
 truncate
 into table produtos
 fields terminated by ","
(  codigo, nome, categoria, data_inclusao )

Como no meu arquivo de dados dados1.txt atualmente existem registros duplicados com o dados2.txt, será criado uma primary key (PK) na tabela PRODUTOS sobre a coluna codigo de forma a não importar dados duplicados e gerar o arquivo badfile com os registros rejeitados.


SQL> alter table produtos add constraint pk_cod primary key(codigo);

Table altered.

Execução da carga:


[oracle@db2 ~]$ sqlldr anderson/anderson@pdb1 control=exemplo12.ctl

SQL*Loader: Release 12.1.0.2.0 - Production on Thu Mar 16 19:43:39 2017

Copyright (c) 1982, 2014, Oracle and/or its affiliates.  All rights reserved.

Path used:      Conventional
Commit point reached - logical record count 5
Commit point reached - logical record count 10
Commit point reached - logical record count 12
Commit point reached - logical record count 14

Table PRODUTOS:
  12 Rows successfully loaded.

Check the log file:
  exemplo12.log
for more information about the load.

Arquivos rejeitados (badfile) – O nome do caminho/arquivo é o configurado no arquivo de controle, neste caso /home/oracle/exemplo12.bad:


[oracle@db2 ~]$ cat exemplo12.bad
11,Trigo,Cereal,16-MAR-17
12,Arroz,Cereal,16-MAR-17

Erro gerado no arquivo de log que justificam os registros rejeitados:


[oracle@db2 ~]$ cat exemplo12.log | grep ORA-
ORA-00001: unique constraint (ANDERSON.PK_COD) violated
ORA-00001: unique constraint (ANDERSON.PK_COD) violated

Registros que foram inseridos:

SQL> select * from produtos;

    CODIGO NOME                           CATEGORIA            DATA_INCL
---------- ------------------------------ -------------------- ---------
         1 Shampoo                        Higiene              16-MAR-17
         2 Creme Dental                   Higiene              16-MAR-17
         3 Detergente                     Limpeza              16-MAR-17
         4 Alvejante                      Limpeza              16-MAR-17
         5 Amaciante                      Limpeza              16-MAR-17
         6 Refrigerante                   Bebida               16-MAR-17
         7 Agua                           Bebida               16-MAR-17
         8 Cerveja                        Bebida               16-MAR-17
         9 Suco                           Bebida               16-MAR-17
        10 Whisky                         Bebida               16-MAR-17
        11 Trigo                          Cereal               16-MAR-17

    CODIGO NOME                           CATEGORIA            DATA_INCL
---------- ------------------------------ -------------------- ---------
        12 Arroz                          Cereal               16-MAR-17

12 rows selected.

9.    Carregando dados em múltiplas tabelas:

Assim como para carregar múltiplos arquivos basta inserir outros infile no arquivo de controle, para carregar os dados em múltiplas tabelas basta inserir outros into table.


Neste exemplo será criado uma nova tabela chamada CATEGORIA onde teremos o código da categoria e seu respectivo nome.


SQL> create table categoria (
2  codigo  number,
3  nome   varchar2(100) );

Table created.

Na tabela PRODUTOS não existirá mais o nome da categoria, mas o código da categoria que fara referência a tabela CATEGORIA. Desta forma, o arquivo de controle foi ajustado para:



[oracle@db2 ~]$ cat exemplo13.ctl
options (errors=9999999, rows=5)
load data
 characterset WE8ISO8859P1
 infile '/home/oracle/dados1.txt'
 badfile '/home/oracle/exemplo13.bad'
 discardfile '/home/oracle/exemplo13.dsc'
 truncate
 into table produtos
 fields terminated by ","
( codigo, nome, categoria "decode(:categoria,'Higiene','1','Limpeza','2','Bebida','3','Cereal','4',:categoria)", data_inclusao )
 into table categoria
 fields terminated by ","
 trailing nullcols
( codigo "decode(:nome,'Higiene','1','Limpeza','2','Bebida','3','Cereal','4',:nome)", x filler position(1), y filler,  nome )

O decode na categoria foi inserido para transformar o "nome da categoria" em "códigos" para os mesmos grupos.


No arquivo de dados existem 4 colunas/registros para cada linha, diferentemente da tabela PRODUTOS a tabela CATEGORIA possui apenas 2 colunas e que não seguem a mesma sequência das colunas no arquivo de dados.


Para ler apenas a 3ª coluna de cada linha e popular corretamente a tabela CATEGORIA foi inserido o comando TRAILING NULLCOLS para que quando não houverem valores eles sejam nulos e foi criado colunas fictícias com o comando FILLER para simular as colunas do arquivo de dados.

Posterior a coluna código da CATEGORIA existe o FILLER x e y além da coluna nome. No arquivo de dados a coluna que contém os dados do nome da categoria é a 3ª e não a 4ª coluna, isto é necessário pois quando se trabalha com múltiplas tabelas e diferentes colunas de inserção o delimitador acaba se perdendo e para voltar ao início da linha foi atribuído junto ao comando FILLER da coluna x o comando position(1), por isso existem mais 3 colunas após a coluna categoria, justamente para simular as colunas fictícias desde o início de cada linha (null,null,CATEGORIA).

Executando a carga dos dados:


[oracle@db2 ~]$ sqlldr anderson/anderson@pdb1 control=exemplo13.ctl

SQL*Loader: Release 12.1.0.2.0 - Production on Fri Mar 17 14:33:33 2017

Copyright (c) 1982, 2014, Oracle and/or its affiliates.  All rights reserved.

Path used:      Conventional
Commit point reached - logical record count 5
Commit point reached - logical record count 10
Commit point reached - logical record count 12

Table PRODUTOS:
  12 Rows successfully loaded.

Table CATEGORIA:
  12 Rows successfully loaded.

Check the log file:
  exemplo13.log
for more information about the load.

Registros carregados na tabela PRODUTOS:


SQL> select * from produtos;

    CODIGO NOME                           CATEGORIA            DATA_INCL
---------- ------------------------------ -------------------- ---------
         1 Shampoo                        1                    16-MAR-17
         2 Creme Dental                   1                    16-MAR-17
         3 Detergente                     2                    16-MAR-17
         4 Alvejante                      2                    16-MAR-17
         5 Amaciante                      2                    16-MAR-17
         6 Refrigerante                   3                    16-MAR-17
         7 Agua                           3                    16-MAR-17
         8 Cerveja                        3                    16-MAR-17
         9 Suco                           3                    16-MAR-17
        10 Whisky                         3                    16-MAR-17
        11 Trigo                          4                    16-MAR-17

    CODIGO NOME                           CATEGORIA            DATA_INCL
---------- ------------------------------ -------------------- ---------
        12 Arroz                          4                    16-MAR-17

12 rows selected.

Pelo output (saída) da execução do SQL*Loader já é perceptível que ocorreu a inserção de 12 linhas também na tabela CATEGORIA, mas temos apenas 4 distintas categorias. Como não é possível trabalhar com distinct no SQL*Loader, basta agora remover os valores duplicados da tabela CATEGORIA.


SQL> delete from categoria A
2      where rowid > (select min(rowid)
3                       from categoria B
4                      where a.codigo = b.codigo
5                        and a.nome   = b.nome
6                     );

8 rows deleted.

SQL> commit;

Commit complete.

SQL> select * from categoria;

    CODIGO NOME
---------- ------------------------------
         1 Higiene
         2 Limpeza
         3 Bebida
         4 Cereal

Além das opções e comandos citados que já tornam o SQL*Loader uma poderosa ferramenta, ainda existem diversas outras parametrizações e comandos que podem ser empregados na sua configuração deixando-o ainda mais rápido (direct, parallel, etc) e versátil (skip, begindata, dnfs_enable, etc).


Referências:

https://docs.oracle.com/database/122/SUTIL/oracle-sql-loader-concepts.htm
https://docs.oracle.com/database/122/SUTIL/oracle-sql-loader-commands.htm
Mais informações →
Postagens mais antigas Página inicial

Translate

# Suporte

# ACE Program

#Oracle

#Oracle
Disclaimer: The views expressed on this blog are my own and do not necessarily reflect the views of Oracle.

#Blog reconhecido

#ARTICULISTA

Marcadores

Postagens populares