Follow Us on Twitter

Van SQL naar XML vanuit een Oracle database

Gepubliceerd in

Februari 2009 - Databestanden aanleveren vanuit de database kan op vele manieren. Veelal gebeurt dit nog op de conventionele manier, het 'platte ASCII bestand'. Het maken van een comma separated (CSV) bestand met behulp van de package UTL_FILE is hier een voorbeeld van. Een heel goed alternatief, zonder gebruik te hoeven maken van PL/SQL(!), is 'Oracle SQL/XML'.

Vanaf Oracle 9i beschikken we over de XML DB repository. Dit is de Oracle 'out-of-the-box' oplossing (een transparante interface) tussen de Oracle database en de daarin opgeslagen XML gegevens. XML documenten kunnen via SQL, of met behulp van internet de protocollen WebDaV en FTP, worden opgeslagen, gewijzigd of verwijderd. Verderop wordt besproken hoe je heel eenvoudig met behulp van het WebDav protocol een XML document kan openen.

Dit artikel laat een toepassing zien waarbij een aantal SQL/XML functies, XMLElement, XMLAttribute, XMLForest, XMLAgg worden gebruikt. Deze functies zijn de Oracle implementaties van de SQL/XML ISO/IEC 9075-14:2006 Standaard. We gaan zien ook hoe met behulp van een XML query een XMLtype view gemaakt wordt. Deze XMLtype view wordt gebruikt om XML bestanden beschikbaar te maken op het filesystem. Tevens laat ik zien hoe met behulp van het WebDav protocol het XML document vanuit de Oracle XML DB repository op het filesysteem beschikbaar is gekomen en kan worden gelezen.

In het voorbeeld wordt gebruik gemaakt van de standaard meegeleverde tabellen DEPARTMENTS en EMPLOYEES van het HR schema.

De eerste functie, XMLElement is tevens de meest gebruikte functie.

select xmlelement("achternaam",emp.last_name )
  from employees emp
where emp.last_name like 'A%'

Abel
Ande
Atkinson
Austin

Het eerst argument is een identifier, niet zomaar een string dus. Vandaar het gebruik van dubbele quotes om achternaam. Laat je die quotes achterwege, dan wordt de identifier (net zoals in kolomnamen van een tabel) naar hoofdletters omgezet. Het tweede argument is uiteraard een kolom van de tabel, in dit geval de last_name.

Je wilt natuurlijk meer dan 1 kolom in je XML file. Hiervoor is het mogelijk een de aanroep van XMLElement te nesten. Dus binnen de aanroep van XMLElement nogmaals, meerdere, aanroepen van XLMElement te plaatsen. Het volgende voorbeeld toont een aantal afdelingen met van die afdeling de naam en de bijhorende manager. Let ook op het gebruik van de functie XMLAttributes. Het department_id wordt in de XML file een attribute van het element . Het gebruik van de outerjoin verraad al dat niet iedere afdeling een manager heeft. Indien de waarde van een kolom NULL is, resulteert dit bij gebruik van XMLElement in een leeg element. In ons geval 2 maal een leeg element .

select XMLElement("afdeling", XMLAttributes(dep.department_id AS ID)
          ,XMLElement("naam",dep.department_name)
          ,XMLElement("manager",emp.last_name)
          )
from   departments dep
      ,employees emp
where  dep.manager_id = emp.employee_id(+)
  and  dep.department_name like 'IT%'

ITHunold
IT Support
IT Helpdesk

Indien een leeg element niet gewenst is kan dit op meerdere manieren worden ondervangen. Dit kan eenvouding door het CASE statement te gebruiken in je select.

select  XMLElement("afdeling", XMLAttributes(dep.department_id AS ID)
          ,XMLElement("naam",dep.department_name)
          ,CASE WHEN emp.last_name IS NULL THEN NULL
             ELSE  XMLElement("manager",emp.last_name) END
          )
from departments dep
    ,employees emp
where dep.manager_id = emp.employee_id(+)
  and dep.department_name like 'IT%'

ITHunold
IT Support
IT Helpdesk

Een andere optie is het gebruik van de functie XMLForest. XMLForest, de naam zegt het al, genereert een woud van elementen middels de aanroep van één functie. Bijkomend voordeel is dat lege elementen automatisch weggelaten worden. Dit resulteert in een zeer compacte notatie. Zeker als we, zoals in het volgende voorbeeld, meerdere kolommen willen tonen van EMPOYEES.

select  XMLElement("medewerker", XMLAttributes(emp.employee_id AS ID)
         ,XMLForest(emp.first_name AS "roepnaam"
                   ,emp.last_name AS "achternaam"
                   ,emp.email AS "emailadres"      
                   ,emp.phone_number AS "telefoon"
                   )
                 )
    from employees emp  
    where emp.last_name like 'A%'

EllenAbel
EABEL011.44.1644.429267

SundarAnde
SANDE011.44.1346.629268

MozheAtkinson
MATKINSO650.124.6234

DavidAustin
DAUSTIN590.423.4569

Bovenstaand XML query levert per medewerker één XML document. We gaan nu per afdeling één XML document leveren met daarin verzamelt alle medewerkers bij deze afdelingen. We doen dit door het gebruik van de functie XMLAgg. XMLAgg is net als MIN, MAX, SUM en COUNT een verzamel functie. We moeten dus in onze XML query gebruik maken van een GROUP BY expressie.

select XMLElement
        ("afdeling",XMLAttributes(dep.department_name as "naam")
         ,XMLAgg(
                 XMLElement("medewerker"
                   ,XMLAttributes(emp.employee_id as "id")
                   ,XMLForest(emp.first_name AS "roepnaam"
                             ,emp.last_name AS "achternaam"
                             ,emp.email AS "emailadres"      
                             ,emp.phone_number AS "telefoon"
                             )
                           )
                 )
       )
  from employees emp          
      ,departments dep
where dep.department_id = emp.department_id
   and dep.department_name like 'A%'
group by dep.department_name


  
       Shelley
       Higgins
       SHIGGINS
       515.123.8080
  
  
      William
      Gietz
      WGIETZ
      515.123.8181
  


  
    Jennifer
    Whalen
    JWHALEN
    515.123.4444
    

En dan nu het leukste (vanaf Oracle9i Database Release 9.2.0.2 of hoger).

Je kunt een SQL/XML query gebruiken als basis voor een XMLtype view. Door deze XMLtype view te koppelen aan een resource in de XML DB repository wordt op het filesystem het bijhordende XML document gemaakt. Wanneer je dit XML document opent in een applicatie als b.v. Microsoft Excel of Internet Explorer, wordt de inhoud van dit document rechtstreeks vanuit de database aangemaakt. Je werkt dus altijd met de meest actuele gegevens!

Eerst de XMLtype view.

create or replace view afdelingen_xml of XMLTYPE
   with object id (extractValue(sys_nc_rowinfo$,'/afdeling/@naam')
                  ) from columns (dep.department_name)
AS
select XMLElement
        ("afdeling",XMLAttributes(dep.department_name as "naam")
         ,XMLAgg(
                 XMLElement("medewerker"
                   ,XMLAttributes(emp.employee_id as "id")
                   ,XMLForest(emp.first_name AS "roepnaam"
                             ,emp.last_name AS "achternaam"
                             ,emp.email AS "emailadres"      
                             ,emp.phone_number AS "telefoon"
                             )
                           )
                 )
       )
  from employees emp          
      ,departments dep
where dep.department_id = emp.department_id
group by dep.department_name

De inhoud van de view is uiteraard gelijk aan het resultaat de XML query waarbij voor het eerst XMLAgg gebruikt werd. De object id clause in het create view statement genereert een uniek object id voor iedere rij in de view. De view maakt per rij, dus voor iedere afdeling een XML document met daarin opgenomen alle medewerkers bij deze afdeling. Sys_nc_rowinfo$ is een verwijzing naar de huidige rij in de view. De XPath query '/afdeling/@naam' zorgt er voor dat waarde van het attribute 'naam' van het element gebruikt wordt om een unieke identificatie te genereren voor iedere rij in de view.

Nu we de XMLtype view AFDELINGEN_XML aangemaakt hebben kunnen we verder met het aanmaken van XML DB repository resources. Deze resources vormen uiteindelijk de XML documenten op het filesystem.

declare
  cursor c_afd is
    select dep.department_name naam
      from employees emp          
          ,departments dep
     where dep.department_id = emp.department_id
     group by dep.department_name;

   xmlref REF XMLType;
   l_res boolean;
  
begin
  for r_afd in c_afd loop
    select make_ref(afdelingen_xml,r_afd.naam)
      into xmlref
      from dual;
      l_res := DBMS_XDB.createResource('/public/'||r_afd.naam||'.xml'
                                      ,xmlref);
  end loop;
end;

Na uitvoeren van dit blok pl/sql code vindt je op je filesystem via het WebDav protocol in de map http://localhost:8080/public per afdeling het bijhorende XML bestand. Merk op dat voor het aanmaken van de XML bestanden de XMLtype view AFDELINGEN_XML niet gelezen wordt. De aanroep naar make_ref genereert een verwijzing naar een rij in de view. Deze verwijzing wordt gebruikt door de aanroep van DBMS_XDB.createResource. Doordat gebruikt wordt gemaakt van verwijzingen is het niet nodig de XML bestanden opnieuw aan te maken zodra er in de onderliggende tabellen gegevens wijzigen. Voor het raadplegen van de actuele stand in de database is opnieuw opvragen van de XML documenten voldoende.

Conclusie

De XML-db repository gecombineerd met de SQL/XML functies, XMLElement, XMLAttribute, XMLForest, XMLAgg zijn heel goed bruikbaar om op een relatief eenvoudige wijze gegevens uit de database beschikbaar te stellen op het filesystem. Bijkomend voordeel van SQL/XML boven het conventionele UTL_FILE is dat de XML bestanden altijd de meest actuele gegevens bevat.

Aan de slag dus!

Referenties

Eerdere relevante Whitebooks over XML in Oracle en XML DB:

Over de auteur
Jan Thuis is consultant bij Whitehorses en heeft ruim 19 jaar IT ervaring waarvan meer dan 15 jaar als Oracle specialist. In zijn vakgebied probeert hij het beste uit al zijn kennisgebieden te combineren om zo een juiste combinatie van flexibiliteit, kwaliteit en snelheid te realiseren.

Waardering:
 

Reacties

Hi, Hoe werkt de omgekeerde weg nl nadat je van een tabel (met een blob kolom) hebt geexporteerd naar xml en via deze xml in dezelfde tabel structuur in een andere schema wilt importeren. Stel ik heb de volgende tabel in schema A nl: test (id number, oms varchar2(40), afbeelding blob) ik kan zonder problemen de inhoud exporteren met bijv. dbms_xmlgen.getxml('select * from test') from dual; Ik zie bijv dat de blob kolom wordt vervangen door een rij met cijfers en letters (waarschijnlijk based64 encoded) Nu wil ik deze xml wederom importeren in test schema B met dezelfde structuur als in schema A. Ik heb veel voorbeelden gezien met xmltype kolom maar ik wil gewoon de gegenereerde xml opnieuw importeren naar dezelfde tabel structuur(met een blob erin).... bvd. Ton

Nieuwe reactie inzenden

De inhoud van dit veld is privé en zal niet openbaar worden gemaakt.

Meer informatie over formaatmogelijkheden

CAPTCHA
Deze vraag is om te testen of u een persoon bent en om spam te voorkomen
Image CAPTCHA
Enter the characters shown in the image.