código·java·oracle
Oracle y SQL

XML en la base de datos Oracle: XMLType y XMLTABLE

Guardar XML en un CLOB es enterrarlo; tratarlo como tipo nativo permite consultarlo con SQL. XMLType, XMLTABLE y el delicado tránsito hacia JSON.

publicado octubre de 2011 revisado agosto de 2026 644 palabras

Hubo una época —digamos, la de los servicios SOAP— en que XML lo era todo: facturas electrónicas, mensajería entre bancos, configuración. Oracle apostó fuerte por aquellos años y convirtió XML en un ciudadano de primera clase de la base de datos con el tipo XMLType, able de almacenarse, indexarse y consultarse con SQL. Los formatos han girado hacia JSON, pero el XML no se ha ido: la factura electrónica de medio mundo sigue siendo XML firmado, y las bases heredadas están llenas de él. Saber interrogarlo sigue pagando la factura.

Columnas XMLType, no CLOB

La decisión estructural: guardar el documento en un CLOB o en una columna XMLType. En CLOB, el documento es una caja cerrada que solo puedes abrir fuera de la base. Con XMLType, el contenido es consultable:

SQLxmltype.sql
CREATE TABLE pedidos (
  id        NUMBER PRIMARY KEY,
  recibido  DATE DEFAULT SYSDATE,
  documento XMLType
) XMLTYPE COLUMN documento
  STORE AS BINARY XML;

INSERT INTO pedidos (id, documento) VALUES (1, XMLType(
  '<pedido><cliente>ACME</cliente>
             <linea sku="A-1" cant="2" precio="80"/>
             <linea sku="B-7" cant="1" precio="24"/>
   </pedido>'));

BINARY XML (11g+) es el almacenamiento recomendado: XML comprimido y post-parseado, más compacto y rápido de consultar que el CLOB clásico. La diferencia práctica frente a CLOB es abismal: sobre el XML binario se pueden crear índices estructurados; sobre texto plano, cualquier XPath es un escaneo con parseo por fila.

XMLTABLE: el puente hacia SQL relacional

La función XMLTABLE proyecta nodos del documento como si fueran columnas de una tabla. Es el corazón práctico del XML en SQL:

SQLxmltable.sql
SELECT p.id,
       x.cliente,
       x.sku,
       x.cantidad,
       x.precio
FROM   pedidos p,
       XMLTABLE('/pedido' PASSING p.documento
         COLUMNS cliente  VARCHAR2(30) PATH 'cliente',
                 sku      VARCHAR2(10) PATH 'linea/@sku',
                 cantidad NUMBER        PATH 'linea/@cant',
                 precio   NUMBER(10,2)  PATH 'linea/@precio') x;

La cláusula PASSING entrega el documento y COLUMNS mapea rutas XPath a columnas: los @ acceden a atributos. Nótese que el join lateral contra pedidos expande cada documento en tantas filas como líneas tenga —basta con variar la ruta base a /pedido/linea para iterar por líneas directamente.

Extraer valores sueltos: EXTRACTVALUE y sus herederos

SQLxmlquery.sql
SELECT XMLQuery('/pedido/cliente/text()'
                PASSING documento RETURNING CONTENT) AS cliente
FROM   pedidos WHERE id = 1;

SELECT XMLExists('/pedido/linea[@sku = "A-1"]'
                 PASSING documento) AS tiene_a1
FROM   pedidos WHERE id = 1;

XMLQuery devuelve fragmentos y XMLExists actúa como predicado —su equivalente antiguo, EXTRACTVALUE, sigue apareciendo en código heredado y solo soporta un nodo escalar por llamada. Para la salida en la otra dirección, generar XML desde SQL, existe XMLELEMENT/XMLAGG, que componen documentos fila a fila; la combinación con MERGE permite sincronizar tablas relacionales con documentos recibidos.

Índices: la diferencia entre segundos y horas

Sin índice, cada XMLExists examina todos los documentos. Con documentos grandes y consultas frecuentes por un nodo concreto, el índice XML:

SQLxmlindex.sql
CREATE INDEX ix_pedidos_sku ON pedidos (documento)
  INDEXTYPE IS XDB.BINARY XML
  XMLINDEX XMLTABLE '/pedido/linea'
    COLUMNS sku VARCHAR2(10) PATH '@sku';

La sintaxis exacta varía entre versiones —en 12c el XMLIndex admite tablas de índice estructuradas y mantenimiento asíncrono—, pero la idea es constante: el índice materializa los XPath calientes y las consultas dejan de parsear.

¿Y JSON? La historia se repite

El giro del ecosistema hacia JSON no borró el patrón, solo la sintaxis: JSON_TABLE es a los documentos JSON lo que XMLTABLE era al XML, con la misma lógica de columnas mapeadas por ruta y el mismo debate CLOB contra nativo. Las bases modernas tratan ambos formatos como tipos consultables, y conviven sin drama: la factura sigue llegando en XML firmado mientras la aplicación interna habla JSON.

  • Para volcar documentos legibles desde SQL*Plus, un SET LONG 100000 y LONGCHUNKSIZE decentes evitan el clásico corte a 80 caracteres: detalles en el comando SET.
  • El tamaño real de los documentos almacenados se vigila con USER_SEGMENTS, entre las consultas útiles del diccionario.

XML en la base de datos dejó de ser noticia, pero sigue siendo infraestructura: saber extraer datos de él con una sola sentencia convierte integraciones viejas en trabajo de minutos.