2

time という列を含む matchstats というテーブルを持つデータベースがあり、アクションが発生するたびに更新されます。また、groundstatsid という列もあり、null でない場合は、立っているのではなく地面でアクションが行われたことを意味します。最後に、Round という列があります。

例:

Time | groundstatsid | Round

 1   | NULL          |  1
 8   | NULL          |  1
 15  | NULL          |  1
 18  | 1             |  1
 20  | 1             |  1
 22  | NULL          |  1
 30  | NULL          |  1
 1   | NULL          |  2

フルタイムの状態を取得するには、基本的に、クエリで最初の時間 (1) を取得してそれを保存し、それから Groundstatsid を非 NULL 値が表示されるまで見て、その位置で時間を取得し、以前に保存された数値を減算します。立ち上がりの時間を取得します (17)。次に、groundstatsid が NULL である場所を探し続けます。その値が見つかったら、groundstatsid または新しいラウンドで非 NULL 値が見つかるまで、同じ検索プロセスを実行する必要があります。その場合、プロセス全体が再び開始されます。

試合全体が終わったら、結果を合計したいと思います。

この例のクエリは 25 を返すと思います。

4

2 に答える 2

4

timeこの問題は、各ラウンド内でソートされた行のペアを考慮する問題に要約できます。PostgreSQL はこれを 1 つのパスで実行できます -- JOIN も PL/pgSQL もありません --ウィンドウ関数を使用します:

SELECT
  round,
  first_value(time) OVER pair AS first_time,
  last_value(time) OVER pair AS last_time,
  first_value(groundstatsid IS NULL) OVER pair AS first_is_standing,
  last_value(groundstatsid IS NULL) OVER pair AS last_is_standing
FROM matchstats
WINDOW pair AS (PARTITION BY round ORDER BY time ROWS 1 PRECEDING);

これは PostgreSQL にテーブルから行を読み取るように指示しますが (おそらくWHERE fightid=?または何かによって制約されます)、roundウィンドウ操作ではそれぞれを個別に考慮するようにします。ウィンドウは のようfirst_valueに機能last_valueし、 に指定した「ウィンドウ」にアクセスできますORDER BY time ROWS 1 PRECEDING。つまり、ウィンドウには現在の行とその直前の行 (存在する場合) の両方が含まれます。したがって、ウィンドウ関数を使用すると、現在の行とその前の行の両方の値を直接出力できます。

あなたが提供したデータについて、このクエリは次の結果をもたらします。

 round | first_time | last_time | first_is_standing | last_is_standing 
-------+------------+-----------+-------------------+------------------
     1 |          1 |         1 | t                 | t
     1 |          1 |         8 | t                 | t
     1 |          8 |        15 | t                 | t
     1 |         15 |        18 | t                 | f
     1 |         18 |        20 | f                 | f
     1 |         20 |        22 | f                 | t
     1 |         22 |        30 | t                 | t
     2 |          1 |         1 | t                 | t

これらの結果を見て、次に何をすべきかを決めるのに役立ちました。あなたの論理の私の理解に基づいて、その人は時間 1..1、1..8、8..15、15..18 から立っていると見なされるべきであり、18..20 から立っていない、立っていないと見なされるべきであると結論付けます。 20..22 から、22..30 から再び立っています。first_timeつまり、とlast_timewhereの差を合計したいということfirst_is_standingです。それをSQLに戻す:

SELECT round, SUM(last_time - first_time) AS total_time_standing
FROM (
  SELECT
    round,
    first_value(time) OVER pair AS first_time,
    last_value(time) OVER pair AS last_time,
    first_value(groundstatsid IS NULL) OVER pair AS first_is_standing,
    last_value(groundstatsid IS NULL) OVER pair AS last_is_standing
  FROM matchstats
  WINDOW pair AS (PARTITION BY round ORDER BY time ROWS 1 PRECEDING)
) pairs
WHERE first_is_standing
GROUP BY round;
 round | total_time_standing 
-------+---------------------
     1 |                  25
     2 |                   0

SUM(CASE WHEN ...)を使用して独立した条件をカウントすることにより、合計時間や転倒回数など、同じ内部クエリから他の値を取得することもできます。

SELECT
  round,
  SUM(CASE WHEN first_is_standing THEN last_time - first_time ELSE 0 END) AS total_time_standing,
  SUM(CASE WHEN first_is_standing AND NOT last_is_standing THEN 1 ELSE 0 END) AS falls,
  SUM(last_time - first_time) AS total_time
FROM (
  SELECT
    round,
    first_value(time) OVER pair AS first_time,
    last_value(time) OVER pair AS last_time,
    first_value(groundstatsid IS NULL) OVER pair AS first_is_standing,
    last_value(groundstatsid IS NULL) OVER pair AS last_is_standing
  FROM matchstats
  WINDOW pair AS (PARTITION BY round ORDER BY time ROWS 1 PRECEDING)
) pairs
GROUP BY round;

 round | total_time_standing | falls | total_time 
-------+---------------------+-------+------------
     1 |                  25 |     1 |         29
     2 |                   0 |     0 |          0
于 2012-11-16T17:11:21.903 に答える
1

これは、任意の数のラウンドの立っている時間を計算します:

SELECT round, sum(down_time - up_time) AS standing_time
FROM  (
   SELECT round, grp, standing, min(time) AS up_time
         ,CASE WHEN standing THEN 
             lead(min(time), 1, max(time)) OVER (PARTITION BY round
                                                 ORDER BY min(time))
          ELSE NULL END AS down_time
   FROM  (
      SELECT round, time, groundstatsid IS NULL AS standing
            ,count(groundstatsid) OVER (PARTITION BY round
                                        ORDER BY time) AS grp
      FROM tbl
      ) x
   GROUP BY 1, 2, standing
   ) y
WHERE  standing
GROUP  BY round
ORDER  BY round;

-> sqlfiddle

説明

  • サブクエリ x:

    count()値をカウントしないNULL(集計としてもウィンドウ関数としても)という事実を利用します。「立っている」アクション ( groundstatsid IS NULL) を持つ連続した行は、 の同じ値になりgrpます。

    使いやすさと優雅さのためにgroundstatsid、ブール値の varに単純化します。standing

  • サブクエリ y:

    グループごとに集計 - 立っている時間が重要です。グラウンドタイムからは、各スタンディングフェーズの後に必要なのは最初の列だけです。

    グループあたりの最小時間をup_time(起立)とします。

    次のtime行の ( lead(min(time) ...) をdown_time(地上に出る) とします。ウィンドウ関数で集計値を使用できることに注意してください。

    lead(min(time), 1, max(time)) OVER ...ラウンドごとに次のラウンドを取得し、ラウンドが終了した場合は現在の行がmin(time)デフォルトになります (次の行はありません)。max(time)

  • 最終選択:

    立っている時間のみを考慮してください。WHERE groundstatsid IS NULL

    sum(down_time - up_time)ラウンドごとの合計立っている時間を集計します。

    ラウンドごとに並べられた結果。出来上がり。

これにより、ウィンドウ関数が多用されます。PostgreSQL 8.4 以降が必要です。
パフォーマンスが最も重要な要件である場合は、plpgsql 関数で手続き的に同じことを行うことができます。
hereまたはhere

于 2012-11-16T17:55:50.190 に答える