ネストされたテーブルには3つのレベルがあります。
サンプルデータ:
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');
方法1:左結合を実行し、クライアントでXMLを処理します
これを処理する最も簡単な方法は、3つのテーブルすべてに対して、最も外側から最も内側の順に左結合を実行することです。次に、結果セットを繰り返し処理し、そのレベルのサブジェクトが変更されるたびに1つの要素を閉じ、別の要素を開きます。
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;
次に、返された行をループし、各行について擬似コード :
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;
XMLを作成するには、 XMLWriter
のようなものを使用します。
。クエリデータを読み取るには、PDOなどの任意のドライバーを使用できます。データセットが大きい場合は、カーソルを使用してデータを読み取ることを検討してください。
これはうまく機能しますが、ロットを転送します n
を転送するため、余分なデータの 各n
の外部テーブルのデータのコピー それに関連付けられた内部テーブルの行。
交換される余分なデータを減らすために、外部テーブルのIDのみを選択できます
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;
...次に、新しいtablea / tablebに切り替えると、SELECT
その後、残りの行。行を読み取っているメイン接続の結果セットとカーソルの状態を中断しないように、おそらく2番目の接続を使用してこれを行います。
方法2:すべてをPostgreSQLで実行する
小さいデータセットの場合、または大きいデータセットの内部レベルの場合は、PostgreSQLのXMLサポートを使用してXMLドキュメントを作成できます(例:
)。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;
...しかし、実際には、そのようなコードを書くには、ある種のマゾヒストでなければなりません。かなり速いことがわかるかもしれませんが。
クエリを理解するには、PostgreSQLXMLドキュメントを読む必要があります 。奇抜な構文はSQL/XML委員会によって考案されました。私たちを責めないでください。
行変数にも注意してください 上記のコードでは、整理するために頻繁に使用されています。 a
、b
およびc
行全体としてクエリの外側のレイヤーに渡されます。これにより、名前が衝突したときにエイリアスをいじる必要がなくなります。構文(a).a_id
、などは「a_id
」を意味します 行変数a
のフィールド "。詳細については、PostgreSQLのマニュアルを参照してください。
上記はより良いXML構造を使用しています(以下のコメントを参照)。要素ではなく属性を出力する場合は、XMLFOREST
を変更できます。 XMLATTRIBUTES
の呼び出し 呼び出します。
出力:
<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>
または、きれいに印刷されています:
<?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>
より良いXMLを出力してください
ちなみに、XMLでそのような属性を使用することは魅力的ですが、操作がすぐに難しく、醜くなります。通常の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>