Masz trzy poziomy zagnieżdżonych tabel.
Przykładowe dane:
CREATE TABLE a(
a_id integer primary key,
name text
);
CREATE TABLE b(
b_id integer primary key,
a_id integer references a(a_id),
val text
);
CREATE TABLE c(
c_id serial primary key,
b_id integer references b(b_id),
blah text
);
INSERT INTO a(a_id, name) VALUES (1, 'fred'),(2, 'bert');
INSERT INTO b(b_id, a_id, val) VALUES
(11, 1, 'x'), (12, 1, 'y'), (21, 2, 'a'), (22, 2, 'b');
INSERT INTO c(b_id, blah) VALUES
(11, 'whatever'), (11, 'gah'), (12, 'borkbork'), (22, 'fuzz');
Metoda 1:Wykonaj lewe sprzężenie, obsługuj XML w kliencie
Najprostszym sposobem poradzenia sobie z tym jest wykonanie lewego sprzężenia dla wszystkich trzech tabel, uporządkowanych od najbardziej zewnętrznego do wewnętrznego. Następnie iterujesz w dół zestawu wyników, zamykając jeden element i otwierając inny, gdy temat na tym poziomie się zmieni.
select *
from a left join b on (a.a_id = b.a_id)
left join c on (b.b_id = c.b_id)
order by a.a_id, b.b_id, c.c_id;
następnie przejdź do zwróconych wierszy, a dla każdego wiersza pseudokod :
cur_row = get_new_row()
if (cur_row[b_id] != prev_row[b_id]) {
emit_close_tableb();
}
if (cur_row[a_id] != prev_row[a_id]) {
emit_close_tablea();
emit_open_tablea(cur_row);
}
if (cur_row[b_id] != prev_row[b_id]) {
emit_open_tableb(cur_row);
}
emit_tablec(cur_row);
prev_row = cur_row;
Aby napisać XML, użyj czegoś takiego jak XMLWriter
. Aby odczytać dane zapytania, możesz użyć czegoś takiego jak PDO lub innego preferowanego sterownika. Jeśli zestaw danych jest duży, rozważ użycie kursora do odczytania danych.
Działa to dobrze, ale przenosi dużo nadmiaru danych, ponieważ przesyłasz n
kopie danych tabeli zewnętrznej dla każdego n
wiersze tabeli wewnętrznej z nią powiązanej.
Aby zmniejszyć nadmiar wymienianych danych, możesz wybrać tylko identyfikatory dla tabel zewnętrznych
select a.a_id, b.b_id, c.*
from a left join b on (a.a_id = b.a_id)
left join c on (b.b_id = c.b_id)
order by a.a_id, b.b_id, c.c_id;
... następnie po przełączeniu na nową tabelęa / tableb, SELECT
pozostałe jego rzędy. Prawdopodobnie użyjesz do tego drugiego połączenia, aby nie zakłócać zestawu wyników i stanu kursora na głównym połączeniu, z którego odczytujesz wiersze.
Metoda 2:Zrób to wszystko w PostgreSQL
W przypadku mniejszych zestawów danych lub wewnętrznych poziomów większych zestawów danych można użyć obsługi XML PostgreSQL do tworzenia dokumentów XML, np.:
WITH xmlinput AS (
SELECT a, b, c
FROM a
LEFT JOIN b ON (a.a_id = b.a_id)
LEFT JOIN c on (b.b_id = c.b_id)
ORDER BY a.a_id, b.b_id, c.c_id
)
SELECT
XMLELEMENT(name items,
xmlagg(
XMLELEMENT(name a,
XMLFOREST((a).a_id AS a_id, (a)."name" AS name),
b_xml
)
ORDER BY (a).a_id)
) AS output
FROM
(
SELECT
a,
xmlagg(
XMLELEMENT(name b,
XMLFOREST((b).b_id AS b_id, (b).val AS val),
c_xml
)
ORDER BY (b).b_id)
AS b_xml
FROM
(
SELECT
a, b,
xmlagg(
XMLELEMENT(name c,
XMLFOREST((c).c_id AS c_id, (c).blah AS blah)
)
ORDER BY (c).c_id)
AS c_xml
FROM xmlinput
GROUP BY a, b
) c_as_xml
GROUP BY a
) b_as_xml;
... ale tak naprawdę, żeby pisać taki kod, trzeba być jakimś masochistą. Chociaż może się to okazać dość szybkie.
Aby zrozumieć zapytanie musisz przeczytać dokumenty XML PostgreSQL . Ta zwariowana składnia została wymyślona przez komisję SQL/XML, nie obwiniajcie nas.
Pamiętaj też, że zmienne wierszowe są intensywnie używane w powyższym kodzie, aby utrzymać porządek. a
, b
i c
są przekazywane jako całe wiersze do zewnętrznych warstw zapytania. Pozwala to uniknąć grzebania w aliasach w przypadku kolizji nazw. Składnia (a).a_id
itd. oznacza „a_id
pole zmiennej wierszowej a
". Więcej informacji znajdziesz w podręczniku PostgreSQL.
Powyższe wykorzystuje lepszą strukturę XML (patrz komentarze poniżej). Jeśli chcesz emitować atrybuty, a nie elementy, możesz zmienić XMLFOREST
wywołania XMLATTRIBUTES
połączeń.
Wyjście:
<items><a><a_id>1</a_id><name>fred</name><b><b_id>11</b_id><val>x</val><c><c_id>1</c_id><blah>whatever</blah></c><c><c_id>2</c_id><blah>gah</blah></c></b><b><b_id>12</b_id><val>y</val><c><c_id>3</c_id><blah>borkbork</blah></c></b></a><a><a_id>2</a_id><name>bert</name><b><b_id>21</b_id><val>a</val><c/></b><b><b_id>22</b_id><val>b</val><c><c_id>4</c_id><blah>fuzz</blah></c></b></a></items>
lub ładnie wydrukowane:
<?xml version="1.0" encoding="utf-16"?>
<items>
<a>
<a_id>1</a_id>
<name>fred</name>
<b>
<b_id>11</b_id>
<val>x</val>
<c>
<c_id>1</c_id>
<blah>whatever</blah>
</c>
<c>
<c_id>2</c_id>
<blah>gah</blah>
</c>
</b>
<b>
<b_id>12</b_id>
<val>y</val>
<c>
<c_id>3</c_id>
<blah>borkbork</blah>
</c>
</b>
</a>
<a>
<a_id>2</a_id>
<name>bert</name>
<b>
<b_id>21</b_id>
<val>a</val>
<c />
</b>
<b>
<b_id>22</b_id>
<val>b</val>
<c>
<c_id>4</c_id>
<blah>fuzz</blah>
</c>
</b>
</a>
</items>
Proszę emitować lepszy kod XML
Na marginesie, używanie takich atrybutów w XML wydaje się kuszące, ale szybko staje się trudne i brzydkie w obsłudze. Proszę używać zwykłych elementów XML:
<Table 1>
<Nr>1</Nr>
<Name>blah</Name>
<Table 2>
<Nr>1</Nr>
<Table 3>
<Col1>42</Col1>
<Col2>...</Col2>
<Col3>...</Col3>
<Col4>...</Col4>
...
</Table 3>
</Table 2>
</Table 1>