1分ごとに、その分までの行の総数を照会する必要があります。
これまでに達成できた最善の策ではうまくいきません。 1分あたりの合計数ではなく、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()
を使用すると、必要なものが正確に返されます。
GROUP BY
分のスライスを使用するため、クエリにid
を含めないでください。
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)
はwrongです。厳密には、 NULL値はcount(id)
ではカウントされません。
同じクエリレベルでGROUP BY
分のスライスを作成することはできません。集計関数が適用されるbeforeウィンドウ関数。ウィンドウ関数count(*)
は、この方法では1分あたり1行しか表示しません。
ただし、DISTINCT
が適用されるため、SELECT DISTINCT
を使用できますafterウィンドウ関数。
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
にSELECT
なしで1分あたり1行を取得します。
sum()
をウィンドウ集計関数として使用して、サブクエリからのカウントを合計します。
これは、1分あたりの行数が多く、かなり高速であることがわかりました。
@ GabiMeがコメントで尋ねた 時間枠内のeveryminute
の行を取得する方法(イベントがないものも含む)発生しました(ベーステーブルに行がありません):
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()
を使用して、最初のイベントと最後のイベントの間の時間枠内で毎分行を生成します。ここでは、サブクエリからの集計値に直接基づいています。
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
を返すことができます。デフォルトは0
とCOALESCE
です。
多くの行と"when"
のインデックスがあるため、このバージョンのサブクエリは、Postgres 9.1〜9.4でテストしたいくつかのバリアントの中で最も高速でした。
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;