SQL:高速累積周波数クエリ(Postgres)
-
10-10-2019 - |
質問
データベースから累積頻度データを取得したいと考えています。私たちが見たすべての一意のステータス更新カウントと、その量のステータス更新を持っているユーザーの数を備えた単純な温度テーブルを作成しました。
Table "pg_temp_4.statuses_count_tmp"
Column | Type | Modifiers
----------------+---------+-----------
statuses_count | integer |
frequency | bigint |
Indexes:
"statuses_count_idx" UNIQUE, btree (statuses_count)
私の現在のクエリは次のとおりです。
select statuses_count, frequency/(select * from total_statuses)::float, (select sum(frequency)/(select * from total_statuses)::float AS percentage from statuses_count_tmp WHERE statuses_count <= SCT.statuses_count) AS cumulative_percent FROM statuses_count_tmp AS SCT ORDER BY statuses_count DESC;
しかし、これにはかなりの時間がかかり、クエリの数は非常に急速に増加します。したがって、私が持っている〜50,000行で、私は読み取られる50kの要因行を見ています。ここに座ってクエリが砕けているのを見ています。
このようなものを手に入れることを望んでいます:
0 0.26975161 0.26975161
1 0.15306534 0.42281695
2 0.05513516 0.47795211
3 0.03050646 0.50845857
4 0.02064444 0.52910301
解決
PostgreSQL 8.4以降があると仮定して、ウィンドウ機能を使用すると解決可能である必要があります。私はそれを推測しています total_statuses
の線に沿ったビューまたは温度テーブルです select sum(frequency) from statuses_count_tmp
?ここでCTEとして書いたので、声明の期間中に結果を一度だけ計算する必要があります。
with total_statuses as (select sum(frequency) from statuses_count_tmp)
select statuses_count,
frequency / (select * from total_statuses) as frequency,
sum(frequency) over(order by statuses_count)
/ (select * from total_statuses) as cumulative_frequency
from statuses_count_tmp
8.4のウィンドウ機能がなければ、最善の策は、単にデータを繰り返し処理することです。
create type cumulative_sum_type as ( statuses_count int, frequency numeric, cumulative_frequency numeric );
create or replace function cumulative_sum() returns setof cumulative_sum_type strict stable language plpgsql as $$
declare
running_total bigint := 0;
total bigint;
data_in record;
data_out cumulative_sum_type;
begin
select sum(frequency) into total from statuses_count_tmp;
for data_in in select statuses_count, frequency from statuses_count_tmp order by statuses_count
loop
data_out.statuses_count := data_in.statuses_count;
running_total := running_total + data_in.frequency;
data_out.frequency = data_in.frequency::numeric / total;
data_out.cumulative_frequency = running_total::numeric / total;
return next data_out;
end loop;
end;
$$;
select * from cumulative_sum();
所属していません StackOverflow