Anda memiliki tiga tingkat tabel bersarang.
Contoh data:
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');
Metode 1:Lakukan join kiri, tangani XML di klien
Cara paling sederhana untuk menangani ini adalah dengan melakukan join kiri pada ketiga tabel, diurutkan dari terluar ke terdalam. Kemudian Anda mengulangi set hasil, menutup satu elemen dan membuka yang lain setiap kali subjek pada tingkat itu berubah.
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;
lalu ulangi baris yang dikembalikan, dan untuk setiap baris, 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;
Untuk menulis XML, Anda akan menggunakan sesuatu seperti XMLWriter
. Untuk membaca data kueri, Anda dapat menggunakan sesuatu seperti PDO atau driver apa pun yang Anda inginkan. Jika kumpulan data besar, pertimbangkan untuk menggunakan kursor untuk membaca data.
Ini bekerja dengan baik, tetapi mentransfer banyak kelebihan data, karena Anda mentransfer n
salinan data tabel luar untuk setiap n
baris tabel bagian dalam yang terkait dengannya.
Untuk mengurangi kelebihan data yang dipertukarkan, Anda hanya dapat memilih ID untuk tabel luar
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;
... kemudian ketika Anda beralih ke tablea / tableb baru, SELECT
sisa barisnya kemudian. Anda mungkin akan menggunakan koneksi kedua untuk melakukan ini sehingga Anda tidak mengganggu set hasil dan status kursor pada koneksi utama tempat Anda membaca baris.
Metode 2:Lakukan semuanya di PostgreSQL
Untuk kumpulan data yang lebih kecil, atau untuk tingkat bagian dalam kumpulan data yang lebih besar, Anda dapat menggunakan dukungan XML PostgreSQL untuk menyusun dokumen XML, mis.:
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;
... tapi sungguh, Anda harus menjadi semacam masokis untuk menulis kode seperti itu. Meskipun itu bisa terbukti cukup cepat.
Untuk memahami kueri Anda harus membaca dokumen XML PostgreSQL . Sintaks aneh diimpikan oleh komite SQL/XML, jangan salahkan kami.
Perhatikan juga bahwa variabel baris banyak digunakan dalam kode di atas agar tetap teratur. a
, b
dan c
dilewatkan sebagai seluruh baris ke lapisan luar kueri. Ini menghindari kebutuhan untuk mengacaukan alias ketika nama bertabrakan. Sintaks (a).a_id
, dll, berarti "a_id
bidang variabel baris a
". Lihat manual PostgreSQL untuk detailnya.
Di atas menggunakan struktur XML yang lebih baik (lihat komentar di bawah). Jika Anda ingin memancarkan atribut bukan elemen, Anda dapat mengubah XMLFOREST
panggilan ke XMLATTRIBUTES
panggilan.
Keluaran:
<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>
atau, dicetak cantik:
<?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>
Harap pancarkan XML yang lebih baik
Di samping catatan, menggunakan atribut seperti itu dalam XML tampaknya menggoda, tetapi dengan cepat menjadi sulit dan jelek untuk digunakan. Harap gunakan elemen XML biasa:
<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>