如何在 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 过程中传递它

如何确定sql中前n个月存在的行或记录?

在 FOR 循环中第一次选择 20 条记录并在 oracle 中再次休息时出错

如何声明变量并在同一个 Oracle SQL 脚本中使用它?

如何在 Oracle SQL 中仅选择最近 30 天内第一次在表中显示的这些 ID?