その分までの行の合計数を毎分クエリする必要があります。
これまでに達成できた最善の方法ではうまくいきません。各分までの合計カウントではなく、1 分あたりのカウントを返します。
SELECT COUNT(id) AS count
, EXTRACT(hour from "when") AS hour
, EXTRACT(minute from "when") AS minute
FROM mytable
GROUP BY hour, minute
その分までの行の合計数を毎分クエリする必要があります。
これまでに達成できた最善の方法ではうまくいきません。各分までの合計カウントではなく、1 分あたりのカウントを返します。
SELECT COUNT(id) AS count
, EXTRACT(hour from "when") AS hour
, EXTRACT(minute from "when") AS minute
FROM mytable
GROUP BY hour, minute
SELECT DISTINCT
date_trunc('minute', "when") AS minute
, count(*) OVER (ORDER BY date_trunc('minute', "when")) AS running_ct
FROM mytable
ORDER BY 1;
を使用するdate_trunc()と、必要なものが正確に返されます。
スライスidを細かくしたいので、クエリに含めないでください。GROUP BY
count()通常、単純な集計関数として使用されます。OVER句を追加すると、ウィンドウ関数になります。ウィンドウ定義を省略します。すべての行PARTITION BYで実行中のカウントが必要です。デフォルトでは、 で定義されているように、現在の行の最初の行から最後のピアまでカウントされます。マニュアル:ORDER BY
デフォルトのフレーミング オプションは です
RANGE UNBOUNDED PRECEDING。これは と同じRANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROWです。を使用ORDER BYすると、フレームが、パーティションの開始から現在の行の最後のORDER BYピアまでのすべての行になるように設定されます。
そして、それはまさにあなたが必要としているものです。
count(*)ではなく使用しcount(id)ます。あなたの質問(「行数」)により適しています。通常、 よりわずかに高速ですcount(id)。そして、それは であると仮定するかもしれませんidがNOT NULL、それは質問で指定されていないためcount(id)、厳密に言えば間違ってcount(id)います。
GROUP BY同じクエリ レベルでスライスを分割することはできません。集計関数はウィンドウ関数の前にcount(*)適用されます。ウィンドウ関数は、このように 1 分あたり 1 行しか表示しません。
ただし、はウィンドウ関数の後に適用されるSELECT DISTINCTため、可能です。DISTINCT
ORDER BY 1ここの省略形ORDER BY date_trunc('minute', "when")です。
1リストの最初の式への位置参照参照SELECTです。
to_char()結果をフォーマットする必要がある場合に使用します。お気に入り:
SELECT DISTINCT
to_char(date_trunc('minute', "when"), 'DD.MM.YYYY HH24:MI') AS minute
, count(*) OVER (ORDER BY date_trunc('minute', "when")) AS running_ct
FROM mytable
ORDER BY date_trunc('minute', "when");
SELECT minute, sum(minute_ct) OVER (ORDER BY minute) AS running_ct
FROM (
SELECT date_trunc('minute', "when") AS minute
, count(*) AS minute_ct
FROM tbl
GROUP BY 1
) sub
ORDER BY 1;
上記とよく似ていますが、次のとおりです。
サブクエリを使用して、1 分あたりの行数を集計およびカウントします。このようにDISTINCTして、outer なしで毎分 1 行を取得しSELECTます。
ウィンドウ集計関数として使用sum()して、サブクエリからのカウントを合計します。
これは、1 分あたりの行数が多いため、大幅に高速であることがわかりました。
@GabiMe はコメントで、イベントが発生しなかったもの (ベース テーブルに行がないもの) を含め、時間枠内のすべて の行を1 行取得する方法を尋ねました。minute
SELECT DISTINCT
minute, count(c.minute) OVER (ORDER BY minute) AS running_ct
FROM (
SELECT generate_series(date_trunc('minute', min("when"))
, max("when")
, interval '1 min')
FROM tbl
) m(minute)
LEFT JOIN (SELECT date_trunc('minute', "when") FROM tbl) c(minute) USING (minute)
ORDER BY 1;
generate_series()サブクエリからの集計値に直接基づいて、最初のイベントと最後のイベントの間の時間枠で 1 分ごとに行を生成します。
LEFT JOIN分とカウントに切り捨てられたすべてのタイムスタンプ。NULL値 (行が存在しない場合) は実行中のカウントに加算されません。
CTE の場合:
WITH cte AS (
SELECT date_trunc('minute', "when") AS minute, count(*) AS minute_ct
FROM tbl
GROUP BY 1
)
SELECT m.minute
, COALESCE(sum(cte.minute_ct) OVER (ORDER BY m.minute), 0) AS running_ct
FROM (
SELECT generate_series(min(minute), max(minute), interval '1 min')
FROM cte
) m(minute)
LEFT JOIN cte USING (minute)
ORDER BY 1;
繰り返しますが、最初のステップで 1 分あたりの行を集計してカウントしますDISTINCT。
とは異なりcount()、sum()返品できNULLます。デフォルトは0withCOALESCEです。
Postgres 9.1 - 9.4 でテストしたいくつかのバリアントの中で、サブクエリを使用したこのバージョンの多くの行とインデックスが最速でした。"when"
SELECT m.minute
, COALESCE(sum(c.minute_ct) OVER (ORDER BY m.minute), 0) AS running_ct
FROM (
SELECT generate_series(date_trunc('minute', min("when"))
, max("when")
, interval '1 min')
FROM tbl
) m(minute)
LEFT JOIN (
SELECT date_trunc('minute', "when") AS minute
, count(*) AS minute_ct
FROM tbl
GROUP BY 1
) c USING (minute)
ORDER BY 1;