私はからにPostgresqlに移動しています。いくつかのOracle階層クエリをPostgresに変換しようとしています。たとえば、Oracleにカンマ区切りを返す(すなわち、子供)の下で、すべてのIDのリストを命じたと、私は次の操作を行いますid_to_start_with
含む:ポストグルで階層的な親クエリ
SELECT LISTAGG(id_something, ',') WITHIN GROUP (ORDER BY id_something) AS somethings FROM(
SELECT DISTINCT D.id_something
FROM something_table D
START WITH D.id_something = :id_to_start_with
CONNECT BY D.id_something_parent = PRIOR D.id_something
)
Postgresの中に相当すると思われる。
同様にWITH RECURSIVE the_somethings(id_something) AS (
SELECT id_something
FROM something_table
WHERE id_something = $id_to_start_with
UNION ALL
SELECT D.id_something
FROM the_somethings DR
JOIN something_table D ON DR.id_something = D.id_something_parent
)
SELECT string_agg(temp_somethings.id_something::TEXT, ',') AS somethings
FROM (
SELECT id_something
FROM the_somethings
ORDER BY id_something
) AS temp_somethings
私はカンマ区切りを返したい場合(すなわち、親)上記のすべてのIDのリストを命じたと私は、Oracleで次の操作を行いますid_to_start_with
含む:
SELECT LISTAGG(id_something, ',') WITHIN GROUP (ORDER BY id_something) AS somethings FROM(
SELECT DISTINCT D.id_something
FROM something_table D
START WITH D.id_something = :id_to_start_with
CONNECT BY D.id_something = PRIOR D.id_something_parent
)
Postgresので同等のように見えるでしょう:
WITH RECURSIVE the_somethings(id_something, path) AS (
SELECT id_something
, id_something::TEXT as path
FROM something_table
WHERE id_something_parent IS NULL
UNION ALL
SELECT D.id_something
, (DR.path || ',' || D.id_something::TEXT) as path
FROM something_table D
JOIN the_somethings DR ON DR.id_something = D.id_something_parent
)
SELECT path
FROM the_somethings
WHERE id_something = $id_to_start_with
ORDER BY id_something
私の質問は、最後のPostgresのクエリに関係しています。私にとっては非常に非効率的だと思うし、それを書く良い方法があるのだろうかと思う。つまり、Oracleでは、クエリはid_to_start_with
の親を検索し、次に親の親などをルートに検索します。
一方、Postgresクエリはすべての単一ルートから子パスへの組み合わせを取得し、私が探しているid_to_start_with
の1つのルートを除いてすべてをスローします。それは、潜在的に私が探している行を除いてすべてを投げ捨てるために作成するデータのトンです。
特定のid_to_start_with
のすべての親のコンマ区切りリストを取得する方法はありますか?これはPostgresでOracleと同じように機能しますか?
編集:OracleとPostgresからのExplainプランを追加する。
Postgresはこれは以下のヤクブの答えに基づいて、最終的なクエリである
CTE Scan on the_somethings (cost=62.27..74.66 rows=3 width=76) (actual time=0.361..0.572 rows=1 loops=1)
Filter: (id_something = 1047)
Rows Removed by Filter: 82
CTE the_somethings
-> Recursive Union (cost=0.00..62.27 rows=551 width=76) (actual time=0.026..0.433 rows=83 loops=1)
-> Seq Scan on something_table (cost=0.00..2.83 rows=1 width=8) (actual time=0.023..0.034 rows=1 loops=1)
Filter: (id_something_parent IS NULL)
Rows Removed by Filter: 82
-> Hash Join (cost=0.33..4.84 rows=55 width=76) (actual time=0.028..0.065 rows=16 loops=5)
Hash Cond: (d.id_something_parent = dr.id_something)
-> Seq Scan on something_table d (cost=0.00..2.83 rows=83 width=16) (actual time=0.002..0.012 rows=83 loops=5)
-> Hash (cost=0.20..0.20 rows=10 width=76) (actual time=0.009..0.009 rows=17 loops=5)
Buckets: 1024 Batches: 1 Memory Usage: 10kB
-> WorkTable Scan on the_somethings dr (cost=0.00..0.20 rows=10 width=76) (actual time=0.001..0.004 rows=17 loops=5)
Planning time: 0.407 ms
Execution time: 0.652 ms
Analyyze出力を説明します。 PostgreSQLの
WITH RECURSIVE the_somethings(id_something, path, level, orig_id, id_something_parent) AS (
SELECT id_something
, id_something::TEXT as path
, 0 as level
, id_something AS orig_id
, id_something_parent
FROM something_table
WHERE id_something IN (1047, 448)
UNION ALL
SELECT D.id_something
, (D.id_something::TEXT || ',' || DR.path) as path
, DR.level + 1 as level
, DR.orig_id as orig_id
, D.id_something_parent
FROM something_table D
JOIN the_somethings DR ON D.id_something = DR.id_something_parent
)
SELECT DISTINCT ON(orig_id) orig_id, path
FROM the_somethings
ORDER BY orig_id, level DESC
;
'FROM the_somethings DR JOIN something_table D'から、あなたが記述したように見えます。しかし、私はそれがクエリプランを変更するとは思わない、私はプランナーはすでにあなたが望むことを行うと思います。 – pozs
私はまだPostgresの説明計画を読む方法を知らないかもしれませんが、再帰的連合(cost = 0.00..62.27 rows = 551 width = 76)のような行が表示されたら、551行で作業していると仮定します。私がOracle explain planを見ると、最大4行のデータ(id_to_start_withとその親行)でしか動作しません。 – irrational
これは単純なEXPLAIN(見積もり情報を表示)です。実際のデータ使用のために 'EXPLAIN ANALYZE'を実行してください。 – pozs