【问题标题】:Find users who did not re-subscribe查找未重新订阅的用户
【发布时间】:2019-04-09 02:27:28
【问题描述】:

我在 PostgreSQL 10.5 中有一个表订阅:

id  user_id  starts_at  ends_at
--------------------------------
1   233      02/04/19   03/03/19
2   233      03/04/19   04/03/19
3   296      02/09/19   03/08/19
4   126      02/01/19   02/28/19
5   126      03/01/19   03/31/19
6   922      02/22/19   03/22/19
7   111      01/22/19   02/21/19
8   111      02/22/19   03/21/19

我想获取三月份未重新订阅的用户 ID 列表。鉴于上面的数据,它应该显示:

user_id
-------
296
922

我将如何计算这个。我尝试了一些查询,但它们不起作用,不值得发布

【问题讨论】:

  • 我的花絮:使用FORMAT(DATE, MM) 自行获取月份。然后您可以比较 starts_at 是否在 3 月,如果您的问题是这个意思。
  • 如果数据集中不存在上述数据,查询将如何返回 user_id 295?

标签: sql postgresql date-range


【解决方案1】:

大概,您想要一个特定的三月,而不是任何一年的三月。所以:

select s.userId
from subscriptions s
group by s.userId
having count(*) filter (where startsAt >= '2019-03-01' and startsAt < '2019-04-01') = 0;

您也可以使用not exists。如果您有一个用户列表,这会更好:

select u.*
from users u
where not exists (select 1
                  from subscriptions s
                  where s.userid = u.userid and
                        s.startsAt >= '2019-03-01' and
                        s.startsAt < '2019-04-01'
                 );

除了users,您还可以使用:

select distinct s.userId
from subscriptions
where . . .

【讨论】:

  • 这不一定有效,因为我们的 users 表包含从未订阅过的用户,所以我们只想查看 2 月订阅但 3 月未续订的用户。
  • @theartofbeing 。 . .在您的问题中,我没有看到 2 月份的任何情况。这并不难添加,但没有。
  • 我认为这是通过要求三月份重新订阅的用户来暗示的,​​道歉。
  • 我错误地问了这个问题,并在stackoverflow.com/questions/55618271/… 中重新问了它 - 根据我最初提出问题的方式,这是正确的答案。
【解决方案2】:

你可以利用 not exists 不获取开始日期为 3 月的客户。

with cte as 
(
select 1  as ID,   233 as User_Id, '02/04/2019' as Startsat   , '03/03/2019' ends_at union all 
select 2  as ID,   233 as User_Id, '03/04/2019' as Startsat   , '04/03/2019' ends_at union all 
select 3  as ID,   296 as User_Id, '02/09/2019' as Startsat   , '03/08/2019' ends_at union all 
select 4  as ID,   126 as User_Id, '02/01/2019' as Startsat   , '02/28/2019' ends_at union all 
select 5  as ID,   126 as User_Id, '03/01/2019' as Startsat   , '03/31/2019' ends_at union all 
select 6  as ID,   922 as User_Id, '02/22/2019' as Startsat   , '03/22/2019' ends_at)

select *  from cte  c 
where  not exists
(select 1 from cte c1 where c.User_Id = c1.User_Id and date_part('Month',to_date(c1.Startsat,'MM/DD/YYYY'))= '3' )

输出:

id  user_id startsat    ends_at
3   296 02/09/2019  03/08/2019
6   922 02/22/2019  03/22/2019

这里是小提琴链接:

https://dbfiddle.uk/?rdbms=postgres_10&fiddle=84e24cd517fa0810bef011d6fb1b2be2

【讨论】:

【解决方案3】:

除了其他答案,这里还有几个其他选项:

选项 1

您可以创建 2 个 CTE,每个月一个(假设您查看的是特定月份,而不仅仅是一般的 2 月/3 月)。请注意,这使用range 数据类型来过滤日期。

WITH 
    -- sample data
    Subscriptions("id", user_id, starts_at, ends_at) AS
    (
        VALUES
        (1,   233,      DATE'02/04/19',   DATE'03/03/19'),
        (2,   233,      DATE'03/04/19',   DATE'04/03/19'),
        (3,   296,      DATE'02/09/19',   DATE'03/08/19'),
        (4,   126,      DATE'02/01/19',   DATE'02/28/19'),
        (5,   126,      DATE'03/01/19',   DATE'03/31/19'),
        (6,   922,      DATE'02/22/19',   DATE'03/22/19')
    ),
    -- separate CTEs for February and March data
    -- using range type for easy filter.
    FebruarySubscriptions AS
    (
        SELECT * FROM Subscriptions 
        WHERE daterange('2019-02-01', '2019-03-01') @> starts_at
    ),
    MarchSubscriptions AS
    (
        SELECT * FROM Subscriptions 
        WHERE daterange('2019-03-01', '2019-04-01') @> starts_at
    )
SELECT * 
FROM FebruarySubscriptions
    LEFT JOIN MarchSubscriptions ON
        MarchSubscriptions.user_id = FebruarySubscriptions.user_id
WHERE MarchSubscriptions."id" IS NULL

选项 2

使用LEAD 窗口函数找出哪些用户没有重新订阅。此选项的好处是可扩展性更强。

WITH 
    Subscriptions("id", user_id, starts_at, ends_at) AS
    (
        VALUES
        (1,   233,      DATE'02/04/19',   DATE'03/03/19'),
        (2,   233,      DATE'03/04/19',   DATE'04/03/19'),
        (3,   296,      DATE'02/09/19',   DATE'03/08/19'),
        (4,   126,      DATE'02/01/19',   DATE'02/28/19'),
        (5,   126,      DATE'03/01/19',   DATE'03/31/19'),
        (6,   922,      DATE'02/22/19',   DATE'03/22/19')
    ),
    Resubscriptions(user_id, current_subscription, next_subscription) AS
    (
        SELECT 
            user_id, 
            starts_at, 
            LEAD(starts_at) OVER
            (
                PARTITION BY user_id
                ORDER BY starts_at ASC
            )
        FROM Subscriptions
    )
SELECT * 
FROM Resubscriptions
WHERE 
    daterange('2019-02-01', '2019-03-01') @> current_subscription
    AND next_subscription IS NULL

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-08
    • 2015-01-16
    • 2017-06-28
    • 2015-09-01
    相关资源
    最近更新 更多