2016-05-05 17 views
2

私はから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プランを追加する。

OracleがEXPLAIN PLAN出力 Oracle Plan ScreenShot

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
; 
+0

'FROM the_somethings DR JOIN something_table D'から、あなたが記述したように見えます。しかし、私はそれがクエリプランを変更するとは思わない、私はプランナーはすでにあなたが望むことを行うと思います。 – pozs

+0

私はまだPostgresの説明計画を読む方法を知らないかもしれませんが、再帰的連合(cost = 0.00..62.27 rows = 551 width = 76)のような行が表示されたら、551行で作業していると仮定します。私がOracle explain planを見ると、最大4行のデータ(id_to_start_withとその親行)でしか動作しません。 – irrational

+2

これは単純なEXPLAIN(見積もり情報を表示)です。実際のデータ使用のために 'EXPLAIN ANALYZE'を実行してください。 – pozs

答えて

0

のCTEは、彼らがマテリアライズされるだけにして、外側のクエリからのフィルタが適用されますでしょう意味fencedです。クエリを正しく実行するには、逆の方法でビルドし、フィルタをCTE内に配置します。

WITH RECURSIVE the_somethings(id_something, path) AS (
SELECT id_something 
, id_something::TEXT as path, 0 as level, id_something AS orig_id 
FROM something_table 
WHERE id_something IN ($id_to_start_with,$id_to_start_with2) 
UNION ALL 
SELECT D.id_something 
, (D.id_something::TEXT || ',' || DR.path) as path, DR.level + 1, DR.orig_id 
FROM something_table D 
JOIN the_somethings DR ON DR.id_something_parent = D.id_something 
) 
SELECT DISTINCT ON(orig_id) orig_id, path 
FROM the_somethings 
ORDER BY orig_id, DR.level DESC