Wykład 6

XML w obiektowo-relacyjnej bazie danych

 

Streszczenie

Język XML jest standardem reprezentowania danych służącym do wymiany danych między aplikacjami, w tym również między bazami danych. Szczególne znaczenie ma on dla wymiany danych w Internecie. Podstawową jednostką organizacji danych staje się dokument. Dokumentem w szczególnym przypadku może być zawartość strony WWW. W tym wykładzie zajmiemy się rozszerzeniami do przetwarzania dokumentów XML, które dodano do obiektowo-relacyjnych baz danych.

W pierwszej części wykładu przedstawimy informacje na temat reprezentacji dokumentów XML w bazie danych oraz na temat wyszukiwania słów kluczowych w dokumentach tekstowych (niekoniecznie w dokumentach XML).

W drugiej części opiszemy implementację XML w obiektowo-relacyjnej bazie danych Oracle za pomocą typu obiektowego XMLType.

 


6.1 Zastosowania dokumentów XML

XML jest skrótem od eXtensible Markup Language. Ratifikowany przez World Wide Web Consortium (W3C), XML stał się standardem identyfikowania i opisywania danych (dokumentów) przekazywanych w sieci WWW. Podobnie jak HTML, XML jest podzbiorem obszerniejszego i mającego dłuższą historię języka SGML (ang. Structured Generalized Markup Language).

XML umożliwia opisywanie zarówno zawartości jak i struktury dokumentu niezależnie od sposobu jego prezentacji na ekranie użytkownika. Zastosowania dokumentów XML są trzech rodzajów.

  1. Umieszczanie metadanych w dokumencie tekstowym/multimedialnym.
  2. Oddzielenie prezentacji od struktury dokumentu, co umożliwia różne  prezentacje tego samego dokumentu.
  3. Ułatwienie wymiany danych biznesowych między aplikacjami. Integracja danych pochodzących z różnych baz danych i aplikacji.

Przykład XML

Rozważymy listę publikacji jako przykładowy dokument XML.
 
<LISTA>
 <PUBLIKACJA>
  <AUTOR>
   <IMIĘ>Lech</IMIĘ >
   <NAZWISKO>Banachowski</NAZWISKO>
  </AUTOR>
  <TYTUŁ>Bazy danych</TYTUŁ>
  <ROK>1998</ROK>
 </PUBLIKACJA>
 <PUBLIKACJA>
  <AUTOR>
   <IMIĘ>Michał</IMIĘ>
   <NAZWISKO>Lentner</NAZWISKO>
  </AUTOR>
  <TYTUŁ>Oracle9i</TYTUŁ>
  <FORMAT>Twarda okładka</FORMAT>
 </PUBLIKACJA>
</LISTA>

Widok dokumentu XML w postaci drzewa

Wygodnie jest patrzeć na dokument XML jak na strukturę hierarchiczną, drzewiastą. Jest to postać, często używana do reprezentowania dokumentu  XML w pamięci wewnętrznej przez procesor dokumentów XML.

Elementy i ich tekstowe zawartości są reprezentowane przez węzły drzewa.

Rys. 6.1 Reprezentacja drzewowa dokumentu XML

Modelowanie powiązań między obiektami w ramach jednego dokumentu XML

Model danych XML umożliwia powiązania między obiektami opisywanymi w dokumencie XML. Na przykład: jedna publikacja może mieć wielu autorów; jeden autor może być autorem wielu publikacji. Ta cecha pozwala definiować semistrukturalne dane w postaci dowolnego grafu nie tylko drzewa. Powiązania między obiektami są realizowane poprzez wartości atrybutów.

Typy danych ID, IDREF, IDREFS

Typ ID dostarcza jednoznacznych identyfikatorów dla elementów.

Typy danych IDREF i IDREFS pozwalają korzystać ze wskaźników do elementów wyróżnionych w danym dokumencie. IDREF oznacza pojedynczy wskaźnik; IDREFS oznacza listę wskaźników.

Wartością atrybutu IDREF musi być nazwa występująca jako wartość atrybutu typu ID.

Wartością atrybutu IDREFS musi być lista nazw występujących jako wartości atrybutów typu ID.

Przykład

Przedstawimy dokument XML z atrybutami typów ID i IDREFS umożliwiającymi powiązanie publikacji z autorami.

<LISTA>
  <AUTOR Id_autora="LB" Lista_pub="BD1,BD2,BD3">
     <IMIĘ>Lech</IMIĘ>
     <NAZWISKO>Banachowski</NAZWISKO>
  </AUTOR>
  <AUTOR Id_autora="KS" Lista_pub="BD1,BD2,SO">
     <IMIĘ>Krzysztof</IMIĘ>
     <NAZWISKO>Stencel</NAZWISKO>
  </AUTOR>
  <PUBLIKACJA Id_publikacji="BD1" Lista_autor="LB,KS">
     <TYTUŁ>Bazy danych. Projektowanie aplikacji na serwerze</TYTUŁ>
     <ROK>2001</ROK>
  </PUBLIKACJA>
  <PUBLIKACJA Id_publikacji="BD2" Lista_autor="LB,KS,AC,EM,KM">
     <TYTUŁ>Bazy danych. Wykłady i ćwiczenia</TYTUŁ>
     <FORMAT>Twardy</FORMAT>
  </PUBLIKACJA>
........
</LISTA>

 


6.2 Metody przechowywania dokumentów w obiektowo-relacyjnej bazie danych

Dokumenty tekstowe (w tym XML) mogą być reprezentowane w obiektowo-relacyjnej bazie danych na dwa sposoby:

1. zwykle jako duże obiekty CLOB - ewentualnie, z dodanym indeksowaniem ich zawartości. Aby dokonać przetworzenia dokumentu sprowadza się go do pamięci wewnętrznej i stosuje się odpowiednie metody, jak na przykład przetwarzanie za pomocą drzewa. Istniejące indeksy pozwalają ograniczyć zbiór sprowadzanych do pamięci dokumentów.

2. dokumenty XML o ustalonym schemacie poprzez zbiór tabel relacyjnych lub obiektowych. Operacje przetwarzania zbioru dokumentów tłumaczy się na operacje na tabelach bazy danych.

Listy książek zapisywane w przykładzie powyżej jako dokumenty XML można reprezentować w bazie danych w płaskich tabelach relacyjnych (według zasady, że elementy przechodzą na tabele):

LISTA(Listid:Integer)
PUBLIKACJA(Pubid:Integer, Listid:Integer, Tytuł:String, Rok:String,
            Rodzaj:String, Format:String)
AUTOR(Autorstwoid:Integer, Pubid:Integer, Imię:string, Nazwisko:string)

I na odwrót, zawartość zwykłej tabeli relacyjnej może być w naturalny sposób reprezentowana jako dokument XML np. przykładowa tabela Emp:

<ROWSET Table="Emp">
  <ROW>
    <EMPNO>2345</EMPNO>
    <ENAME>SCOTT</ENAME>
...
  </ROW>
  <ROW> ....
  </ROW>
....
</ROWSET>
Dla ułatwienia posługiwania się dokumentami XML w Oracle wprowadzono nowy, specjalny obiektowy typ danych XMLType, którego wartości reprezentują dokumenty XML. XMLType osłania programistę bazy danych przed szczegółami implementacyjnymi. Programista nie musi zajmować się tym, czy dokument XML jest zapisany jako obiekt CLOB czy jako zbiór powiązanych wierszy w tabelach obiektowo-relacyjnych. O XMLType pomówimy za chwilę po zaznajomieniu się najpierw z tematem przechowywania dowolnych dokumentów tekstowych (niekoniecznie XML).

Dla pełności obrazu reprezentacji dokumentów XML w bazie danych wspomnijmy jeszcze o przechowywaniu w bazie danych tylko samego adresu URL strony WWW zawierającej dokument.

 


6.3 Tekstowa baza danych

Tekstowa baza danych jest to zbiór dokumentów tekstowych. W bazie danych dokumenty tekstowe są zwykle zapisywane jako:

Dodatkowo dobudowuje się struktury indeksowe wspomagające wyszukiwanie dokumentów.

Ważną klasę zapytań w tekstowej bazie danych stanowią wyszukiwania po słowach kluczowych.

Zapytania boolowskie: Składniki zapytania są powiązane spójnikami AND, OR i NOT. Wynikiem zapytania jest lista dokumentów, które spełniają wyrażenie boolowskie, np.

Database AND (Microsoft OR Oracle)
Zapytania rankingowe: Wynikiem zapytania jest lista dokumentów, które spełniają wyrażenie boolowskie, uporządkowane według stopnia istotności danego dokumentu dla zapytania.

Struktura wektorowa

Zawiera informacje: termin j występuje k razy w dokumencie i.

Id Database Oracle Microsoft SQL Table Query
1 2 2 0 1 5 8
2 1 0 1 1 4 9
3 0 0 0 23 56 67
4 1 1 1 0 0 0
5 0 1 1 1 0 0

Zapytanie o dokumenty zawierające podane słowo kluczowe np. Database zwraca dokumenty uporządkowane według liczby wystąpień danego słowa kluczowego (najprostsza miara istotności). Wyliczenie wyniku zapytania boolowskiego np. Database AND Oracle AND Microsoft sprowadza się do zastosowania operatora koniunkcji na trzech wektorach (kolumnach) - w wyniku otrzymujemy dokument 4 z wagą 1.

Indeksowanie - pliki odwrócone (ang. inverted files)

Dla każdego terminu zapisujemy listę odwróconą identyfikatorów  dokumentów, w których występuje dany termin. Na przykład, dla dwóch dokumentów:
 
Id_dok Dokument
1 Oracle Database
2 Microsoft Database

struktura danych list odwróconych wygląda następująco:
 
Słowo Lista odwrócona
Oracle 1
Database 1, 2
Microsoft 2

Wyznaczenie wyniku zapytania boolowskiego sprowadza się do operacji przecięcia i sumy list odwróconych.

Przykład: Wyznaczenie wyniku zapytania Database AND Oracle sprowadza się do przecięcia dwóch list odwróconych. Wyznaczenie wyniku zapytania Database OR Oracle sprowadza się do sumy dwóch list odwróconych.

W praktyce listy odwrócone (np. w wyszukiwarkach internetowych) są bardzo długie. Słowa kluczowe są zwykle zorganizowane w strukturę danych nazywaną leksykonem (np. w postaci drzewa) przechowywaną w pamięci wewnętrznej.

Zastosowanie funkcji haszującej

Niech wartością funkcji haszującej h(k) dla słowa kluczowego k będzie wektor bitowy. Niech dokument D zawiera słowa kluczowe k1,…,kn. Sygnaturą dokumentu H(D) nazywamy wartość h(k1) OR … OR h(kn). Jeśli zapytanie Z dotyczy koniunkcji słów kluczowych z1,…, zi liczymy sygnaturę zapytania: H(Z)=h(z1) OR … OR h(zi).

Wyszukiwanie przeprowadzamy najpierw na sygnaturach. Mianowicie, szukamy wszystkich dokumentów D takich, że H(Z) AND H(D) = H(Z) (czyli H(D) zawiera H(Z)) (ograniczając się do sygnatur dokumentów) i tylko dla nich sprawdzamy czy D należy do wyników zapytania Z tj. czy wszystkie słowa kluczowe zapytania Z zawierają się w zbiorze słów kluczowych dokumentu D.

 


6.4 Oracle: typ obiektowy XMLType

W Oracle dokument XML może być reprezentowany jako obiekt wbudowanego typu obiektowego XMLType. Za pomocą metod typu XMLType można tworzyć, wydobywać i indeksować dane XML przechowywane w bazie danych. Typ XMLType tak jak każdy typ obiektowy może być używany jako typ danych kolumn lub jako typ tabeli obiektowej.

Załóżmy, że chcielibyśmy przechowywać w bazie danych książkę adresową z umożliwieniem zapisu obok danych typowych również nietypowych danych kontaktowych. Dodatkowo chcielibyśmy móc łatwo przekazywać dane kontaktowe w postaci tekstowej przez Internet. Oto nasze rozwiązanie korzystające z kolumny typu XMLType.

CREATE TABLE Kontakty(
    Nazwisko VARCHAR2(50),
    Karta XMLTYPE,
    Data_utworzenia DATE);
Dla pełności obrazu zauważmy, że alternatywnie można wszystkie dane umieścić wewnątrz dokumentu XML i zamiast z tabeli relacyjnej Kontakty korzystać z tabeli obiektowej typu XMLType.
CREATE TABLE Obj_Kontakty OF XMLType;

Wstawianie do kolumn typu XMLType 

Egzemplarz typu XMLType tworzy się z wartości VARCHAR2 lub CLOB przy użyciu konstruktora XMLType:

INSERT INTO Kontakty VALUES ('KOWALSKI',
  XMLType('<KARTA>
             <EMAIL>Jan.Kowalski@praca.pl</EMAIL>
             <TEL_PRACA>33-456</TEL_PRACA>
             <TEL_DOM>28-991</TEL_DOM>
             <TEL_KOM>600-345</TEL_KOM>
             <ADRES>
                <ULICA>Wygodna 9m1</ULICA>
                <MIASTO>Warszawa</MIASTO>
                <KOD>02-782</KOD>
             </ADRES>
           </KARTA>'),
Sysdate);

INSERT INTO Kontakty VALUES ('NOWAK',
  XMLType('<KARTA>
             <EMAIL>Anna.Nowak@praca.pl</EMAIL>
             <WWW>http://www.praca.pl/prac/annaw.html</WWW>
             <TEL_PRACA>33-456</TEL_PRACA>
             <TEL_DOM>65-998</TEL_DOM>
             <ADRES>
                <HOTEL>Moniuszko</HOTEL>
                <ULICA>Moniuszki 19 pok.205</ULICA>
                <MIASTO>Warszawa</MIASTO>
                <KOD>00-782</KOD>
             </ADRES>
           </KARTA>'),
Sysdate);

Wyszukiwanie w dokumentach XML

Przy wydobywaniu danych z obiektu typu XMLType korzystamy z metod konwersji (obowiązuje zasada, że część obiektu XMLType jest też obiektem XMLType):
 
GetStringval(): XMLType -> VARCHAR2
GetClobval(): XMLType -> CLOB

GetNumberval: XMLType -> NUMBER

Załóżmy, że interesują nas wszystkie adresy elektroniczne z tabeli Kontakty. Użyjemy metody Extract oraz jej argumentu będącego wyrażeniem XPath. Metoda ta wyznacza wszystkie elementy dokumentu XML opisane przez podaną ścieżkę.

SELECT w.Karta.Extract('/KARTA/EMAIL/text()').GetStringVal() "EMail"
FROM Kontakty w;
W wyniku otrzymujemy kolumnę wartości typu VARCHAR2:
EMail
---------------------
Jan.Kowalski@praca.pl
Anna.Nowak@praca.pl

Gdybyśmy nie zastosowali funkcji text() na końcu wyrażenia ścieżkowego, tzn. gdybyśmy napisali:

SELECT w.Karta.Extract('/KARTA/EMAIL').GetStringVal() "EMail"
FROM Kontakty w;
otrzymalibyśmy następującą kolumnę wartości typu VARCHAR2:
EMail
------------------------------------
<EMAIL>Jan.Kowalski@praca.pl</EMAIL>
<EMAIL>Anna.Nowak@praca.pl</EMAIL>
W szczególności
SELECT w.Nazwisko, w.Karta.Extract('/KARTA').GetStringval() AS "KARTA"
FROM Kontakty w;
wypisuje pełne teksty dokumentów XML traktując je jako wartości VARCHAR2, podobnie jak:
SELECT w.Nazwisko, w.Karta.GetStringval() AS "KARTA"
FROM Kontakty w;

Gdy chcemy wziąć cały dokument XML i przesłać go jako duży dokument tekstowy typu CLOB, piszemy:

SELECT w.Karta.GetClobval() as KartaKow
FROM Kontakty w
WHERE w.Nazwisko = 'KOWALSKI';

Aby sprawdzić czy w dokumencie XML występuje element <WWW> stosujemy metodę ExistsNode:

SELECT w.Nazwisko, w.Karta.ExistsNode('/KARTA/WWW') "Ma stronę WWW"
FROM Kontakty w
WHERE w.Karta IS NOT NULL;

Wynik:

NAZWISKO      Ma stronę WWW
----------- ---------------
KOWALSKI                  0
NOWAK                     1

Zauważmy, że gdybyśmy opuścili warunek w.Karta IS NOT NULL, w wyniku znalazłyby się nazwiska wszystkich osób. Dla tych, które nie mają przyporządkowanej karty, otrzymalibyśmy NULL w kolumnie Karta, co sygnalizowałoby niemożliwość zastosowania metody ExistsNode do nieokreślonego obiektu (NULL).

Gdy chcemy wziąć adresy osób, których adres elektroniczny to "Jan.Kowalski@praca.pl" napiszemy:

SELECT w.Nazwisko, w.Karta.Extract('/KARTA/ADRES').GetStringval() AS "Adres"
FROM Kontakty w
WHERE w.Karta.ExistsNode('/KARTA[EMAIL="Jan.Kowalski@praca.pl"]') = 1;

Następujące zapytanie jest podobne, ale nie liczy dokładnie tego samego co poprzednio. Dlaczego?

SELECT w.Nazwisko, w.Karta.Extract('/KARTA[EMAIL="Jan.Kowalski@praca.pl"]/ADRES').GetStringval() AS "Adres"
FROM Kontakty w;

Aktualizacja kolumny XMLType

Jedna metoda polega na zastąpieniu jednego dokumentu innym:

UPDATE Kontakty w
SET w.Karta =
     XMLType('<KARTA>
             <EMAIL>Jan.Kowalski@praca.pl</EMAIL>
             <EMAIL>JanKow@wp.pl</EMAIL>
             <TEL_PRACA>33-456</TEL_PRACA>
             <TEL_DOM>28-991</TEL_DOM>
             <TEL_KOM>600-345</TEL_KOM>
             <ADRES>
                <ULICA>Wygodna 9m1</ULICA>
                <MIASTO>Warszawa</MIASTO>
                <KOD>02-782</KOD>
             </ADRES>
           </KARTA>')
WHERE w.Nazwisko='KOWALSKI';

Druga metoda polega na zastosowaniu funkcji UpdateXML albo w odniesieniu do stałej wartości:

UPDATE Kontakty w
SET w.Karta = UpdateXML(w.Karta,
'/KARTA[EMAIL="Jan.Kowalski@praca.pl"]/TEL_DOM/text()',
'9999-10')
WHERE w.Nazwisko='KOWALSKI';

albo w odniesieniu do całego elementu:

UPDATE Kontakty w
SET w.Karta = UpdateXML(w.Karta,'/KARTA[EMAIL="Jan.Kowalski@praca.pl"]/ADRES',
      XMLType('<ADRES>
                <ULICA>Aksamitna 90m10</ULICA>
                <MIASTO>Warszawa</MIASTO>
                <KOD>12-782</KOD>
             </ADRES>'))
WHERE w.Nazwisko='KOWALSKI';
W szczególności można tą metodą usuwać fragmenty dokumentów XML. Odwołując się do naszego przykładu możemy chcieć usunąć element zawierający kod adresu Kowalskiego. Następująca instrukcja:
UPDATE Kontakty w
SET w.Karta =  UpdateXML(w.Karta,'/KARTA[EMAIL="Jan.Kowalski@praca.pl"]/ADRES/KOD', NULL)
WHERE w.Nazwisko='KOWALSKI';
usunie kod z adresu Kowalskiego a dokładniej zastąpi element KOD przez pusty element <KOD/>. Aby usunąć cały dokument piszemy:
UPDATE Kontakty w
SET w.Karta = NULL
WHERE w.Nazwisko='KOWALSKI';

Usuwanie wiersza zawierającego kolumnę typu XMLType

Aby usunąć wszystkie wiersze, w których Karta nie zawiera elementu ADRES, piszemy:

DELETE FROM Kontakty w
WHERE w.Karta.ExistsNode('/KARTA/ADRES') = 0;

Indeksy funkcyjne na kolumnach typu XMLType

W celu przyśpieszenia wykonywania zapytania

SELECT * FROM Kontakty w WHERE w.Karta.Extract('/KARTA/ADRES/MIASTO/text()').GetStringVal()= 'WARSZAWA';
indeksuje się zawartości elementu MIASTO w dokumentach XML kolumny Karta. Następująca instrukcja tworzy indeks dla wartości elementu MIASTO:
CREATE INDEX Miasto_index ON Kontakty w (w.Karta.Extract('/KARTA/ADRES/MIASTO/text()').GetStringVal());
Zapytanie SQL użyje tego indeksu funkcyjnego, zamiast czytać dokumenty XML wiersz po wierszu i obliczać wartości wyrażeń XPath.

Ponadto w Oracle są dostępne konstrukcje, których nie omawiamy na wykładzie:

  1. XML Schema - poprawność dokumentów XML - pakiet DBMS_XMLSCHEMA;
  2. Konwersja: dane relacyjne -> XML - pakiet DBMS_XMLGEN;
  3. Transformacja XSL - metoda Transform obiektu XMLType (argumentem metody jest obiekt typu XMLType reprezentujący dokument .xsl).

Więcej informacji można znaleźć w dokumentacji Oracle w szczególności w: Oracle9i XML API Reference - XDK and Oracle XML DB.

 


6.5 Podsumowanie

W czasie tego wykładu przedstawiono problematykę związaną z językiem XML ze szczególnym uwzględnieniem rozszerzeń obiektowo-relacyjnych baz danych o udogodnienia do przetwarzania dokumentów XML.

Przedstawiono dwie podstawowe metody przechowywania dokumentów XML w obiektowo-relacyjnej bazie danych:

Pokazano proste konstrukcje programistyczne PL/SQL wspomagające definiowanie, przechowywanie i przetwarzanie dokumentów XML.

Reasumując, do obsługi dokumentów XML w bazie danych wystarczają wprowadzone wcześniej pojęcia jak typy obiektowe, duże obiekty CLOB oraz oczywiście język PL/SQL.

Zatem z jednej strony, dokumenty XML można reprezentować w relacyjnej i w obiektowo-relacyjnej bazie danych, ale również na odwrót, dane relacyjne i obiektowo-relacyjne dają się w sposób naturalny reprezentować jako dane XML. Zachodzi więc pytanie. Skoro do komunikacji używamy danych w postaci XML to może, zamiast za każdym razem dokonywać konwersji, po prostu korzystać wyłącznie z baz danych XML, które są ogólniejsze niż bazy relacyjne i obiektowo-relacyjne?

 


6.6 Słownik pojęć

XML - język dokumentów oparty na znacznikach, którego celem jest ułatwienie komunikacji i przekazywania danych w sieci między aplikacjami, bazami danych i ludźmi.

XMLType - typ obiektowy umożliwiający reprezentowanie, przechowywanie i przekształcanie dokumentów XML w obiektowo-relacyjnej bazie danych Oracle osłaniający programistę bazy danych przed szczegółami implementacyjnymi.

 


6.7 Sprawdzenie wiedzy

  1. Jakie są podstawowe zastosowania języka XML:

    zamieszczanie metadanych w dokumentach tekstowych/multimedialnych,
    programowanie formularzy webowych,
    oddzielenie prezentacji od struktury dokumentu,
    wymiana danych biznesowych między aplikacjami.

  2. Do czego służą typy danych ID, IDREF, IDREFS? Odpowiedź
  3. Jakie struktury danych wspomagają wyszukiwania w dokumentach tekstowych za pomocą zapytań boolowskich i rankingowych? Odpowiedź
  4. Jakie są metody przechowywania dokumentów XML w obiektowo-relacyjnej bazie danych? Odpowiedź
  5. Jakiego rodzaju typem danych jest typ XMLType i w jaki sposób są w nim reprezentowane dokumenty XML? Odpowiedź

 


6.8 Zadania

1. Wykonaj przykłady z wykładu dotyczące XMLType.

2. Utwórz bazę danych przyjmowania zamówień (Klienci, Faktury, Pozycje, Towary). Może to być baza relacyjna albo obiektowo-relacyjna. Napisz aplikację przyjmowania zamówień i wystawiania faktur, za pomocą XML.

Klient składa zamówienie (na jeden towar) przysyłając dokument XML:

<Zamawiam>
  <Klient>
    <Imie>Jan</Imie>
    <Nazwisko>Kowalski</Nazwisko>
    <Adres>Cicha 5m1, 08099 Warszawa</Adres>
  </Klient>
  <Pozycja>
      <Towar>Cukier</Towar>
      <Ilość>4</Ilość>
      <Miara>kg</Miara>
  </Pozycja>
</Zamawiam>
Aplikacja ma sprawdzić zamówienie, zapisać w bazie danych i wysłać fakturę w postaci dokumentu XML:
<Faktura>
    <Klient>
      <Imie>Jan</Imie>
      <Nazwisko>Kowalski</Nazwisko>
      <Adres>Cicha 5m1, 08099 Warszawa</Adres>
    </Klient>
    <Zakup>
        <Towar>Cukier</Towar>
        <Ilość>4</Ilość>
        <Miara>kg</Miara>
        <Cena_jedn>2</Cena_jedn>
        <Cena>8</Cena>
    </Zakup>
</Faktura>

3. Zaproponuj sposób zapisywania zamówień jako dokumentów XML w kolumnie tabeli Klienci.
 



Strona przygotowana przez Lecha Banachowskiego. Ostatnia aktualizacja - 06/17/07