祖先の子孫の深さを計算する必要があります。レコードにがある場合、レコードはobject_id = parent_id = ancestor_id
ルートノード(祖先)と見なされます。WITH RECURSIVE
PostgreSQL 9.4でクエリを実行しようとしています。
データや列を制御しません。データおよびテーブルスキーマは外部ソースから取得されます。テーブルは継続的に成長しています。現在、1日あたり約3万件の記録があります。ツリー内のノードは欠落している可能性があり、ある時点で外部ソースからプルされます。彼らは通常引き込まれますcreated_at DESC
順番にますが、データは非同期のバックグラウンドジョブでプルされます。
最初はこの問題に対するコードソリューションがありましたが、現在は500万行以上あり、完了するまでに約30分かかります。
テーブル定義とテストデータの例:
CREATE TABLE objects (
id serial NOT NULL PRIMARY KEY,
customer_id integer NOT NULL,
object_id integer NOT NULL,
parent_id integer,
ancestor_id integer,
generation integer NOT NULL DEFAULT 0
);
INSERT INTO objects(id, customer_id , object_id, parent_id, ancestor_id, generation)
VALUES (2, 1, 2, 1, 1, -1), --no parent yet
(3, 2, 3, 3, 3, -1), --root node
(4, 2, 4, 3, 3, -1), --depth 1
(5, 2, 5, 4, 3, -1), --depth 2
(6, 2, 6, 5, 3, -1), --depth 3
(7, 1, 7, 7, 7, -1), --root node
(8, 1, 8, 7, 7, -1), --depth 1
(9, 1, 9, 8, 7, -1); --depth 2
object_id
は一意ではありませんが、組み合わせ(customer_id, object_id)
は一意であることに注意してください。
次のようなクエリを実行します。
WITH RECURSIVE descendants(id, customer_id, object_id, parent_id, ancestor_id, depth) AS (
SELECT id, customer_id, object_id, parent_id, ancestor_id, 0
FROM objects
WHERE object_id = parent_id
UNION
SELECT o.id, o.customer_id, o.object_id, o.parent_id, o.ancestor_id, d.depth + 1
FROM objects o
INNER JOIN descendants d ON d.parent_id = o.object_id
WHERE
d.id <> o.id
AND
d.customer_id = o.customer_id
) SELECT * FROM descendants d;
generation
計算された深さとして列を設定したいと思います。新しいレコードが追加されると、生成列は-1に設定されます。parent_id
がまだプルされていない場合があります。もしparent_id
が存在しない、生成列を-1のままにしておく必要があります。
最終データは次のようになります。
id | customer_id | object_id | parent_id | ancestor_id | generation
2 1 2 1 1 -1
3 2 3 3 3 0
4 2 4 3 3 1
5 2 5 4 3 2
6 2 6 5 3 3
7 1 7 7 7 0
8 1 8 7 7 1
9 1 9 8 7 2
クエリの結果は、生成列を正しい深さに更新することです。
SOに関するこの関連する質問への回答から作業を始めました。
update
再帰CTEの結果を表にしたいですか?