计算有序行集中的相等、连续的值
Count equal, consecutive values in an ordered rowset
我有一个包含两列的 table,例如:
CREATE TABLE actions (
action_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
"action" text NOT NULL
);
其中包含以下数据:
action_time | action
----------------------------+--------
2016-12-30 14:12:33.353269 | a
2016-12-30 14:12:38.536818 | b
2016-12-30 14:12:43.305001 | a
2016-12-30 14:12:49.432981 | a
2016-12-30 14:12:53.536397 | b
2016-12-30 14:12:57.449101 | b
2016-12-30 14:13:01.592785 | a
2016-12-30 14:13:06.192907 | b
2016-12-30 14:13:11.249181 | b
2016-12-30 14:13:13.690897 | b
(10 rows)
您可以假定 action_time 列中没有重复值。
如何计算从上一个动作开始连续执行的相同动作的次数?
连续相同动作的次数没有限制,任何一个动作都可以是最后一个。此外,不同操作的多样性没有限制:我只使用了两个来简化示例数据。
对于此示例数据,我希望结果为 3。这是因为最后一个操作是 "b",并且它连续发生了 3 次。
我认为可以结合 window 函数和 WITH RECURSIVE
子句来实现解决方案,但我不知道该怎么做。
我对经典的间隙和孤岛解决方案进行了一些改动。
注意 ROW_NUMBER 函数如何使用降序 ORDER BY。
select count(*)
from (select
action
,row_number() over ( order by action_time desc) as rn
,row_number() over (partition by action order by action_time desc) as rn_action
from mytab
) t
group by action
,rn - rn_action
having min(rn) = 1
想到这个:
select count(*)
from t cross join
(select t2.action
from t t2
order by action_time desc
limit 1
) last
where t.action_time >= (select max(t2.action_time)
from t t2
where t2.action <> last.action
);
这应该能够利用 (action_time, action)
上的索引。
这应该可以做到。
SELECT COUNT(*)
FROM actions
WHERE action_time > (
SELECT action_time
FROM actions
WHERE action <> (SELECT action FROM actions ORDER BY action_time DESC LIMIT 1)
ORDER BY action_time DESC LIMIT 1);
最内层查询
SELECT action FROM actions ORDER BY action_time DESC LIMIT 1
确定最后一个动作。
查询
SELECT action_time
FROM actions
WHERE action <> (SELECT action FROM actions ORDER BY action_time DESC LIMIT 1)
ORDER BY action_time DESC LIMIT 1
找到具有不同操作的最后一行。
最外层查询查找该行之后的所有行。
改进的解决方案
select count(*)
from (select
action
,row_number() over ( order by action_time desc) as rn
,row_number() over (partition by action order by action_time desc) as rn_action
from mytab
) t
where rn = rn_action
我有一个包含两列的 table,例如:
CREATE TABLE actions (
action_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
"action" text NOT NULL
);
其中包含以下数据:
action_time | action
----------------------------+--------
2016-12-30 14:12:33.353269 | a
2016-12-30 14:12:38.536818 | b
2016-12-30 14:12:43.305001 | a
2016-12-30 14:12:49.432981 | a
2016-12-30 14:12:53.536397 | b
2016-12-30 14:12:57.449101 | b
2016-12-30 14:13:01.592785 | a
2016-12-30 14:13:06.192907 | b
2016-12-30 14:13:11.249181 | b
2016-12-30 14:13:13.690897 | b
(10 rows)
您可以假定 action_time 列中没有重复值。
如何计算从上一个动作开始连续执行的相同动作的次数?
连续相同动作的次数没有限制,任何一个动作都可以是最后一个。此外,不同操作的多样性没有限制:我只使用了两个来简化示例数据。
对于此示例数据,我希望结果为 3。这是因为最后一个操作是 "b",并且它连续发生了 3 次。
我认为可以结合 window 函数和 WITH RECURSIVE
子句来实现解决方案,但我不知道该怎么做。
我对经典的间隙和孤岛解决方案进行了一些改动。
注意 ROW_NUMBER 函数如何使用降序 ORDER BY。
select count(*)
from (select
action
,row_number() over ( order by action_time desc) as rn
,row_number() over (partition by action order by action_time desc) as rn_action
from mytab
) t
group by action
,rn - rn_action
having min(rn) = 1
想到这个:
select count(*)
from t cross join
(select t2.action
from t t2
order by action_time desc
limit 1
) last
where t.action_time >= (select max(t2.action_time)
from t t2
where t2.action <> last.action
);
这应该能够利用 (action_time, action)
上的索引。
这应该可以做到。
SELECT COUNT(*)
FROM actions
WHERE action_time > (
SELECT action_time
FROM actions
WHERE action <> (SELECT action FROM actions ORDER BY action_time DESC LIMIT 1)
ORDER BY action_time DESC LIMIT 1);
最内层查询
SELECT action FROM actions ORDER BY action_time DESC LIMIT 1
确定最后一个动作。
查询
SELECT action_time
FROM actions
WHERE action <> (SELECT action FROM actions ORDER BY action_time DESC LIMIT 1)
ORDER BY action_time DESC LIMIT 1
找到具有不同操作的最后一行。
最外层查询查找该行之后的所有行。
改进的解决方案
select count(*)
from (select
action
,row_number() over ( order by action_time desc) as rn
,row_number() over (partition by action order by action_time desc) as rn_action
from mytab
) t
where rn = rn_action