SQL-如何在特定行之前选择x行数
我有这张桌子:SQL-如何在特定行之前选择x行数,sql,datetime,amazon-redshift,window-functions,gaps-and-islands,Sql,Datetime,Amazon Redshift,Window Functions,Gaps And Islands,我有这张桌子: ts | user_id | event | ------------------------------- 1500 a eat 1501 a walk 1502 a sleep 1500 b eat 1501 b sleep 1502 b wake 1
ts | user_id | event |
-------------------------------
1500 a eat
1501 a walk
1502 a sleep
1500 b eat
1501 b sleep
1502 b wake
1500 c walk
1501 c eat
1502 c sit
1503 c sleep
1504 c wake
因此,我想在某个事件之前选择x
行数,假设我想在每个用户idsleep
之前选择2个事件
我的最终表格结果应该如下所示:
user_id | event | rank |
--------------------------------
a eat 1
a walk 2
a sleep 3
b NULL 0
b eat 1
b sleep 2
c eat 2
c sit 3
c sleep 4
如何在SQL(特别是红移SQL)中执行此操作这是一个间隙和孤岛问题,您需要每个孤岛的第一行和最后两行 最安全的方法可能是对睡眠事件进行窗口汇总以定义组,然后使用
行数()进行过滤。
:
嗯。您可以使用
lead()
:
注意:这只返回数据中的行。如果需要生成行,则需要附加逻辑
编辑:
实际上,您可以概括如下:
select t.*
from (select t.*,
sum(case when event = 'sleep') over (partition by user_id order by ts rows between current row and 2 following) as cnt_sleep
from t
) t
where cnt_sleep > 0;
这将统计下一行
n
中的“睡眠”数(n-1)。如果在其中任何一个数据库中发现“sleep”,它将返回一行。请用您正在运行的数据库标记您的问题:mysql、oraclce、sql server…?如前所述,这是红移sql,我将标记它。谢谢。(1) 如果同一用户睡两次呢?(2) 您需要一个列来对每个用户的行进行排序,您有这样的列吗?SQL表表示无序集。除非列指定了顺序,否则之前没有行。1。如果用户睡眠两次,它仍然必须在每个睡眠事件之前显示x个事件数。2.我正在使用rank()over(按用户id顺序按日期划分)
为每个用户对行进行排序。很抱歉,我错过了对事件进行排序的列,我确实有时间戳列来对事件进行排序。尝试运行时,选择t.*,求和(当事件='sleep'时,则为1,否则为0结束)over(按用户id顺序划分)grp从mytable t
,我得到了一个错误聚合窗口函数,它带有ORDER BY子句,需要一个frame子句
@上瘾:啊,是的,这是一个奇怪的红移限制。我编辑了我的答案。谢谢你。我正在尝试你的提问,并试图获得直觉。嗨@GMB谢谢你的回答。我选择Gordon的答案作为答案,因为它更简单,并且达到了我想要的。如果我需要有3个以前的事件,而不在查询中添加额外的列,该怎么办?仍然理解直觉,但这看起来足够简单,可以得到我想要的。谢谢@Gordon的答案。我选择这个作为最接近我想要的答案。另外,如果我想要固定数量的事件列(如event1、event2、event3),我想我可以使用lead
函数?@addicted。对Lead允许您将相邻行中的数据放入列中。
ts | user_id | event | grp | rn_asc | rn_desc
---: | :------ | :---- | --: | -----: | ------:
1500 | a | eat | 1 | 1 | 3
1501 | a | walk | 1 | 2 | 2
1502 | a | sleep | 1 | 3 | 1
1500 | b | eat | 1 | 1 | 2
1501 | b | sleep | 1 | 2 | 1
1500 | c | walk | 1 | 1 | 4
1502 | c | sit | 1 | 3 | 2
1503 | c | sleep | 1 | 4 | 1
select *
from (
select t.*,
row_number() over(
partition by user_id, grp
order by ts rows between unbounded preceding and current row
) rn_asc,
row_number() over(
partition by user_id, grp
order by ts rows between unbounded preceding and current row
) rn_desc
from (
select t.*,
sum(case when event = 'sleep' then 1 else 0 end) over(
partition by user_id
order by ts desc
order by ts rows between unbounded preceding and current row
) grp
from mytable t
) t
) t
where (rn_asc = 1 or rn_desc <= 2) and grp > 0
order by user_id, ts
select t.*
from (select t.*,
lead(event) over (partition by user_id order by ts) as next_event,
lead(event, 2) over (partition by user_id order by ts) as next_event2
from t
) t
where 'sleep' in (event, next_event, next_event2);
select t.*
from (select t.*,
sum(case when event = 'sleep') over (partition by user_id order by ts rows between current row and 2 following) as cnt_sleep
from t
) t
where cnt_sleep > 0;