Bạn có ba cấp bảng lồng nhau.
Dữ liệu mẫu:
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');
Phương pháp 1:Thực hiện kết hợp bên trái, xử lý XML trong ứng dụng khách
Cách đơn giản nhất để xử lý điều này là thực hiện phép nối trái trên cả ba bảng, theo thứ tự từ ngoài cùng đến trong cùng. Sau đó, bạn lặp lại tập kết quả, đóng một phần tử này và mở một phần tử khác bất cứ khi nào chủ đề ở cấp đó thay đổi.
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;
sau đó lặp qua các hàng được trả về và đối với mỗi hàng, mã giả :
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;
Để viết XML, bạn sẽ sử dụng một cái gì đó như XMLWriter
. Để đọc dữ liệu truy vấn, bạn có thể sử dụng một cái gì đó như PDO hoặc bất kỳ trình điều khiển nào bạn thích. Nếu tập dữ liệu lớn, hãy cân nhắc sử dụng con trỏ để đọc dữ liệu.
Điều này hoạt động tốt, nhưng nó chuyển nhiều rất nhiều dữ liệu dư thừa, do bạn chuyển n
bản sao dữ liệu của bảng bên ngoài cho mỗi n
các hàng của bảng bên trong được liên kết với nó.
Để giảm bớt dữ liệu thừa được trao đổi, bạn chỉ có thể chọn ID cho các bảng bên ngoài
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;
... thì khi bạn chuyển sang một tablea / tableb mới, hãy SELECT
phần còn lại của các hàng của nó sau đó. Có thể bạn sẽ sử dụng kết nối thứ hai để thực hiện việc này, do đó bạn không làm gián đoạn tập kết quả và trạng thái con trỏ trên kết nối chính mà bạn đang đọc các hàng.
Phương pháp 2:Làm tất cả trong PostgreSQL
Đối với các tập dữ liệu nhỏ hơn hoặc đối với các cấp bên trong của các tập dữ liệu lớn hơn, bạn có thể sử dụng hỗ trợ XML của PostgreSQL để tạo các tài liệu XML, ví dụ:
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;
... nhưng thực sự, bạn phải là một loại khổ dâm nào đó để viết mã như vậy. Mặc dù nó có thể được chứng minh là khá nhanh.
Để hiểu truy vấn bạn sẽ cần đọc tài liệu XML PostgreSQL . Cú pháp kỳ lạ đã được tạo ra bởi ủy ban SQL / XML, đừng đổ lỗi cho chúng tôi.
Cũng lưu ý rằng biến-hàng được sử dụng nhiều trong đoạn mã trên để giữ cho nó có tổ chức. a
, b
và c
được chuyển dưới dạng toàn bộ hàng đến các lớp bên ngoài của truy vấn. Điều này tránh phải nhầm lẫn với các bí danh khi các tên xung đột với nhau. Cú pháp (a).a_id
, v.v., có nghĩa là "a_id
trường của biến hàng a
". Xem hướng dẫn sử dụng PostgreSQL để biết thêm chi tiết.
Phần trên sử dụng cấu trúc XML tốt hơn (xem nhận xét bên dưới). Nếu bạn muốn phát ra thuộc tính không phải là phần tử, bạn có thể thay đổi XMLFOREST
cuộc gọi đến XMLATTRIBUTES
cuộc gọi.
Đầu ra:
<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>
hoặc, in đẹp:
<?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>
Vui lòng tạo ra XML tốt hơn
Một lưu ý nhỏ là, việc sử dụng các thuộc tính như vậy trong XML có vẻ hấp dẫn, nhưng nó nhanh chóng trở nên khó khăn và xấu khi làm việc với nó. Vui lòng chỉ sử dụng các phần tử XML bình thường:
<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>