【问题标题】:Getting date, and count of unique customers when first order was placed首次下订单时获取日期和唯一客户数
【发布时间】:2019-10-27 14:20:27
【问题描述】:

我有一个名为 orders 的表,如下所示:

+--------------+---------+------+-----+---------+-------+
| Field        | Type    | Null | Key | Default | Extra |
+--------------+---------+------+-----+---------+-------+
| id           | int(11) | YES  |     | NULL    |       |
| memberid     | int(11) | YES  |     | NULL    |       |
| deliverydate | date    | YES  |     | NULL    |       |
+--------------+---------+------+-----+---------+-------+

其中包含以下数据:

+------+----------+--------------+
| id   | memberid | deliverydate |
+------+----------+--------------+
|    1 |      991 | 2019-10-25   |
|    2 |      991 | 2019-10-26   |
|    3 |      992 | 2019-10-25   |
|    4 |      992 | 2019-10-25   |
|    5 |      993 | 2019-10-24   |
|    7 |      994 | 2019-10-21   |
|    6 |      994 | 2019-10-26   |
|    8 |      995 | 2019-10-26   |
+------+----------+--------------+

我想要一个返回每个唯一日期的结果集,以及一个单独的列,显示当天有多少客户下了他们的第一个订单。

我在以正确方式查询时遇到问题,尤其是当数据包含同一客户同一天的多个订单时。

我的方法是

  1. 获取在我要查看的时间段内下订单的所有唯一会员 ID
  2. 通过比较该时间段前下单的memberid,过滤掉该时间段内首次下单的会员
  3. 按交货日期分组,并计算所有唯一的 memberid(但这显然每天单独计算唯一的 memberid!)

下面是对应的SQL:

SELECT deliverydate,COUNT(DISTINCT memberid) FROM orders
WHERE 
    MemberId IN (SELECT DISTINCT memberid FROM orders WHERE deliverydate BETWEEN '2019-10-25' AND '2019-10-26')
AND NOT 
    MemberId In (SELECT DISTINCT memberid FROM orders WHERE deliverydate < '2019-10-25')
GROUP BY deliverydate
ORDER BY deliverydate ASC;

但这会导致上述数据如下:

+--------------+--------------------------+
| deliverydate | COUNT(DISTINCT memberid) |
+--------------+--------------------------+
| 2019-10-25   |                        2 |
| 2019-10-26   |                        2 |
+--------------+--------------------------+

2019-10-26 的计数应该是 1。

感谢任何帮助:)

【问题讨论】:

    标签: sql date group-by window-functions


    【解决方案1】:

    你可以聚合两次:

    select first_deliverydate, count(*) cnt
    from (
        select min(deliverydate) first_deliverydate
        from orders
        group by memberid
    ) t
    group by first_deliverydate
    order by first_deliverydate
    

    子查询为您提供每个成员的第一个订单数据,然后外部查询按第一个订单日期聚合和计数。

    demo on DB Fiddle 与您的示例数据返回:

    first_deliverydate | cnt :----------------- | --: 2019-10-21 | 1 2019-10-24 | 1 2019-10-25 | 2 2019-10-26 | 1

    在 MySQL 8.0 中,这也可以通过窗口函数来实现:

    select deliverydate first_deliverydate, count(*) cnt
    from (
        select deliverydate, row_number() over(partition by memberid order by deliverydate) rn
        from orders
    ) t
    where rn = 1
    group by deliverydate
    order by deliverydate
    

    Demo on DB Fiddle

    【讨论】:

      【解决方案2】:

      您必须首先弄清楚第一个交货日期是什么时候:

      SELECT firstdeliverydate,COUNT(DISTINCT memberid) FROM (
      select memberid, min(deliverydate) as firstdeliverydate
      from orders
      WHERE 
          MemberId IN (SELECT DISTINCT memberid FROM orders WHERE deliverydate BETWEEN '2019-10-25' AND '2019-10-26')
      AND NOT 
          MemberId In (SELECT DISTINCT memberid FROM orders WHERE deliverydate < '2019-10-25')
      group by memberid)
      t1
      group by firstdeliverydate
      

      【讨论】:

        【解决方案3】:

        使用NOT EXISTS 获取每个客户的第一个订单,然后使用GROUP BY deliverydate 计算下订单的不同客户:

        select o.deliverydate, count(distinct o.memberid) counter
        from orders o
        where not exists (
          select 1 from orders
          where memberid = o.memberid and deliverydate < o.deliverydate
        )  
        group by o.deliverydate
        

        请参阅demo
        结果:

        | deliverydate        | counter |
        | ------------------- | ------- |
        | 2019-10-21 00:00:00 | 1       |
        | 2019-10-24 00:00:00 | 1       |
        | 2019-10-25 00:00:00 | 2       |
        | 2019-10-26 00:00:00 | 1       |
        

        但是,如果您想要表格中所有日期的结果,包括那些没有新客户订单的日期(所以计数器将是 0):

        select d.deliverydate, count(distinct o.memberid) counter
        from (
          select distinct deliverydate  
          from orders
        ) d left join orders o
        on o.deliverydate = d.deliverydate and not exists (
          select 1 from orders
          where memberid = o.memberid and deliverydate < o.deliverydate
        )  
        group by d.deliverydate
        

        【讨论】:

          猜你喜欢
          • 2023-01-21
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2022-01-14
          • 1970-01-01
          相关资源
          最近更新 更多