44

その分までの行の合計数を毎分クエリする必要があります。

これまでに達成できた最善の方法ではうまくいきません。各分までの合計カウントではなく、1 分あたりのカウントを返します。

SELECT COUNT(id) AS count
     , EXTRACT(hour from "when") AS hour
     , EXTRACT(minute from "when") AS minute
  FROM mytable
 GROUP BY hour, minute
4

1 に答える 1

107

アクティビティのある分のみを返す

最短

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)。そして、それは であると仮定するかもしれませんidNOT 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;
于 2011-11-19T12:34:39.547 に答える