Sie haben drei Ebenen verschachtelter Tabellen.
Beispieldaten:
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');
Methode 1:Linksverknüpfung durchführen, XML im Client verarbeiten
Der einfachste Weg, dies zu handhaben, besteht darin, eine Linksverknüpfung über alle drei Tabellen durchzuführen, geordnet von der äußersten zur innersten. Dann iterieren Sie die Ergebnismenge nach unten, schließen ein Element und öffnen ein anderes, wenn sich das Thema auf dieser Ebene ändert.
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;
Schleifen Sie dann die zurückgegebenen Zeilen und für jede Zeile Pseudocode :
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;
Um das XML zu schreiben, würden Sie so etwas wie XMLWriter
verwenden
. Um die Abfragedaten zu lesen, können Sie so etwas wie PDO oder einen beliebigen Treiber verwenden. Wenn der Datensatz groß ist, erwägen Sie die Verwendung eines Cursors zum Lesen der Daten.
Das funktioniert gut, aber es wird viel übertragen von überschüssigen Daten, da Sie n
übertragen Kopien der Daten der äußeren Tabelle für jeden n
Zeilen der zugehörigen inneren Tabelle.
Um den Datenüberschuss zu reduzieren, können Sie nur die IDs für die äußeren Tabellen auswählen
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;
... dann, wenn Sie zu einer neuen Tablea / Tableb wechseln, SELECT
den Rest seiner Zeilen dann. Sie werden wahrscheinlich eine zweite Verbindung verwenden, um dies zu tun, damit Sie die Ergebnismenge und den Cursorstatus auf der Hauptverbindung, von der Sie Zeilen lesen, nicht stören.
Methode 2:Machen Sie alles in PostgreSQL
Für kleinere Datensätze oder für die inneren Ebenen größerer Datensätze können Sie die XML-Unterstützung von PostgreSQL verwenden, um die XML-Dokumente zu erstellen, z. B.:
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;
... aber wirklich, man muss eine Art Masochist sein, um solchen Code zu schreiben. Obwohl es sich als ziemlich schnell erweisen könnte.
Um die Abfrage zu verstehen müssen Sie die PostgreSQL XML-Dokumentation lesen . Die verrückte Syntax wurde vom SQL/XML-Komitee erfunden, machen Sie uns keine Vorwürfe.
Beachten Sie auch, dass row-variables werden im obigen Code stark verwendet, um ihn organisiert zu halten. a
, b
und c
werden als ganze Zeilen an äußere Schichten der Abfrage übergeben. Dies vermeidet die Notwendigkeit, mit Aliasnamen herumzuspielen, wenn Namen kollidieren. Die Syntax (a).a_id
usw. bedeutet "die a_id
Feld der Zeilenvariable a
". Einzelheiten finden Sie im PostgreSQL-Handbuch.
Das obige verwendet eine bessere XML-Struktur (siehe Kommentare unten). Wenn Sie Attribute statt Elemente ausgeben möchten, können Sie den XMLFOREST
ändern Aufrufe von XMLATTRIBUTES
Anrufe.
Ausgabe:
<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>
oder schön gedruckt:
<?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>
Bitte geben Sie besseres XML aus
Nebenbei bemerkt, die Verwendung solcher Attribute in XML scheint verlockend, aber es wird schnell schwierig und hässlich, damit zu arbeiten. Bitte verwenden Sie einfach normale XML-Elemente:
<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>