Búsquedas fuzzy, índices context y gestión documental con Oracle. Parte II.

(Continua a partir de parte I).

Recientemente he estado hablando con gente interesada en la gestión de contenidos de documentos y, en general, la mayoría utiliza formatos PDF. El anterior artículo se basaba únicamente en documentos word, y he creído interesante hacer un ejemplo de como Intermedia realiza la indexación de documentos word y PDF de forma totalmente transparente.

Algo importante a mencionar sobre los índices context es que éstos no se mantienen automáticamente con las inserciones. Hay una causa mayor referente al impacto del rendimiento que tendría dicha reconstrucción. En este caso, se incluyen seis nuevas filas BFILE vinculadas a ficheros PDF existentes en el mismo directorio. Posteriormente a la inserción, es preciso reconstruir el índice para recrearlo con la nueva relación de términos.

Para la implementación completa del ejemplo (creación de la tabla, el índice, el objeto directory y temas relativos a privilegios) es preciso seguir los pasos de la Parte I.

Pasos:

Parte I.

       1.- Creación de una tabla con columna BFILE
       2.- Creación de un DIRECTORY oracle para vincular los documentos Word.
       3.- Concesión de privilegios sobre el DIRECTORY al usuario CTXSYS, propietario de Oracle Context.
       4.- Inserción de las filas en la tabla DOCUMENTOS.
       5.- Indexación de la columna BFILE.
       6.- Ejemplos de consultas.

Parte II.

       7.- Inserción de las filas de documentos PDF.
       8.- Reconstrucción del índice context.
       9.- Ejemplos de consultas.


SQL> host dir c:documentos
El volumen de la unidad C no tiene etiqueta.
El número de serie del volumen es: 2416-B169

Directorio de c:documentos

09/05/2005  09:31       <DIR>          .
09/05/2005  09:31       <DIR>          ..
25/04/2005  13:21              271.360 EntornosNoProd_Permisos extendidos.doc
25/04/2005  13:21               31.232 normas_dba.doc
25/04/2005  13:21              379.904 creacion_entorno_bbdd.doc
25/04/2005  13:21              252.928 CREACION_INSTANCIA_DE_ORACLE_EN_UNIX_v2_4.doc              
04/06/2002  21:58            1.479.383 Oracle9i – New Features for Administrators – Vol I.pdf
04/06/2002  21:59            1.542.651 Oracle9i – New Features for Administrators – Vol II.pdf
04/06/2002  21:55            1.244.186 Oracle9i – New Features for Developers – Vol I.pdf
04/06/2002  21:57            2.342.972 Oracle9i – New Features for Developers – Vol II.pdf
04/06/2002  21:13            1.245.254 Oracle9i – New Features Overview – Vol I.pdf
04/06/2002  21:14              801.119 Oracle9i – New Features Overview – Vol II.pdf

10 archivos        9.935.424 bytes
               2 dirs   4.970.479.616 bytes libres

SQL> — Inserción de los seis documentos PDF sobre la tabla
SQL> — y recreación del índice CONTEXT
SQL> ———————————————————————————–
SQL> insert into documentos values
  2  (5,’PDF’,’Oracle9i – New Features for Administrators – Vol I.pdf’,
  3  BFILENAME(‘DOCUMENTOS_WORD’,’Oracle9i – New Features for Administrators – Vol I.pdf’));

1 row created.

SQL> insert into documentos values
  2  (6,’PDF’,’Oracle9i – New Features for Administrators – Vol II.pdf’,
  3  BFILENAME(‘DOCUMENTOS_WORD’,’Oracle9i – New Features for Administrators – Vol II.pdf’));

1 row created.

SQL> insert into documentos values
  2  (7,’PDF’,’Oracle9i – New Features for Developers – Vol I.pdf’,
  3  BFILENAME(‘DOCUMENTOS_WORD’,’Oracle9i – New Features for Developers – Vol I.pdf’));

1 row created.

SQL> insert into documentos values
  2  (8,’PDF’,’Oracle9i – New Features for Developers – Vol II.pdf’,
  3  BFILENAME(‘DOCUMENTOS_WORD’,’Oracle9i – New Features for Developers – Vol II.pdf’));

1 row created.

SQL> insert into documentos values
  2  (9,’PDF’,’Oracle9i – New Features Overview – Vol I.pdf’,
  3  BFILENAME(‘DOCUMENTOS_WORD’,’Oracle9i – New Features Overview – Vol I.pdf’));

1 row created.

SQL> insert into documentos values
  2  (10,’PDF’,’Oracle9i – New Features Overview – Vol II.pdf’,
  3  BFILENAME(‘DOCUMENTOS_WORD’,’Oracle9i – New Features Overview – Vol II.pdf’));

1 row created.

SQL> commit;

Commit complete.

SQL> ALTER INDEX idx_documentos_doc REBUILD;

Index altered.

— Las búsquedas CONTEXT sobre índices domain se realizan con la cláusula CONTAINS
— más documentación al respecto en   http://download-west.oracle.com/docs/cd/B10501_01/text.920/a96518/cqoper.htm )
————————————————————

— documentos con la palabra ‘tablespace’ cerca de ‘storage’
SQL> select tipo, titulo
  2  from documentos
  3  where CONTAINS(documento, ‘tablespace near storage’) &gt 0;

TIPO TITULO
—- ————————————————————
PDF  Oracle9i – New Features Overview – Vol II.pdf
PDF  Oracle9i – New Features Overview – Vol I.pdf
WORD Creación de instancia Oracle en UNIX.
WORD Normativa de DBA.

— documentos con una palabra parecida a ‘locally’ en un 70%
SQL> select tipo, titulo
  2  from documentos
  3  where CONTAINS(documento, ‘fuzzy(locally, 70, 6, weight)’, 1) &gt 0;

TIPO TITULO
—- ————————————————————
PDF  Oracle9i – New Features Overview – Vol II.pdf
PDF  Oracle9i – New Features Overview – Vol I.pdf
WORD Creación de un entorno de BBDD.

— documentos con una palabra parecida a ‘locally’ en un 60%
— nota: la palabra LOCAL aparece en estos dos nuevos documentos
SQL> select tipo, titulo
  2  from documentos
  3  where CONTAINS(documento, ‘fuzzy(locally, 60, 6, weight)’, 1) &gt 0;

TIPO TITULO
—- ————————————————————
PDF  Oracle9i – New Features Overview – Vol II.pdf
PDF  Oracle9i – New Features Overview – Vol I.pdf
WORD Creación de instancia Oracle en UNIX.
WORD Creación de un entorno de BBDD.
WORD Entornos no producción. Permisos extendidos.

— documentos que hablen sobre ‘listener’
SQL> select tipo, titulo
  2  from documentos
  3  where CONTAINS(documento, ‘about(listener)’) &gt 0;

TIPO TITULO
—- ————————————————————
PDF  Oracle9i – New Features Overview – Vol II.pdf
WORD Creación de instancia Oracle en UNIX.
WORD Normativa de DBA.

— relevancia de documentos que hablan de ‘listener’
— nota: en ‘Normativa de DBA’, efectivamente, se habla bastante menos sobre el listener.
SQL> select tipo, CONTAINS(documento, ‘about(listener)’)||’%’ relevancia, titulo
  2  from documentos
  3  where CONTAINS(documento, ‘about(listener)’) &gt 0;

TIPO RELEVANCIA TITULO
—- ———- ————————————————————
PDF  41%        Oracle9i – New Features Overview – Vol II.pdf
WORD 91%        Creación de instancia Oracle en UNIX.
WORD 23%        Normativa de DBA.


Más información en http://download-west.oracle.com/docs/cd/B10501_01/text.920/a96518/csql.htm#21732

Defragmentación de un tablespace mediante scripts.

En muchas ocasiones recurro a generar scripts a partir de consultas.
Ejecuto en SQL*Plus algo parecido a:


SQL> select ‘alter package ‘||owner||’.’||object_name||’ compile;’ sentencia
  2> from dba_objects where status=’INVALID’ and object_type =’PACKAGE BODY’;

SENTENCIA
——————————————————————
alter package USUARIO.MAP02_CUACON02 compile;
alter package USUARIO.MAP01_CUACON03 compile;
alter package USUARIO.MAP01_FLUXHISTO03_CI compile;
alter package USUARIO.MAP01_BALANCEBE03 compile;
alter package USUARIO.MAP01_PAHISTOACT03 compile;
alter package USUARIO.MAP01_RAPPELS03 compile;

6 filas seleccionadas.

y simplemente copio y pego las filas como comandos en la consola SQL.

El procedimiento podrá parecer tosco, pero resulta muy versátil ejecutar una consulta que construya dinámicamente todas las sentencias SQL que se precisan para una determinada tarea y, lo más importante, que se adapten a cualquier entorno.

Un ejemplo de ésto podría ser aplicado a la reorganización de los segmentos de un tablespace.

Para la defragmentación de un tablespace, los objetos deben reconstruirse de la siguiente forma:

   – Tablas:   ALTER TABLE xxxx MOVE STORAGE INITIAL tamaño K/M);
   – Índices:   ALTER INDEX xxxx REBUILD [online];
   – Tablas particionadas:  ALTER TABLE xxxx MOVE PARTITION …….;
   – Índices particionados: ALTER INDEX xxxx REBUILD PARTITION …… [online];

Teniendo en cuenta que mover una tabla en Oracle9i deja inválidos los índices, una vez ejecutados estos comandos por todas las tablas e índices del tablespace, sería preciso comprobar qué índices (o partitiones de índice) pueden haber quedado afectados y reconstruirlos.

Habría otro detalle a tener presente: los segmentos reconstruidos sobre el mismo tablespace, dificilmente consiguen liberar el espacio contiguo al final de los ficheros, por lo que sería útil hacer un doble movimiento: mover primero a un tablespace vacío, y posteriormente mover los objetos al tablespace original de vuelta.

Si bien esto no es complicado, sí resulta minucioso. Este script, en este caso, puede resultar tremendamente útil.

Una vez defragmentado el tablespace, es preciso ejecutar el segundo script para reconstruir índices, lo que evitará que éstos se queden inválidos después del transporte.

* Nota: Es preciso tener en cuenta el tamaño total de los objetos. Si éstos son grandes, conviene aumentar el tamaño definido como inicial. Lo mejor es utilizar tablespaces gestionados localmente con gestión automática de las extensiones para que Oracle decida, en tablas grandes, hacer extensiones mayores dinámicamente.


— DEFRAGMENTA_TABLESPACE.SQL —
set verify off
set lines 120
set pages 1000
define tablespace_origen =&TABLESPACE_DATOS
define tablespace_destino=&TABLESPACE_VACIO  
define t_extension_inicial=&TAMAÑO_EXTENSION_INICIAL
select ‘alter table ‘||owner||’.’||table_name||
       ‘ move tablespace &tablespace_destino storage (initial &t_extension_inicial M);’
from dba_tables
where upper(tablespace_name)=’&tablespace_origen’
union all
select ‘alter table ‘||table_owner||’.’||table_name||
       ‘ move partition ‘||partition_name||
       ‘ tablespace &tablespace_destino storage (initial &t_extension_inicial M);’
from dba_tab_partitions
where upper(tablespace_name)=’&tablespace_origen’
union all
select ‘alter index ‘||owner||’.’||index_name||
       ‘ rebuild tablespace &tablespace_destino storage (initial &t_extension_inicial M);’
from dba_indexes
where upper(tablespace_name)=’&tablespace_origen’
union all
select ‘alter index ‘||index_owner||’.’||index_name||
       ‘ rebuild partition ‘||partition_name||
       ‘ tablespace &tablespace_destino storage (initial &t_extension_inicial M);’
from dba_ind_partitions
where upper(tablespace_name)=’&tablespace_origen’
union all
select ‘alter table ‘||owner||’.’||table_name||
       ‘ move tablespace &tablespace_origen storage (initial &t_extension_inicial M);’
from dba_tables
where upper(tablespace_name)=’&tablespace_origen’
union all
select ‘alter table ‘||table_owner||’.’||table_name||
       ‘ move partition ‘||partition_name||
       ‘ tablespace &tablespace_origen storage (initial &t_extension_inicial M);’
from dba_tab_partitions
where upper(tablespace_name)=’&tablespace_origen’
union all
select ‘alter index ‘||owner||’.’||index_name||
       ‘ rebuild tablespace &tablespace_origen storage (initial &t_extension_inicial M);’
from dba_indexes
where upper(tablespace_name)=’&tablespace_origen’
union all
select ‘alter index ‘||index_owner||’.’||index_name||
       ‘ rebuild partition ‘||partition_name||
       ‘ tablespace &tablespace_origen storage (initial &t_extension_inicial M);’
from dba_ind_partitions
where upper(tablespace_name)=’&tablespace_origen’;

— RECONSTRUYE_INDICES.SQL —
select ‘alter index ‘||index_owner||’.’||index_name||
       ‘ rebuild partition ‘||partition_name||
       ‘ tablespace &tablespace_origen storage (initial &t_extension_inicial M);’ SENTENCIA
from dba_ind_partitions
where upper(tablespace_name)=’&tablespace_origen’
union all
select ‘alter index ‘||owner||’.’||index_name||’ rebuild;’
from dba_indexes
where status=’UNUSABLE’
union all
select ‘alter index ‘||index_owner||’.’||index_name||
       ‘ rebuild partition ‘||partition_name||’;’
from dba_ind_partitions
where status=’UNUSABLE’;

TEST
====
SQL> @c:reorganiza_tablespace
Introduzca un valor para tablespace_datos: DM_ACTIVIDAD01
Introduzca un valor para tablespace_vacio: TS_VACIO
Introduzca un valor para tama±o_extension_inicial: 1

‘ALTERTABLE’||OWNER||’.’||TABLE_NAME||’MOVETABLESPACETS_VACIOSTORAGE(INITIAL1M);’
————————————————————————————————————–
alter table USUARIO.TABLA_XXX02 move tablespace TS_VACIO storage (initial 1 M);
alter table USUARIO.TABLA_XXXX02 move tablespace TS_VACIO storage (initial 1 M);
alter table USUARIO.TABLA_XXXASMA02 move tablespace TS_VACIO storage (initial 1 M);
alter table USUARIO.TABLA_XXXXX02 move tablespace TS_VACIO storage (initial 1 M);
alter table USUARIO.TABLA_XXXXXX02 move tablespace TS_VACIO storage (initial 1 M);
alter table USUARIO.TABLA_XXXPMA02 move tablespace TS_VACIO storage (initial 1 M);
alter table USUARIO.TABLA_XXXP02 move tablespace TS_VACIO storage (initial 1 M);
alter table USUARIO.TABLA_XXXAS02 move tablespace TS_VACIO storage (initial 1 M);
alter table USUARIO.TABLA_XXXON02 move tablespace TS_VACIO storage (initial 1 M);
alter table USUARIO.TABLA_XXXPMES02 move tablespace TS_VACIO storage (initial 1 M);
alter table USUARIO.TABLA_XXXASMES02 move tablespace TS_VACIO storage (initial 1 M);
alter table USUARIO.TABLA_XXXCIBOSMES02 move tablespace TS_VACIO storage (initial 1 M);
alter table USUARIO.TABLA_XXXCIBOSMA02 move tablespace TS_VACIO storage (initial 1 M);
alter table USUARIO.TABLA_XXXCIBOS02 move tablespace TS_VACIO storage (initial 1 M);
alter table USUARIO.TABLA_XXXTASMES02 move tablespace TS_VACIO storage (initial 1 M);
alter table USUARIO.TABLA_XXXTAS02 move tablespace TS_VACIO storage (initial 1 M);
alter table USUARIO.TABLA_XXXTASMA02 move tablespace TS_VACIO storage (initial 1 M);
alter table USUARIO.TABLA_XXXXXXY02 move tablespace TS_VACIO storage (initial 1 M);
alter table USUARIO.TABLA_XXXMA02 move tablespace TS_VACIO storage (initial 1 M);
alter table USUARIO.TABLA_XXXMES02 move tablespace TS_VACIO storage (initial 1 M);
alter table USUARIO.TABLA_XXXACION02 move tablespace TS_VACIO storage (initial 1 M);
alter table USUARIO.TABLA_XXXACIONMA02 move tablespace TS_VACIO storage (initial 1 M);
alter table USUARIO.TABLA_XXXACIONMES02 move tablespace TS_VACIO storage (initial 1 M);
alter table USUARIO.TABLA_XXXTVS02 move tablespace TS_VACIO storage (initial 1 M);
alter table USUARIO.TABLA_XXXTVSMA02 move tablespace TS_VACIO storage (initial 1 M);
alter table USUARIO.TABLA_XXXTVSMES02 move tablespace TS_VACIO storage (initial 1 M);
alter table USUARIO.TABLA_XXXYY02 move tablespace DM_ACTIVIDAD01 storage (initial 1 M);
alter table USUARIO.TABLA_XXXY02 move tablespace DM_ACTIVIDAD01 storage (initial 1 M);
alter table USUARIO.TABLA_XXXASMA02 move tablespace DM_ACTIVIDAD01 storage (initial 1 M);
alter table USUARIO.TABLA_XXX02 move tablespace DM_ACTIVIDAD01 storage (initial 1 M);
alter table USUARIO.TABLA_XXX02 move tablespace DM_ACTIVIDAD01 storage (initial 1 M);
alter table USUARIO.TABLA_XXXPMA02 move tablespace DM_ACTIVIDAD01 storage (initial 1 M);
alter table USUARIO.TABLA_XXXP02 move tablespace DM_ACTIVIDAD01 storage (initial 1 M);
alter table USUARIO.TABLA_XXXAS02 move tablespace DM_ACTIVIDAD01 storage (initial 1 M);
alter table USUARIO.TABLA_XXXON02 move tablespace DM_ACTIVIDAD01 storage (initial 1 M);
alter table USUARIO.TABLA_XXXPMES02 move tablespace DM_ACTIVIDAD01 storage (initial 1 M);
alter table USUARIO.TABLA_XXXASMES02 move tablespace DM_ACTIVIDAD01 storage (initial 1 M);
alter table USUARIO.TABLA_XXXCIBOSMES02 move tablespace DM_ACTIVIDAD01 storage (initial 1 M);
alter table USUARIO.TABLA_XXXCIBOSMA02 move tablespace DM_ACTIVIDAD01 storage (initial 1 M);
alter table USUARIO.TABLA_XXXCIBOS02 move tablespace DM_ACTIVIDAD01 storage (initial 1 M);
alter table USUARIO.TABLA_XXXTASMES02 move tablespace DM_ACTIVIDAD01 storage (initial 1 M);
alter table USUARIO.TABLA_XXXTAS02 move tablespace DM_ACTIVIDAD01 storage (initial 1 M);
alter table USUARIO.TABLA_XXXTASMA02 move tablespace DM_ACTIVIDAD01 storage (initial 1 M);
alter table USUARIO.TABLA_XXX02 move tablespace DM_ACTIVIDAD01 storage (initial 1 M);
alter table USUARIO.TABLA_XXXMA02 move tablespace DM_ACTIVIDAD01 storage (initial 1 M);
alter table USUARIO.TABLA_XXXMES02 move tablespace DM_ACTIVIDAD01 storage (initial 1 M);
alter table USUARIO.TABLA_XXXACION02 move tablespace DM_ACTIVIDAD01 storage (initial 1 M);
alter table USUARIO.TABLA_XXXACIONMA02 move tablespace DM_ACTIVIDAD01 storage (initial 1 M);
alter table USUARIO.TABLA_XXXACIONMES02 move tablespace DM_ACTIVIDAD01 storage (initial 1 M);
alter table USUARIO.TABLA_XXXTVS02 move tablespace DM_ACTIVIDAD01 storage (initial 1 M);
alter table USUARIO.TABLA_XXXTVSMA02 move tablespace DM_ACTIVIDAD01 storage (initial 1 M);
alter table USUARIO.TABLA_XXXTVSMES02 move tablespace DM_ACTIVIDAD01 storage (initial 1 M);

52 filas seleccionadas.

Una vez ejecutados todos estos comandos, lanzamos el script que consulta los índices inválidos con el fin de recompilarlos.


SQL> @c:reconstruye_indices
SENTENCIA
——————————————————————-
alter index USUARIO.INDICEXXXOSP02_01 rebuild;
alter index USUARIO.INDICEXXXURAS02_01 rebuild;
alter index USUARIO.INDICEXXXSION02_01 rebuild;
alter index USUARIO.INDICEXXXOSPMES02_01 rebuild;
alter index USUARIO.INDICEXXXRECIBOS02_01 rebuild;
alter index USUARIO.INDICEXXXABTAS02_01 rebuild;
alter index USUARIO.INDICEXXXDA02_01 rebuild;
alter index USUARIO.INDICEXXXCIACION02_01 rebuild;
alter index USUARIO.INDICEXXXTVS02_01 rebuild;
alter index USUARIO.INDICEXXXTVSMA02_01 rebuild;
alter index USUARIO.INDICEXXXTVSMES02_01 rebuild;

11 filas seleccionadas.

Generación de XML mediante query.

En algunos proyectos en los que he participado, el equipo de front-end, que desarrollaba la interfaz web sobre plataforma .NET, tenía serios problemas para recuperar un conjunto de filas con un objeto tipo array incrustado.

La consulta debía devolver un vector de registros, algunos de ellos formados por una lista de valores. En aquel entonces, el driver de .NET no podía gestionar objetos UDT (tipos de datos definidos por el usuario) y hubo que comprar un componente aparte que tampoco dio buen resultado,pues no soportaba consultas a NESTED TABLES.

Una de las soluciones propuestas fue devolver el resultado (se trataba de fichas de clientes) en formato XML. La idea era buena, pero había alguno que pretendía “decodificar” columnas y filas para elaborar el resultado… ¡escribiendo un procedure con substrs!

No hace falta. La funcionalidad de crear UDT tiene una importancia relevante en las conversiones a XML. Definiendo los tipos como OBJECT y TABLE OF OBJECT podemos crear una estructura de datos facilmente exportable a XML tal y como muestra el ejemplo.

Las tablas tomadas como referencia corresponden a EMP y DEPT del esquema de ejemplo SCOTT.


SQL> create or replace type r_empleado is object(
  2     NUM_EMP          NUMBER(4),
  3     NOMBRE_EMP       VARCHAR2(10),
  4     OFICIO_EMP       VARCHAR2(9),
  5     JEFE_EMP         NUMBER(4),
  6     FCONTRATO_EMP    DATE,
  7     SALARIO_EMP      NUMBER(7,2),
  8     COMISION_EMP     NUMBER(7,2),
  9     DEPARTAMENTO_EMP NUMBER(2));
10  /

Tipo creado.

SQL>
SQL> create or replace type t_empleado is table of r_empleado;
  2  /

Tipo creado.

SQL>
SQL> create or replace type r_dept is object(
  2     NUM_DEPT     NUMBER(2),
  3     NOMBRE_DEPT  VARCHAR2(14),
  4     CIUDAD_DEPT  VARCHAR2(13),
  5     empleados    t_empleado);
  6  /

Tipo creado.

SQL>
SQL> create or replace type t_dept is table of r_dept;
  2  /

Tipo creado.

SQL>
SQL>
SQL> select sys_xmlgen(
  2               r_dept(dept.deptno, dept.dname, dept.loc,
  3                 cast(multiset(select emp.*
  4                                 from emp
  5                                where emp.deptno = dept.deptno) as t_empleado)
  6                    ),xmlformat.createFormat(‘DEPARTAMENTO’)).getClobVal() as XML
  7  from dept
  8  where deptno=10;

XML
——————————————————————————–
<?xml version=”1.0″?>
<DEPARTAMENTO>
<NUM_DEPT>10</NUM_DEPT>
<NOMBRE_DEPT>ACCOUNTING</NOMBRE_DEPT>
<CIUDAD_DEPT>NEW YORK</CIUDAD_DEPT>
<EMPLEADOS>
  <R_EMPLEADO>
   <NUM_EMP>7782</NUM_EMP>
   <NOMBRE_EMP>CLARK</NOMBRE_EMP>
   <OFICIO_EMP>MANAGER</OFICIO_EMP>
   <JEFE_EMP>7839</JEFE_EMP>
   <FCONTRATO_EMP>09/06/81</FCONTRATO_EMP>
   <SALARIO_EMP>2450</SALARIO_EMP>
   <DEPARTAMENTO_EMP>10</DEPARTAMENTO_EMP>
  </R_EMPLEADO>
  <R_EMPLEADO>
   <NUM_EMP>7839</NUM_EMP>
   <NOMBRE_EMP>KING</NOMBRE_EMP>
   <OFICIO_EMP>PRESIDENT</OFICIO_EMP>
   <FCONTRATO_EMP>17/11/81</FCONTRATO_EMP>
   <SALARIO_EMP>5000</SALARIO_EMP>
   <DEPARTAMENTO_EMP>10</DEPARTAMENTO_EMP>
  </R_EMPLEADO>
  <R_EMPLEADO>
   <NUM_EMP>7934</NUM_EMP>
   <NOMBRE_EMP>MILLER</NOMBRE_EMP>
   <OFICIO_EMP>CLERK</OFICIO_EMP>
   <JEFE_EMP>7782</JEFE_EMP>
   <FCONTRATO_EMP>23/01/82</FCONTRATO_EMP>
   <SALARIO_EMP>1300</SALARIO_EMP>
   <DEPARTAMENTO_EMP>10</DEPARTAMENTO_EMP>
  </R_EMPLEADO>
</EMPLEADOS>
</DEPARTAMENTO>