一列的历史聚合,直到另一列中每一行的指定时间
Posted
技术标签:
【中文标题】一列的历史聚合,直到另一列中每一行的指定时间【英文标题】:historical aggregation of a column up until a specified time in each row in another column 【发布时间】:2021-10-06 18:43:42 【问题描述】:我在 Amazon RedShift 中有两个表 login_attempts
和 checkouts
。一个用户可以有多次(不)成功的登录尝试和多次(不)成功的结帐,如下例所示:
login_attempts
login_id | user_id | login | success
-------------------------------------------------------
1 | 1 | 2021-07-01 14:00:00 | 0
2 | 1 | 2021-07-01 16:00:00 | 1
3 | 2 | 2021-07-02 05:01:01 | 1
4 | 1 | 2021-07-04 03:25:34 | 0
5 | 2 | 2021-07-05 11:20:50 | 0
6 | 2 | 2021-07-07 12:34:56 | 1
和
checkouts
checkout_id | checkout_time | user_id | success
------------------------------------------------------------
1 | 2021-07-01 18:00:00 | 1 | 0
2 | 2021-07-02 06:54:32 | 2 | 1
3 | 2021-07-04 13:00:01 | 1 | 1
4 | 2021-07-08 09:05:00 | 2 | 1
鉴于此信息,我如何获得下表,其中包含每次结帐的历史表现截至当时?
checkout_id | checkout | user_id | lastGoodLogin | lastFailedLogin | lastGoodCheckout | lastFailedCheckout |
---------------------------------------------------------------------------------------------------------------------------------------
1 | 2021-07-01 18:00:00 | 1 | 2021-07-01 16:00:00 | 2021-07-01 14:00:00 | NULL | NULL
2 | 2021-07-02 06:54:32 | 2 | 2021-07-02 05:01:01 | NULL | NULL | NULL
3 | 2021-07-04 13:00:01 | 1 | 2021-07-01 16:00:00 | 2021-07-04 03:25:34 | NULL | 2021-07-01 18:00:00
4 | 2021-07-08 09:05:00 | 2 | 2021-07-07 12:34:56 | 2021-07-05 11:20:50 | 2021-07-02 06:54:32 | NULL
更新:我能够得到lastFailedCheckout
和lastGoodCheckout
,因为那是在同一张表上进行窗口操作(结帐),但我不明白如何最好地将它与login_attempts
表连接起来以获得last[Good|Failed]Login
字段。 (sqlfiddle)
P.S.:我也愿意接受PostgreSQL
的建议。
【问题讨论】:
你熟悉窗口函数吗?特别是框架条款?这应该允许你做你正在寻找的东西。如果您坚持实施解决方案,我可以建议您在 sqlfiddle.com 中设置您的测试用例并展示您已经取得了多远?更容易分享特定领域的专业知识,并显示您遇到的问题将显示有助于您理解的领域。 @BillWeiner 感谢您的提示 :) 我更新了帖子。 【参考方案1】:好的开始!您的 SQL 中有几件事 - 1) 您应该尽量避免不等式连接,因为这些连接会导致数据爆炸,并且在这种情况下不需要。只需在您的窗口函数中放置一个 CASE 语句,以仅使用您想要的结帐(或登录)类型。 2)您可以使用框架子句在查找以前的结帐时不选择同一行。
一旦你有了这个模式,你就可以用它来找到你正在寻找的其他 2 列数据。第一步是将表 UNION 在一起,而不是 JOIN。这意味着制作更多的列,以便数据可以一起存在,但这很容易。现在,您在同一数据中拥有了用户 ID 和“事情”发生的时间。你只需要再WINDOW 2 次来提取你想要的信息。最后,您需要使用带有 where 子句的外部 select 去除非结帐行。
像这样:
create table login_attempts(
loginid smallint,
userid smallint,
login timestamp,
success smallint
);
create table checkouts(
checkoutid smallint,
userid smallint,
checkout_time timestamp,
success smallint
);
insert into login_attempts values
(1, 1, '2021-07-01 14:00:00', 0),
(2, 1, '2021-07-01 16:00:00', 1),
(3, 2, '2021-07-02 05:01:01', 1),
(4, 1, '2021-07-04 03:25:34', 0),
(5, 2, '2021-07-05 11:20:50', 0),
(6, 2, '2021-07-07 12:34:56', 1)
;
insert into checkouts values
(1, 1, '2021-07-01 18:00:00', 0),
(2, 2, '2021-07-02 06:54:32', 1),
(3, 1, '2021-07-04 13:00:01', 1),
(4, 2, '2021-07-08 09:05:00', 1)
;
SQL:
select *
from (
select
c.checkoutid,
c.userid,
c.checkout_time,
max(case success when 0 then checkout_time end) over (
partition by userid
order by event_time
rows between unbounded preceding and 1 preceding
) as lastFailedCheckout,
max(case success when 1 then checkout_time end) over (
partition by userid
order by event_time
rows between unbounded preceding and 1 preceding
) as lastGoodCheckout,
max(case lsuccess when 0 then login end) over (
partition by userid
order by event_time
rows between unbounded preceding and 1 preceding
) as lastFailedLogin,
max(case lsuccess when 1 then login end) over (
partition by userid
order by event_time
rows between unbounded preceding and 1 preceding
) as lastGoodLogin
from (
select checkout_time as event_time, checkoutid, userid,
checkout_time, success,
NULL as login, NULL as lsuccess
from checkouts
UNION ALL
select login as event_time,NULL as checkoutid, userid,
NULL as checkout_time, NULL as success,
login, success as lsuccess
from login_attempts
) c
) o
where o.checkoutid is not null
order by o.checkoutid
【讨论】:
完整的 sqlfiddle 在这里:sqlfiddle.com/#!15/7ccb1/53 非常感谢简洁的解释和回答:) 没问题。万事如意以上是关于一列的历史聚合,直到另一列中每一行的指定时间的主要内容,如果未能解决你的问题,请参考以下文章