19. Februar 2016

"gzip" und "gunzip" - in der Datenbank: UTL_COMPRESS

Heute möchte ich auf ein PL/SQL Paket hinweisen, was es schon sehr, sehr lange in der Datenbank gibt, was aber ein wenig in Vergessenheit geraten ist: UTL_COMPRESS. Die Funktionen LZ_COMPRESS und LZ_UNCOMPRESS sind dabei die Datenbank-Äquivalente zu gzip und gunzip auf dem Betriebssystem.

Probiert das doch gleich einmal aus. Ihr könnt in eurer Datenbank sehr einfach an einen BLOB kommen, indem Ihr die XMLDB-Konfiguration abruft. Ihr bekommt ein XML-Dokument zurück. Das folgende Beispiel ruft zuerst als CLOB ab, dann als BLOB (UTL_COMPRESS arbeitet nur mit BLOBs).

select xmlserialize(document dbms_xdb.cfg_get() as clob) XML_CLOB from dual;

XML_CLOB
--------------------------------------------------------------------------------
<xdbconfig xmlns="http://xmlns.oracle.com/xdb/xdbconfig.xsd" xmlns:xsi="http://w
ww.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.oracle.com/x
db/xdbconfig.xsd http://xmlns.oracle.com/xdb/xdbconfig.xsd">
  <sysconfig>

select xmlserialize(document dbms_xdb.cfg_get() as blob) XML_BLOB from dual;

XML_BLOB
--------------------------------------------------------------------------------
3C786462636F6E66696720786D6C6E733D22687474703A2F2F786D6C6E732E6F7261636C652E636F
6D2F7864622F786462636F6E6669672E7873642220786D6C6E733A7873693D22687474703A2F2F77
77772E77332E6F72672F323030312F584D4C536368656D612D696E7374616E636522207873693A73
6368656D614C6F636174696F6E3D22687474703A2F2F786D6C6E732E6F7261636C652E636F6D2F78
64622F786462636F6E6669672E78736420687474703A2F2F786D6C6E732E6F7261636C652E636F6D

Die Größe des BLOBs (das brauchen wir gleich noch) ...

select dbms_lob.getlength(
  xmlserialize(document dbms_xdb.cfg_get() as blob)
) original_len from dual;

ORIGINAL_LEN
------------
       13513

Jetzt komprimieren wir mal ...

select utl_compress.lz_compress(
  xmlserialize(document dbms_xdb.cfg_get() as blob)
) from dual;

1F8B0800000000000003E55BDD73DB36127FCF5FA1E974EEE16E28CA76ECF3E554CE246D33D79BB4
75E36426F7E481C095841824680094E8FCF5B70049F143944DD282CF9DF38BC5FDC0FEB0BB582E41
709E850B2AE2255B4DB288C7EA87EFD65A276F7CDF5E4D852494C3948AC847417F273CCD54F85DAE
F126536CA7B5DD6EA7DB33545BF9A7B3D989FFE5D70FD7740D11F158AC348929A096626F94257E10
946826E2014627FDE105AF2693B9BA5739C95CE135CA7B11C93CB282E0E47CEED7AF2B094A109FA7

select dbms_lob.getlength(
  utl_compress.lz_compress(
    xmlserialize(document dbms_xdb.cfg_get() as blob)
  )
) zipped_len from dual;

ZIPPED_LEN
----------
      1874

Nun schreiben wir den komprimierten BLOB ins Filesystem - dazu könnt Ihr bspw. mein Package für das Filesystem und Betriebsystem-Kommandos hernehmen. Auf Unix-Ebene kann die Datei dann mit gunzip ausgepackt werden.

select file_pkg.get_file('/home/oracle/xdbconfig.xml.gz').write_to_file(
  utl_compress.lz_compress(
    xmlserialize(document dbms_xdb.cfg_get() as blob)
  )
) bytes_written from dual;

BYTES_WRITTEN
-------------
         1874

Auf Unix-Ebene kann die Datei dann mit gunzip ausgepackt werden. Man sieht dann die XML-Inhalte des urspränglichen BLOBs ...

$ gunzip xdbconfig.xml.gz
$ more xdbconfig.xml
<xdbconfig xmlns="http://xmlns.oracle.com/xdb/xdbconfig.xsd" xmlns:xsi="http://w
ww.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.oracle.com/x
db/xdbconfig.xsd http://xmlns.oracle.com/xdb/xdbconfig.xsd">
  <sysconfig>
  :

Und der umgekehrte Weg funktioniert genauso. Insbesondere wenn es darum geht, BLOBs oder CLOBs über das Netzwerk zum Datenbankserver zu übertragen, kann dieses Paket eine wertvolle Hilfe sein. Wenn man LOB-Inhalte vor dem Absenden an die Datenbank mit gzip verpackt und in der Datenbank mit UTL_COMPRESS.LZ_UMCOMPRESS wieder auspackt, so lässt sich die meist kostbare Netzwerk-Bandbreite gleich viel besser ausnutzen.

Auch APEX-Entwickler sollten sich UTL_COMPRESS ansehen, sobald sie mit Datei-Uploads arbeiten. Zwar ist APEX seit der Version 5.0 in der Lage, ZIP-Archive zu verarbeiten, mit GZIP-Dateien kann APEX_ZIP aber nicht umgehen. Genau hier kann man sehr schön mit dem UTL_COMPRESS Paket arbeiten. Es ist schnell eingesetzt und kann in manchen Situationen ein echter Quick Win sein. Schaut's euch an.

This blog posting will be about a PL/SQL package which is part of the database for a very long time, but which is also forgotten by most Oracle users: UTL_COMPRESS. Its funktions LZ_COMPRESS and LZ_UNCOMPRESS are the equivalents to the OS utilities gzip and gunzip.

It's very easy to try these functions out. First, you need a BLOB to compress. If you don't have some BLOB values ready in a table, you can easily generate one by retrieving the XML configuration of your database. This is actually an XML document.

select xmlserialize(document dbms_xdb.cfg_get() as clob) XML_CLOB from dual;

XML_CLOB
--------------------------------------------------------------------------------
<xdbconfig xmlns="http://xmlns.oracle.com/xdb/xdbconfig.xsd" xmlns:xsi="http://w
ww.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.oracle.com/x
db/xdbconfig.xsd http://xmlns.oracle.com/xdb/xdbconfig.xsd">
  <sysconfig>

select xmlserialize(document dbms_xdb.cfg_get() as blob) XML_BLOB from dual;

XML_BLOB
--------------------------------------------------------------------------------
3C786462636F6E66696720786D6C6E733D22687474703A2F2F786D6C6E732E6F7261636C652E636F
6D2F7864622F786462636F6E6669672E7873642220786D6C6E733A7873693D22687474703A2F2F77
77772E77332E6F72672F323030312F584D4C536368656D612D696E7374616E636522207873693A73
6368656D614C6F636174696F6E3D22687474703A2F2F786D6C6E732E6F7261636C652E636F6D2F78
64622F786462636F6E6669672E78736420687474703A2F2F786D6C6E732E6F7261636C652E636F6D

Determine the BLOB size (we'll need this later on) ...

select dbms_lob.getlength(
  xmlserialize(document dbms_xdb.cfg_get() as blob)
) original_len from dual;

ORIGINAL_LEN
------------
       13513

OK - let's compress.

select utl_compress.lz_compress(
  xmlserialize(document dbms_xdb.cfg_get() as blob)
) from dual;

1F8B0800000000000003E55BDD73DB36127FCF5FA1E974EEE16E28CA76ECF3E554CE246D33D79BB4
75E36426F7E481C095841824680094E8FCF5B70049F143944DD282CF9DF38BC5FDC0FEB0BB582E41
709E850B2AE2255B4DB288C7EA87EFD65A276F7CDF5E4D852494C3948AC847417F273CCD54F85DAE
F126536CA7B5DD6EA7DB33545BF9A7B3D989FFE5D70FD7740D11F158AC348929A096626F94257E10
946826E2014627FDE105AF2693B9BA5739C95CE135CA7B11C93CB282E0E47CEED7AF2B094A109FA7

select dbms_lob.getlength(
  utl_compress.lz_compress(
    xmlserialize(document dbms_xdb.cfg_get() as blob)
  )
) zipped_len from dual;

ZIPPED_LEN
----------
      1874

Then we'll write the compressed BLOB to the file system - you might use my Package for file system and OS commands for this, but working with UTL_FILE or similar is also OK.

select file_pkg.get_file('/home/oracle/xdbconfig.xml.gz').write_to_file(
  utl_compress.lz_compress(
    xmlserialize(document dbms_xdb.cfg_get() as blob)
  )
) bytes_written from dual;

BYTES_WRITTEN
-------------
         1874

Now you can inflate the file with the gunzip utility - and having done this, you can see the original XML contents.

$ gunzip xdbconfig.xml.gz
$ more xdbconfig.xml
<xdbconfig xmlns="http://xmlns.oracle.com/xdb/xdbconfig.xsd" xmlns:xsi="http://w
ww.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.oracle.com/x
db/xdbconfig.xsd http://xmlns.oracle.com/xdb/xdbconfig.xsd">
  <sysconfig>
  :

The other way around does, of course, also work. So you can upload a gzip-compressed LOB to your database and before storing it into your table, you run the UTL_COMPRESS.LZ_UNCOMPRESS function on it. This might be a very nice approach to better utilize the network bandwith to your database.

UTL_COMPRESS is also interesting for APEX developers - when building APEX applications, you sometimes have to build file uploads. APEX can deal with ZIP archives since the APEX_ZIP package has been introduced with APEX 5.0. But APEX_ZIP cannot handle GZIP files - to process these, UTL_COMPRESS comes to your rescue. I think, the UTL_COMPRESS package is a real quick win, using it is extemely easy and you get an immediate effect.

Beliebte Postings