61

Postgres に次のようなテーブルがあります。

# select * from p;
 id | value 
----+-------
  1 |   100
  2 |      
  3 |      
  4 |      
  5 |      
  6 |      
  7 |      
  8 |   200
  9 |          
(9 rows)

そして、次のようにクエリを実行したいと思います。

# select * from p;
 id | value | new_value
----+-------+----------
  1 |   100 |    
  2 |       |    100
  3 |       |    100
  4 |       |    100
  5 |       |    100
  6 |       |    100
  7 |       |    100
  8 |   200 |    100
  9 |       |    200
(9 rows)

選択のサブクエリでこれを既に実行できますが、実際のデータには20k以上の行があり、非常に遅くなります。

ウィンドウ関数でこれを行うことは可能ですか? lag() を使用したいのですが、IGNORE NULLSオプションをサポートしていないようです。

select id, value, lag(value, 1) over (order by id) as new_value from p;
 id | value | new_value
----+-------+-----------
  1 |   100 |      
  2 |       |       100
  3 |       |      
  4 |       |
  5 |       |
  6 |       |
  7 |       |
  8 |   200 |
  9 |       |       200
(9 rows)
4

7 に答える 7

130

Postgresでも機能するSQL Serverのこの回答を見つけました。今までやったことがなかったので、そのテクニックはかなり賢いと思いました。基本的に、彼は入れ子になったクエリ内で case ステートメントを使用して、ウィンドウ関数用のカスタム パーティションを作成します。値が null でない場合は合計をインクリメントし、それ以外の場合はそのままにしておきます。これにより、すべてのヌル セクションを前の非ヌル値と同じ番号で区切ることができます。クエリは次のとおりです。

SELECT
  id, value, value_partition, first_value(value) over (partition by value_partition order by id)
FROM (
  SELECT
    id,
    value,
    sum(case when value is null then 0 else 1 end) over (order by id) as value_partition

  FROM p
  ORDER BY id ASC
) as q

そして結果:

 id | value | value_partition | first_value
----+-------+-----------------+-------------
  1 |   100 |               1 |         100
  2 |       |               1 |         100
  3 |       |               1 |         100
  4 |       |               1 |         100
  5 |       |               1 |         100
  6 |       |               1 |         100
  7 |       |               1 |         100
  8 |   200 |               2 |         200
  9 |       |               2 |         200
(9 rows)
于 2013-09-25T18:19:43.893 に答える