Oracle: Update de milhões de registros

Tempo de leitura: menos de 1 minuto

Update de dadosNesse último mês, andei trabalhando bastante com SQL, melhor dizendo PL/SQL. Basicamente minha tarefa era migração de dados, especificamente, alteração da PK de uma tabela que é referenciada por várias outras tabelas. No entanto, precisava fazer isso com a melhor performance possível.

Como não sou um DBA, faço apenas o “feijão com arroz”, estudei as formas de update que o Oracle disponibiliza.

Duas delas me chamaram bastante atenção, pois são as mais usadas pelos DBAs, são elas Create Table as Select e Merge Update.

Claro, essas são as mais indicadas, pois não uso particionamento de tabelas.

Create Table as Select

A primeira alternativa, Create Table as Select, consiste em criar uma cópia da tabela onde será feito o update. Essa estratégia possibilita realizar a alteração na projeção da query.

Vantagens:

  • Rápido, o tempo é pouco maior que o retorno da query;
  • Em uma única instrução é possível alterar os dados de quantas colunas forem necessárias, inclusive adicionar ou excluir colunas;
  • Comando simples de ler e entender. A complexidade fica na query que retornará os dados;
  • Não utiliza a tablespace de undo (tablespace usada no rollback).

Desvantagens:

  • Duplicidade de dados (tabela);
  • Pode estourar a tablespace de dados, portanto é necessário certificar-se que existe espaço suficiente para criação da tabela;
  • Procedimentos manuais que devem ser executados pós cópia:
    • Recriar as constraints e index na nova tabela, pois essa estratégia só leva os dados;
    • Recriar constraints (FKs) de tabelas que referenciam a tabela “copiada”;
    • “Dropar” a tabela original, se necessário;
    • Em alguns bancos, onde o recurso de lixeira está habilitado, deve-se expurgar a tabela dropada, caso contrário o espaço não é liberado.

Exemplo de uso:

Abaixo segue script para criar as tabelas e incluir os registros. Dado que existam as tabelas :

  • NF com as colunas ID (é a PK da tabela) e NUMERO (Observação: 1000 registros);

    [code language=”sql”]
    CREATE TABLE NF (
    ID NUMBER(9) NOT NULL,
    NUMERO NUMBER(9) NOT NULL,
    CONSTRAINT PK_NF PRIMARY KEY(ID) USING INDEX TABLESPACE &&table_space_name_index
    ) TABLESPACE &&table_space_name;

    DECLARE
    C NUMBER;
    BEGIN
    FOR C IN 1.. 1000 LOOP
    INSERT INTO NF VALUES (C, C+1000);
    END LOOP;
    COMMIT;
    END;
    /
    [/code]

  • NF_ITEM com as colunas ID (é a PK da tabela), DESCRICAO, ID_NF (Observação: 1 milhão de registros).

    [code language=”sql”]
    CREATE TABLE NF_ITEM (
    ID NUMBER(9) NOT NULL,
    DESCRICAO VARCHAR2(100 CHAR),
    ID_NF NUMBER(9) NOT NULL,
    CONSTRAINT PK_NF_ITEM PRIMARY KEY(ID) USING INDEX TABLESPACE &&table_space_name_index
    ) TABLESPACE &&table_space_name;

    DECLARE
    C NUMBER;
    BEGIN
    FOR C IN 1.. 1000000 LOOP
    INSERT INTO NF_ITEM VALUES (C, ‘DESCRIÇÃO ‘ || C, FLOOR((C/1000))+1);
    END LOOP;
    COMMIT;
    END;
    /
    [/code]

Problema:

Preciso alterar todos os registros de NF_ITEM fazendo com que o ID seja composto pelo NF.NUMERO + “|#|” + NF_ITEM.ID.

A query necessária para fazer essa migração usando Create Table as Select é a seguinte:

[code language=”sql”]
CREATE TABLE NF_ITEM_COPY TABLESPACE &&table_space_name AS (
SELECT
I.ID AS ID_OLD, –remoneio o campo ID para ID_OLD
NF.NUMERO || ‘|#|’ || I.ID AS ID, — crio uma nova coluna para ID, concatenando os campos necessários
I.DESCRICAO,
I.ID_NF
FROM NF_ITEM I
JOIN NF ON NF.ID = I.ID_NF
);
[/code]

No exemplo acima, criamos uma nova tabela chamada “NF_ITEM_COPY”. Além disso, alteramos o nome da coluna ID para ID_OLD, e criamos uma nova coluna ID concatenando os campos NF.NUMERO e NF_ITEM.ID, separando-os por “|#|”. O tempo de execução desse script foi:

tempo "create table as select" foi 0,83 segundos
Rápido né?

Merge Update

A segunda alternativa, Merge Update, consiste em executar um super update em lote. Essa estratégia não possibilita realizar a alteração na projeção como a anterior.

Vantagens:

  • Não estoura tablespace de dados;
  • Não há necessidade de recriar constrains;
  • Não há necessidade de recriar todos os índices;

Desvantagens:

  • Queries mais complexas;
  • Mais lento que o “Create Table as select“;
  • Não possui paginação de dados, sendo assim, pode estourar a tablespace UNDO;
  • Não é possível criar ou excluir colunas da tabela que sofrerá o update.

Exemplo de uso:

Partindo do princípio que iremos fazer a mesma alteração do exemplo anterior, nosso código ficará assim:

[code language=”sql”]
MERGE INTO NF_ITEM I
USING NF ON (NF.ID = I.ID_NF)
WHEN MATCHED THEN
UPDATE SET ID = NF.NUMERO || ‘|#|’ || I.ID;
[/code]

No exemplo acima, alteramos os valor do campo ID da mesma forma como fizemos com o Create Table as select, só que desta vez não criamos uma nova tabela. O tempo de execução deste script foi bem maior que o anterior, no entato, é mais rápido do que fazer update registro a registro:

tempo "merge update"

Valeu pessoal, abaixo deixo a fonte, na qual é muito boa a comparação que o autor faz:

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *