次のようなCTEを返すクエリがあります
+-----------+-------------+
| node_id | ancestors |
|-----------+-------------|
| 1 | [] |
| 2 | [] |
| 3 | [1] |
| 4 | [2] |
| 5 | [4, 2] |
+-----------+-------------+
私がやりたいのは、nodes
テーブルと結合し、ancestors
列にあるIDをnodes
テーブルの別の列に置き換えることです。これまでの私のクエリは次のとおりです。
WITH RECURSIVE tree AS (
-- snip --
)
SELECT node.entity_id AS id,
array_remove(array_agg(parent_nodes.entity_id), NULL) AS ancestors
FROM tree
JOIN entity.nodes AS node ON node.id = tree.node_id
LEFT OUTER JOIN entity.nodes AS parent_nodes ON parent_nodes.id = ANY(tree.ancestors)
GROUP BY node.id;
このクエリの問題は、元のancestors
配列の順序が失われることです。 array_agg
関数で元の順序を維持しながら結合を実行する方法はありますか?
クエリの問題は、結合条件id = ANY(ancestors)
です。元の順序が保持されないだけでなく、配列内の重複する要素が排除されます。 (id
はancestors
の10個の要素と一致する可能性がありますが、1回だけ選択されます。)クエリのロジックで重複する要素を許可するかどうかはわかりませんが、そうであればかなりわかりますすべてのインスタンスを保持したい-結局のところ「元の順序」を維持したい。
現在のPostgres9.4 +が情報不足であると仮定して、私は完全に異なるアプローチを提案します:
SELECT n.entity_id, p.ancestors
FROM tree t
JOIN nodes n ON n.id = t.node_id
LEFT JOIN LATERAL (
SELECT ARRAY (
SELECT p.entity_id
FROM unnest(t.ancestors) WITH ORDINALITY a(id, ord)
JOIN entity.nodes p USING (id)
ORDER BY ord
) AS ancestors
) p ON true;
クエリは、nodes.id
が主キーとして定義され、nodes.entity_id
も一意である場合にのみ、意図したとおりに機能します。質問に情報がありません。
通常、明示的なORDER BY
なしのこの単純なクエリも機能しますが、保証はありません(Postgres9.3 +)...
SELECT n.entity_id, p.ancestors
FROM tree t
JOIN nodes n ON n.id = t.node_id
LEFT JOIN LATERAL (
SELECT ARRAY (
SELECT p.entity_id
FROM unnest(t.ancestors) id
JOIN entity.nodes p USING (id)
) AS ancestors
) p ON true;
これを安全にすることもできます。詳細な説明:
SQL Fiddle demo for Postgres 9.3。
entity.nodes
に2回参加します-node_id
とancestors
を同様に置き換えます。別の方法は、両方を1つの配列または1つのセットに折りたたみ、1回だけ結合することです。高速かもしれませんが、テストする必要があります。
これらの代替案では、どのような場合でもORDER BY
が必要です:
ネストを解除する前に、node_id
をancestors
配列に追加します...
SELECT p.arr[1] AS entity_id, p.arr[2:2147483647] AS ancestors
FROM tree t
LEFT JOIN LATERAL (
SELECT ARRAY (
SELECT p.entity_id
FROM unnest(t.node_id || t.ancestors) WITH ORDINALITY a(id, ord)
JOIN entity.nodes p USING (id)
ORDER BY ord
) AS arr
) p ON true;
または、結合する前にancestors
のネストされていない要素にnode_id
を追加します...
SELECT p.arr[1] AS entity_id, p.arr[2:2147483647] AS ancestors
FROM tree t
LEFT JOIN LATERAL (
SELECT ARRAY (
SELECT p.entity_id
FROM (
SELECT t.node_id AS id, 0 AS ord
UNION ALL
SELECT * FROM unnest(t.ancestors) WITH ORDINALITY
) x
JOIN entity.nodes p USING (id)
ORDER BY ord
) AS arr
) p ON true;
あなたは私たちのCTEを示さなかった、これはさらに最適化されるかもしれない...