我会把这个问题归结为一个你考虑成对的行,time
在每一轮中排序。PostgreSQL 可以一次性做到这一点——没有 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
分别考虑每个行以进行窗口操作。Window 的功能类似于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
哪里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