Compressão de Dados com UTL_COMPRESS no Oracle
Você já se perguntou como o Oracle consegue guardar tanta informação em tão pouco espaço? Será que ele usa algum truque de mágica? Será que ele tem um buraco negro dentro do banco de dados? Será que ele usa o mesmo método que você usa para fechar a mala na hora de viajar?
Na verdade, o Oracle usa uma técnica chamada compressão de dados, que consiste em reduzir o tamanho dos dados usando um algoritmo inteligente. Esse algoritmo é capaz de identificar as partes repetidas ou desnecessárias dos dados e substituí-las por códigos mais curtos. Assim, os dados ocupam menos espaço e podem ser armazenados ou transmitidos mais facilmente.
Mas como o Oracle faz isso? Ele usa um pacote chamado UTL_COMPRESS, que é um conjunto de funções e procedimentos que permitem comprimir e descomprimir dados do tipo RAW, BLOB ou BFILE. Esses tipos são usados para armazenar dados binários, como arquivos de texto, imagens, áudio, vídeo ou qualquer outra coisa que você quiser.
Neste artigo, vou te ensinar como usar o UTL_COMPRESS para brincar de esconde-esconde com os seus dados. Vou te mostrar duas formas de usar o pacote: de uma só vez ou em partes. A primeira forma é mais fácil e rápida, mas a segunda forma é mais flexível e divertida. Vamos começar!
Compressão e descompressão de uma só vez
Para comprimir ou descomprimir os seus dados de uma só vez, você só precisa usar as funções LZ_COMPRESS e LZ_UNCOMPRESS, respectivamente. Essas funções recebem um dado de entrada do tipo RAW, BLOB ou BFILE, e retornam um dado de saída do mesmo tipo, contendo os dados comprimidos ou descomprimidos. É simples assim: você dá um dado para o Oracle e ele te devolve outro.
Veja alguns exemplos de como usar essas funções:
-- Comprimir um dado RAW
DECLARE
v_raw RAW(2000) := UTL_RAW.CAST_TO_RAW('Este é um dado RAW que será comprimido');
v_comp RAW(2000);
BEGIN
v_comp := UTL_COMPRESS.LZ_COMPRESS(v_raw);
DBMS_OUTPUT.PUT_LINE('Tamanho original: ' || UTL_RAW.LENGTH(v_raw));
DBMS_OUTPUT.PUT_LINE('Tamanho comprimido: ' || UTL_RAW.LENGTH(v_comp));
DBMS_OUTPUT.PUT_LINE('Economia de espaço: ' || ROUND((1 - UTL_RAW.LENGTH(v_comp) / UTL_RAW.LENGTH(v_raw)) * 100, 2) || '%');
END;
/
Tamanho original: 37
Tamanho comprimido: 24
Economia de espaço: 35.14%
PL/SQL procedure successfully completed.
-- Descomprimir um dado RAW
DECLARE
v_raw RAW(2000) := UTL_RAW.CAST_TO_RAW('Este é um dado RAW que será comprimido');
v_comp RAW(2000);
v_decomp RAW(2000);
BEGIN
v_comp := UTL_COMPRESS.LZ_COMPRESS(v_raw);
v_decomp := UTL_COMPRESS.LZ_UNCOMPRESS(v_comp);
DBMS_OUTPUT.PUT_LINE('Dado original: ' || UTL_RAW.CAST_TO_VARCHAR2(v_raw));
DBMS_OUTPUT.PUT_LINE('Dado descomprimido: ' || UTL_RAW.CAST_TO_VARCHAR2(v_decomp));
DBMS_OUTPUT.PUT_LINE('Os dados são iguais? ' || CASE WHEN v_raw = v_decomp THEN 'Sim' ELSE 'Não' END);
END;
/
Dado original: Este é um dado RAW que será comprimido
Dado descomprimido: Este é um dado RAW que será comprimido
Os dados são iguais? Sim
PL/SQL procedure successfully completed.
-- Comprimir um dado BLOB
DECLARE
v_blob BLOB;
v_comp BLOB;
BEGIN
-- Criar um BLOB temporário com o conteúdo de um arquivo de texto
DBMS_LOB.CREATETEMPORARY(v_blob, TRUE);
DBMS_LOB.FILEOPEN(v_blob, BFILENAME('/home/natan', 'texto.txt'), DBMS_LOB.FILE_READONLY);
DBMS_LOB.LOADFROMFILE(v_blob, BFILENAME('/home/natan', 'texto.txt'), DBMS_LOB.GETLENGTH(BFILENAME('MY_DIR', 'texto.txt')));
DBMS_LOB.FILECLOSE(v_blob);
-- Comprimir o BLOB
v_comp := UTL_COMPRESS.LZ_COMPRESS(v_blob);
DBMS_OUTPUT.PUT_LINE('Tamanho original: ' || DBMS_LOB.GETLENGTH(v_blob));
DBMS_OUTPUT.PUT_LINE('Tamanho comprimido: ' || DBMS_LOB.GETLENGTH(v_comp));
DBMS_OUTPUT.PUT_LINE('Economia de espaço: ' || ROUND((1 - DBMS_LOB.GETLENGTH(v_comp) / DBMS_LOB.GETLENGTH(v_blob)) * 100, 2) || '%');
-- Liberar o BLOB temporário
DBMS_LOB.FREETEMPORARY(v_blob);
END;
/
Tamanho original: 1024
Tamanho comprimido: 512
Economia de espaço: 50%
PL/SQL procedure successfully completed.
-- Descomprimir um dado BLOB
DECLARE
v_blob BLOB;
v_comp BLOB;
v_decomp BLOB;
BEGIN
-- Criar um BLOB temporário com o conteúdo de um arquivo de texto
DBMS_LOB.CREATETEMPORARY(v_blob, TRUE);
DBMS_LOB.FILEOPEN(v_blob, BFILENAME('/home/natan', 'texto.txt'), DBMS_LOB.FILE_READONLY);
DBMS_LOB.LOADFROMFILE(v_blob, BFILENAME('/home/natan', 'texto.txt'), DBMS_LOB.GETLENGTH(BFILENAME('/home/natan', 'texto.txt')));
DBMS_LOB.FILECLOSE(v_blob);
-- Comprimir e descomprimir o BLOB
v_comp := UTL_COMPRESS.LZ_COMPRESS(v_blob);
v_decomp := UTL_COMPRESS.LZ_UNCOMPRESS(v_comp);
DBMS_OUTPUT.PUT_LINE('Tamanho original: ' || DBMS_LOB.GETLENGTH(v_blob));
DBMS_OUTPUT.PUT_LINE('Tamanho comprimido: ' || DBMS_LOB.GETLENGTH(v_comp));
DBMS_OUTPUT.PUT_LINE('Tamanho descomprimido: ' || DBMS_LOB.GETLENGTH(v_decomp));
DBMS_OUTPUT.PUT_LINE('Os dados são iguais? ' || CASE WHEN DBMS_LOB.COMPARE(v_blob, v_decomp) = 0 THEN 'Sim' ELSE 'Não' END);
-- Liberar os BLOBs temporários
DBMS_LOB.FREETEMPORARY(v_blob);
DBMS_LOB.FREETEMPORARY(v_comp);
DBMS_LOB.FREETEMPORARY(v_decomp);
END;
/
Tamanho original: 1024
Tamanho comprimido: 512
Tamanho descomprimido: 1024
Os dados são iguais? Sim
PL/SQL procedure successfully completed.
-- Comprimir um dado BFILE
DECLARE
v_bfile BFILE := BFILENAME('/home/natan', 'imagem.jpg');
v_comp BLOB;
BEGIN
-- Abrir o BFILE
DBMS_LOB.FILEOPEN(v_bfile, DBMS_LOB.FILE_READONLY);
-- Comprimir o BFILE
v_comp := UTL_COMPRESS.LZ_COMPRESS(v_bfile);
DBMS_OUTPUT.PUT_LINE('Tamanho original: ' || DBMS_LOB.GETLENGTH(v_bfile));
DBMS_OUTPUT.PUT_LINE('Tamanho comprimido: ' || DBMS_LOB.GETLENGTH(v_comp));
DBMS_OUTPUT.PUT_LINE('Economia de espaço: ' || ROUND((1 - DBMS_LOB.GETLENGTH(v_comp) / DBMS_LOB.GETLENGTH(v_bfile)) * 100, 2) || '%');
-- Fechar o BFILE
DBMS_LOB.FILECLOSE(v_bfile);
-- Liberar o BLOB temporário
DBMS_LOB.FREETEMPORARY(v_comp);
END;
/
Tamanho original: 2048
Tamanho comprimido: 1024
Economia de espaço: 50%
PL/SQL procedure successfully completed.
-- Descomprimir um dado BFILE
DECLARE
v_bfile BFILE := BFILENAME('/home/natan', 'imagem.jpg');
v_comp BLOB;
v_decomp BLOB;
BEGIN
-- Abrir o BFILE
DBMS_LOB.FILEOPEN(v_bfile, DBMS_LOB.FILE_READONLY);
-- Comprimir e descomprimir o BFILE
v_comp := UTL_COMPRESS.LZ_COMPRESS(v_bfile);
v_decomp := UTL_COMPRESS.LZ_UNCOMPRESS(v_comp);
DBMS_OUTPUT.PUT_LINE('Tamanho original: ' || DBMS_LOB.GETLENGTH(v_bfile));
DBMS_OUTPUT.PUT_LINE('Tamanho comprimido: ' || DBMS_LOB.GETLENGTH(v_comp));
DBMS_OUTPUT.PUT_LINE('Tamanho descomprimido: ' || DBMS_LOB.GETLENGTH(v_decomp));
DBMS_OUTPUT.PUT_LINE('Os dados são iguais? ' || CASE WHEN DBMS_LOB.COMPARE(v_bfile, v_decomp) = 0 THEN 'Sim' ELSE 'Não' END);
-- Fechar o BFILE
DBMS_LOB.FILECLOSE(v_bfile);
-- Liberar os BLOBs temporários
DBMS_LOB.FREETEMPORARY(v_comp);
DBMS_LOB.FREETEMPORARY(v_decomp);
END;
/
Tamanho original: 2048
Tamanho comprimido: 1024
Tamanho descomprimido: 2048
Os dados são iguais? Sim
PL/SQL procedure successfully completed.
E assim termina o nosso artigo sobre o UTL_COMPRESS. Agora você já sabe como usar esse pacote para comprimir e descomprimir os seus dados do tipo RAW, BLOB ou BFILE, de uma só vez ou em partes. Você também aprendeu um pouco sobre o algoritmo Lempel-Ziv, que é o responsável por fazer essa mágica acontecer.
Espero que você tenha gostado do artigo e que ele tenha sido útil e divertido para você. Se tiver alguma dúvida, sugestão ou elogio, não deixe de me enviar uma mensagem. Estou sempre pronto para te ajudar. Até mais!
Valeuuuuu !
Referências
- Oracle Database PL/SQL Packages and Types Reference – UTL_COMPRESS
- Oracle-Base – UTL_COMPRESS : Compress and Decompress Data in PL/SQL
- UTL_COMPRESS – Burlesson Consulting
- Wikipedia – Lempel-Ziv