0

同じ集約関数の組み合わせを使用するクエリが頻繁にあります。例えば。PostgreSQLで集計を使用して式を再利用する方法

SELECT 
    my_id, 
    sum(a * weight)/nullif(sum(CASE WHEN a IS NOT NULL THEN weight END), 0) AS a, 
    sum(b * weight)/nullif(sum(CASE WHEN b IS NOT NULL THEN weight END), 0) AS b 
FROM my_table 
GROUP BY my_id 

同じ表現を何度も繰り返してはいけません。新しい機能weighted_avgと同じ結果を得るために素晴らしいことだ:

SELECT 
    my_id, 
    weighted_avg(a, weight) AS a, 
    weighted_avg(b, weight) AS b 
FROM my_table 
GROUP BY my_id 

私が知っている、これを行うための唯一の方法は、中間状態とCREATE AGGREGATEと行ごとに呼び出されますSFUNCを使用することです。残念ながら、これは元のクエリよりもはるかに遅く、私の場合は使用できなくなります。

私は私の理想的なソリューションが

CREATE AGGREGATE FUNCTION weighted_avg(x float, weight float) 
RETURNS float AS $$ 
    SELECT sum(x * weight)/nullif(sum(CASE WHEN x IS NOT NULL THEN weight END), 0) 
$$ language SQL IMMUTABLE; 

のように見えるし、クエリの実行時にインラインだろうと想像。しかし、私はPostgresでサポートされているものは見つけられません。

+1

機能の使用は、おそらく常に少しになるだろう元のコードの式を使用するよりも遅くなります。 –

+0

私はいくつかのオーバーヘッドで大丈夫ですが、 'CREATE AGGREGATE'のplpgsql実装では私の場合には4倍の時間がかかります。だから私は受け入れられる元の表現を維持するだろうが、私はより良い解決策を望んでいた。 –

+0

'FROM'のサブクエリを使用して、入力式を1回計算します。 –

答えて

0

テスト済みの集計関数が表示されませんでした。これは私がそれを作成する方法を次のとおりです。

create function weighted_avg_acumm (fa float[], x float, weight float) 
returns float[] as $$ 
    select array[ 
     fa[1] + x * weight, 
     fa[2] + weight 
    ]::float[] 
$$ language sql immutable strict; 

create function weighted_avg_acumm_final (fa float[]) 
returns float as $$ 
    select fa[1]/fa[2] 
$$ language sql immutable strict; 

create aggregate weighted_avg (x float, weight float)(
    sfunc = weighted_avg_acumm, 
    finalfunc = weighted_avg_acumm_final, 
    stype = float[], 
    initcond = '{0,0}' 
); 

更新

私がテストし、それは私にとっても非常に遅い:

create table t (a int, weight int); 
insert into t (a, weight) 
select 
    nullif(round(random() * 10), 0), 
    trunc(random() * 10) + 1 
from generate_series(1,1000000) 
; 

explain analyze 
select weighted_avg(a, weight) 
from t; 
                QUERY PLAN              
------------------------------------------------------------------------------------------------------------------- 
Aggregate (cost=269425.25..269425.26 rows=1 width=8) (actual time=7933.440..7933.440 rows=1 loops=1) 
    -> Seq Scan on t (cost=0.00..14425.00 rows=1000000 width=8) (actual time=0.018..241.571 rows=1000000 loops=1) 
Planning time: 0.189 ms 
Execution time: 7933.508 ms 

explain analyze 
select 
    sum(a::numeric * weight)/
    nullif(sum(case when a is not null then weight end), 0) 
from t; 
                QUERY PLAN              
------------------------------------------------------------------------------------------------------------------- 
Aggregate (cost=26925.00..26925.02 rows=1 width=8) (actual time=904.852..904.852 rows=1 loops=1) 
    -> Seq Scan on t (cost=0.00..14425.00 rows=1000000 width=8) (actual time=0.010..127.264 rows=1000000 loops=1) 
Planning time: 0.048 ms 
Execution time: 904.891 ms 
+0

ほとんど同じです(いくつかの異なるゼロとNULLの処理)。残念ながら、これはネイティブ表現より約4倍遅いです。 –

関連する問題