如何在同一张表上没有左连接的情况下获得 D1 D7 D30

Posted

技术标签:

【中文标题】如何在同一张表上没有左连接的情况下获得 D1 D7 D30【英文标题】:How to get D1 D7 D30 without left join on the same table 【发布时间】:2019-09-22 00:19:06 【问题描述】:

目标:

我想知道在第 0 天注册的每个人,有多少人在 D1、D7 和 D30 之后登录。我想做一个表值函数,用户在其中插入日期并获取 D0、D1、D7、D30 的结果。 结果应如下所示:

Date        TotalD0 TotalD1 TotalD7 TotalD30
2019-04-01    3         3      2       1

情况:

我有一个包含电子邮件和 login_time 的登录表。我在同一张桌子上离开了三次,它在一张假桌子上工作。但是,当使用具有数百万行的真实数据时,它会永远运行。必须有一种更有效的方法来做到这一点。

我尝试了什么:

CREATE FUNCTION fnTestData
(
    @StartDate AS Date
)
RETURNS TABLE
AS
RETURN
    select @startdate, 
       COUNT(distinct t1.id) As TotalD0, 
       COUNT(distinct t1a.id) As TotalD1,
       COUNT(distinct t1b.id) As TotalD7,
       COUNT(distinct t1c.id) As TotalD30
    from #test1 t1
       left join #test1 t1a on t1.id=t1a.id and t1a.login_time >= 
             DATEADD(day,1,t1.login_time)
       left join #test1 t1b on t1.id=t1b.id and t1b.login_time >= 
             DATEADD(day,7,t1.login_time)
       left join #test1 t1c on t1.id=t1c.id and t1c.login_time >= 
             DATEADD(day,30,t1.login_time)
    where t1.login_time = @startdate
    group by t1.login_time

测试数据:

create table #test1 (id int, login_time date)
insert into #test1 values
(1, '2019-04-01'),
(1, '2019-04-01'),
(1, '2019-04-02'),
(1, '2019-04-19'),
(1, '2019-05-05'),
(2, '2019-04-01'),
(2, '2019-04-05'),
(2, '2019-04-10'),
(2, '2019-04-15'),
(3, '2019-04-01'),
(3, '2019-04-01'),
(3, '2019-04-02')

【问题讨论】:

@SalmanA StartDate(用户输入) 对于您的测试数据,预期的结果是什么? 您的表有登录时间,但您的问题是关于注册的。我很困惑。 @GordonLinoff 错字(登录时间) 该查询似乎表明某人必须在 start_date 或以后登录。对吗? 【参考方案1】:

您的查询可以翻译成GROUP BY

DECLARE @StartDate Date = '2019-04-01'

SELECT COUNT(DISTINCT id) D0
     , COUNT(DISTINCT CASE WHEN login_time >= DATEADD(DAY,  1, @StartDate) THEN id END) AS D1
     , COUNT(DISTINCT CASE WHEN login_time >= DATEADD(DAY,  7, @StartDate) THEN id END) AS D7
     , COUNT(DISTINCT CASE WHEN login_time >= DATEADD(DAY, 30, @StartDate) THEN id END) AS D30
FROM #test1 AS t
WHERE login_time >= @StartDate
AND EXISTS (
    SELECT 1
    FROM #test1 AS x
    WHERE x.id = t.id
    AND x.login_time = @StartDate
)
D0    D1    D7    D30
3     3     2     1

您需要创建适当的索引来加快速度。

【讨论】:

我只需要知道这个人在 D1、D7、D30 之后是否至少登录过一次 啊...所以如果一个人在 d1 和 d7 之间登录并且在 d30 之后他被计算一次?在 d1 和 d7 之间? 如果一个人在 D1 之后登录 20 次,他就被计入 D1 列。如果他在 D7 之后登录 14 次,那么再次登录,但 D7 列。 EXISTS 部分最后在做什么?没有它,结果仍然有效 @roger 我按原样翻译了您的查询,忽略了所有 cmets。您的查询似乎找到了在 start_date 登录的人的下一次登录...这部分确保了这一点。它不会计算在 4 月 2 日登录但在 4 月 1 日未登录的人。【参考方案2】:

如果您想根据人们开始的日期进行同期群分析:

select first_ld,
       count(*) as num_d0,
       sum(case when login_date >= dateadd(day, 1, firstld) then 1 else 0 end) as num_d1,
       sum(case when login_date >= dateadd(day, 7, firstld) then 1 else 0 end) as num_d7,
       sum(case when login_date >= dateadd(day, 30, firstld) then 1 else 0 end) as num_d30
from (select id, convert(date, login_time) as login_date,
             min(convert(date, login_time)) over (partition by id) as first_ld
      from #test1 t
      group by id, convert(date, login_time)
     ) t
group by first_ld
order by first_ld;

【讨论】:

这似乎添加了所有这些。它应该只计算不同的人数。我应该分别得到 3、3、2、1。 不是反对者,但似乎 OP 并不太清楚他真正想要完成的事情。 @RogerSteinberg。 . .它应该生成一个递减序列。它应该适用于有人开始的每一天。

以上是关于如何在同一张表上没有左连接的情况下获得 D1 D7 D30的主要内容,如果未能解决你的问题,请参考以下文章

同一张表上的多个连接:转换状态

同一张表上的多个连接,在一个查询中计数

如何在同一张表上计算不同结果的计数[重复]

难以在同一张表上创建更新总计查询

如何在 Oracle SQL 中查找和过滤同一张表上的数据

更新查询在同一张表上的 Sql 查询死锁