如何在 Oracle SQL 中选择上个月登录并在 8 月之前的一个月中记录最少 1 次的行?
Posted
技术标签:
【中文标题】如何在 Oracle SQL 中选择上个月登录并在 8 月之前的一个月中记录最少 1 次的行?【英文标题】:How to select rows where logged in last month and logged min 1 time in one of month preceding August in Oracle SQL? 【发布时间】:2021-11-20 20:58:11 【问题描述】:我在 Oracle SQL 中有一个表格,显示了客户的 ID 和他们登录应用程序的日期:
ID | LOGGED
----------------
11 | 2021-07-10 12:55:13.278
11 | 2021-08-10 13:58:13.211
11 | 2021-02-11 12:22:13.364
22 | 2021-01-10 08:34:13.211
33 | 2021-04-02 14:21:13.272
我只需要选择在上个月(8 月)至少登录 1 次且在 8 月(6 月或 7 月)前一个月至少登录 1 次的客户 (ID)
目前是 9 月,所以... 我需要在 8 月登录至少 1 次的客户 7 月或 6 月最少 1 次, 如果 6 月登录 -> 7 月不登录 如果在 7 月登录 -> 在 6 月未登录因此我需要如下所示:
ID
----
11
如何在 Oracle SQL 中做到这一点?请注意,“LOGGED”列的时间戳如下:2021-01-10 08:34:13.211
【问题讨论】:
你不能调整你在上一个问题中学到的东西吗? (***.com/questions/69356813/…) 现在,我需要选择记录(最少 1 次)是 2 个月前但不是 3 个月前或记录是 3 个月前但不是 2 个月前的行 :) 你能帮我吗?intersect
按两个时间间隔检索的两个数据集。试着把你的通用词翻译成 SQL 操作,差别不大,是交集定义的结果
... 现在,我需要选择记录(最少 1 次)是 2 个月前但不是 3 个月前或记录是 3 个月前但不是 2 个月前的行 :) ...至于我,这是having count
的任务
【参考方案1】:
也许你会这样认为:
select id
from yourtable
group by id
having count(case
months_between(trunc(sysdate,'MM'),
trunc(logged,'MM')
) when 1 then 1 end
) >= 1
and count
(case when
months_between(trunc(sysdate,'MM') ,
trunc(logged,'MM')
) in (2,3) then 1 end
) = 1
我不明白一件事: 你写道:
8 月(6 月或 7 月)前一个月内至少 1 次
然后:
如果在六月登录 -> 在七月不登录
如果在 7 月登录 -> 未在 6 月登录
如果您只需要一个月 - 六月或七月 只需考虑我上面的查询。
如果您需要在 6 月和 7 月至少登录一次,那么:
select id
from yourtable
group by id
having count(case
months_between(trunc(sysdate,'MM'),
trunc(logged,'MM')
) when 1 then 1 end
) >= 1
and count
(case when
months_between(trunc(sysdate,'MM') ,
trunc(logged,'MM')
) in (2,3) then 1 end
) >= 1
【讨论】:
【参考方案2】:您的问题需要澄清一下,但根据您的描述,我看到了几个选项。
最简单的可能是使用数据密集化组合(用于为每个 id 生成每个月的行)和分析函数(用于启用行间计算。这是一个简单的示例:
rem create a dummy table with some more data (you do not seem to worry about the exact timestamp)
drop table logs purge;
create table logs (ID number, LOGGED timestamp);
insert into logs values (11, to_timestamp('2021-07-10 12:55:13.278','yyyy-mm-dd HH24:MI:SS.FF'));
insert into logs values (11, to_timestamp('2021-07-11 12:55:13.278','yyyy-mm-dd HH24:MI:SS.FF'));
insert into logs values (11, to_timestamp('2021-08-10 13:58:13.211','yyyy-mm-dd HH24:MI:SS.FF'));
insert into logs values (11, to_timestamp('2021-02-11 12:22:13.364','yyyy-mm-dd HH24:MI:SS.FF'));
insert into logs values (11, to_timestamp('2021-04-11 12:22:13.364','yyyy-mm-dd HH24:MI:SS.FF'));
insert into logs values (22, to_timestamp('2021-01-10 08:34:13.211','yyyy-mm-dd HH24:MI:SS.FF'));
insert into logs values (33, to_timestamp('2021-04-02 14:21:13.272','yyyy-mm-dd HH24:MI:SS.FF'));
commit;
以下 SQL 将您的数据加密并在同一行列出一个月和上个月的登录总数,以便您可以进行比较计算。那时我还没有完成,但我希望你能明白。
with t as
(-- dummy artificial table just to create a time dimension for densification
select distinct to_char(sysdate - rownum,'yyyy-mm') mon
from dual connect by level < 300),
l_sparse as
(-- aggregating your login info per month
select id, to_char(logged,'yyyy-mm') mon, count(*) cnt
from logs group by id, to_char(logged,'yyyy-mm') ),
l_dense as
(-- densification with partition outer join
select t.mon, l.id, cnt from l_sparse l partition by (id)
right outer join t on (l.mon = t.mon)
)
-- final analytical function to list current and previous row info in same record
select mon, id
, cnt
, lag(cnt) over (partition by id order by mon asc) prev_cnt
from l_dense
order by id, mon;
部分结果:
MON ID CNT PREV_CNT
------- ---------- ---------- ----------
2020-12 11
2021-01 11
2021-02 11 2
2021-03 11 2
2021-04 11 1
2021-05 11 1
2021-06 11
2021-07 11 3
2021-08 11 2 3
2021-09 11 2
2020-12 22
2021-01 22 2
2021-02 22 2
2021-03 22
2021-04 22
...
您可以看到 ID 11 在 2021-08 年您有当前和上个月的登录信息,因此您可以对其进行数学计算。 (需要另一个子选择/带有分支)。
替代方案是:
行间计算加上两个记录时间戳之间的时间数学 模式匹配没有深入了解这些,没有足够的信息来说明您的实际需求。
【讨论】:
以上是关于如何在 Oracle SQL 中选择上个月登录并在 8 月之前的一个月中记录最少 1 次的行?的主要内容,如果未能解决你的问题,请参考以下文章
Java JDBC Oracle SQL 查询每隔几个月就会挂起一次
如何在 Java 中构造 oracle.sql.ANYDATA 并在 PL/SQL 过程中传递它
在 FOR 循环中第一次选择 20 条记录并在 oracle 中再次休息时出错