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_time
whereの差を合計したいということ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