web-dev-qa-db-ja.com

PostgreSQL:「分単位」のクエリの実行中の行数

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
37
GabiMe

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

最短

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)よりも少し速いです。また、idNOT 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分あたりの行を集計およびカウントします。このようにして、外側のDISTINCTSELECTなしで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を返すことができます。デフォルトは0COALESCEです。

多くの行と"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;
92