Je hebt drie niveaus van geneste tabellen.
Voorbeeldgegevens:
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:Doe een left join, behandel XML in de client
De eenvoudigste manier om dit aan te pakken is om een left join uit te voeren over alle drie de tafels, gerangschikt van buitenste naar binnenste. Vervolgens herhaal je de resultatenset, waarbij je het ene element sluit en het andere opent wanneer het onderwerp op dat niveau verandert.
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;
loop dan over de geretourneerde rijen, en voor elke rij, 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;
Om de XML te schrijven zou je zoiets gebruiken als XMLWriter
. Om de querygegevens te lezen, kunt u iets als PDO gebruiken of een ander stuurprogramma dat u verkiest. Als de dataset groot is, overweeg dan om een cursor te gebruiken om de data te lezen.
Dit werkt goed, maar het draagt veel over van overtollige gegevens, aangezien u n
. overdraagt kopieën van de gegevens van de buitenste tabel voor elke n
rijen van de binnenste tabel die ermee verbonden zijn.
Om de overtollige gegevens die worden uitgewisseld te verminderen, kunt u alleen de ID's voor de buitenste tabellen selecteren
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;
... wanneer u vervolgens overschakelt naar een nieuwe tablea / tableb, SELECT
de rest van zijn rijen dan. U zult hiervoor waarschijnlijk een tweede verbinding gebruiken, zodat u de resultatenset en de cursorstatus niet verstoort op de hoofdverbinding waarvan u rijen leest.
Methode 2:Doe het allemaal in PostgreSQL
Voor kleinere datasets, of voor de innerlijke niveaus van grotere datasets, kunt u de XML-ondersteuning van PostgreSQL gebruiken om de XML-documenten te construeren, bijvoorbeeld:
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;
... maar echt, je moet een soort masochist zijn om zo'n code te schrijven. Hoewel het redelijk snel zou kunnen blijken te zijn.
Om de query te begrijpen, moet u de PostgreSQL XML-documenten lezen . De maffe syntaxis is bedacht door de SQL/XML-commissie, neem het ons niet kwalijk.
Merk ook op dat rijvariabelen worden veel gebruikt in de bovenstaande code om het georganiseerd te houden. a
, b
en c
worden als hele rijen doorgegeven aan de buitenste lagen van de query. Dit vermijdt de noodzaak om met aliassen te knoeien wanneer namen botsen. De syntaxis (a).a_id
, etc, betekent "de a_id
veld van de rijvariabele a
". Zie de PostgreSQL-handleiding voor details.
Het bovenstaande maakt gebruik van een betere XML-structuur (zie opmerkingen hieronder). Als u attributen en geen elementen wilt uitzenden, kunt u de XMLFOREST
. wijzigen oproepen naar XMLATTRIBUTES
oproepen.
Uitgang:
<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>
of, mooi bedrukt:
<?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>
Stuur een betere XML uit
Even terzijde, het gebruik van dergelijke attributen in XML lijkt verleidelijk, maar het wordt al snel moeilijk en lelijk om mee te werken. Gebruik gewoon normale XML-elementen:
<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>