SQL Server 如何找到日期之间的差异,并在电子商务数据库中找到第一次购买

1rhkuytd  于 2022-12-28  发布在  其他
关注(0)|答案(2)|浏览(117)

我正在使用Microsoft SQL Server Management Studio。我正在尝试衡量电子商务网站的客户保留率。
为此,我需要四个值:

  • 第一个月
  • order_purchase_timestamp
  • age_by_month
  • first_purchase

age_by_monthfirst_purchase的值在我的数据库中是而不是,我想计算它们。
在我的数据库中,我有customer_idorder_purchase_timestamp
first_purchase应该是order_purchase_timestamp的最早示例,我只需要月份和年份。
age_by_month应该是从first_purchaseorder_purchase_timestamp的月份差。
我只想衡量每个月的客户保留率,所以如果在同一个月进行了两次购买,它不应该显示。
日期在2016 - 10 - 01到2018 - 09 - 30之间.它应该是由order_purchase_timestamp排序的
一个例子
| 客户标识|订单_购买_时间戳|
| - ------| - ------|
| 1个|2016年9月4日|
| 第二章|2016年9月5日|
| 三个|2016年9月5日|
| 三个|2016年9月15日|
| 1个|2016年10月4日|

| 客户标识|首次购买|年龄_按_月|订单_购买_时间戳|
| - ------| - ------| - ------| - ------|
| 1个|2016年至2009年|无|2016年9月4日|
| 第二章|2016年至2009年|无|2016年9月5日|
| 三个|2016年至2009年|无|2016年9月5日|
| 1个|2016年至2009年|1个|2016年10月4日|
我所做的

SELECT 
    customer_id, order_purchase_timestamp
FROM
    orders
WHERE  
    (order_purchase_timestamp BETWEEN '2016-10-01' AND '2016-12-31') 
    OR (order_purchase_timestamp BETWEEN '2017-01-01' AND '2017-03-31') 
    OR (order_purchase_timestamp BETWEEN '2017-04-01' AND '2017-06-30') 
    OR (order_purchase_timestamp BETWEEN '2017-07-01' AND '2017-09-30') 
    OR (order_purchase_timestamp BETWEEN '2017-10-01' AND '2017-12-31') 
    OR (order_purchase_timestamp BETWEEN '2018-01-01' AND '2018-03-31') 
    OR (order_purchase_timestamp BETWEEN '2018-04-01' AND '2018-06-30') 
    OR (order_purchase_timestamp BETWEEN '2018-07-01' AND '2018-09-30')
ORDER BY 
    order_purchase_timestamp

本来我打算按季度来做,但现在我想按月来做。

kmpatx3s

kmpatx3s1#

下面的方法被设计成相对容易理解。还有其他方法(例如窗口函数)可能稍微更有效;但这使得以您当前的SQL技能水平进行维护变得很容易。
请注意,下面的SQL命令是相互构建的(所以答案在最后)。为了沿着,下面是一个db<>fiddle,其中包含。
它基于一个简单的查询(我们将使用它作为子查询),该查询查找每个客户的第一个order_purchase_timestamp。

SELECT customer_id, MIN(order_purchase_timestamp) AS first_purchase_date
FROM orders
GROUP BY customer_id

接下来是DATEDIFF,查找两个日期之间的差异。
然后,您可以使用上面的内容作为子查询,获取每一行的第一个日期-然后查找日期差,例如,

SELECT orders.customer_id, 
       orders.order_purchase_timestamp,
       first_purchases.first_purchase_date,
       DATEDIFF(month, first_purchases.first_purchase_date, orders.order_purchase_timestamp) AS age_by_month
FROM   orders
       INNER JOIN
          (SELECT customer_id, MIN(order_purchase_timestamp) AS first_purchase_date
           FROM orders
           GROUP BY customer_id
          ) AS first_purchases ON orders.customer_id = first_purchases.customer_id
  • 注意- DATEDIFF有一个“陷阱”,它可以吸引大多数人,但对你有好处-当比较月份时,它会忽略天的组成部分,例如,如果找到月份的差异,1月1日和1月31日之间的月份差异为0。另一方面,1月31日和2月1日之间的月份差异为1。然而,我认为这实际上是你想要的!

但是,当客户在一个月内有多次购买时(每次购买占一行),上面的操作会重复。相反,我们可以使用GROUP BY按月份进行分组,然后只取当月的第一次购买。
一个“直接”的方法是按YEAR(orders.order_purchase_timestamp)和MONTH(orders.order_purchase_timestamp)分组。但是,我使用了下面的小技巧-使用EOMONTH查找月份的最后一天。EOMONTH返回该月份中任何日期的相同日期;因此,我们可以按此分组。
最后,您可以添加WHERE表达式和ORDER BY以获得您所要求的结果(在两个日期之间)

SELECT orders.customer_id, 
       MIN(orders.order_purchase_timestamp) AS order_purchase_timestamp,
       first_purchases.first_purchase_date,
       DATEDIFF(month, first_purchases.first_purchase_date, EOMONTH(orders.order_purchase_timestamp)) AS age_by_month
FROM   orders
       INNER JOIN
          (SELECT customer_id, MIN(order_purchase_timestamp) AS first_purchase_date
           FROM orders AS orders_ref
           GROUP BY customer_id
          ) AS first_purchases ON orders.customer_id = first_purchases.customer_id
WHERE  orders.order_purchase_timestamp BETWEEN '20161001' AND '20180930'
GROUP BY orders.customer_id, first_purchases.first_purchase_date, EOMONTH(orders.order_purchase_timestamp)
ORDER BY order_purchase_timestamp;

结果-请注意,它们与您的结果不同,因为您希望最早日期为2016年1月10日。

customer_id   order_purchase_timestamp   first_purchase_date        age_by_month
1             2016-10-04 00:00:00.000    2016-09-04 00:00:00.000    1

**编辑:**因为其他人也会这样做!你可以用一次通读来完成,这样可能会运行得更快一些。它也有点短--但是更难理解imo。

下面的代码使用windows functions来计算客户的最早购买量和每个月的最早购买量(并且使用DISTINCT而不是GROUP BY),然后使用DATEDIFF来计算差额。

WITH monthly_orders AS
    (SELECT DISTINCT orders.customer_id, 
           MIN(orders.order_purchase_timestamp) OVER (PARTITION BY orders.customer_id, EOMONTH(orders.order_purchase_timestamp)) AS order_purchase_timestamp,
           MIN(orders.order_purchase_timestamp) OVER (PARTITION BY orders.customer_id) AS first_purchase_date
     FROM   orders)
SELECT *, DATEDIFF(month, first_purchase_date, order_purchase_timestamp) AS age_by_month
FROM   monthly_orders
WHERE  order_purchase_timestamp BETWEEN '20161001' AND '20180930';

但是请注意,这在结果中有一个不同之处,如果您在一个月内有2个订单,并且您的最小日期过滤器在到之间(例如,订单在15/10和20/10,而您的最小日期是16/10),那么行 * 将不会 * 被包括在内,因为该月最早的购买超出了过滤器范围。
还要注意这两种情况以及您使用的日期或日期时间字段的类型-如果您有日期时间而不仅仅是日期,那么BETWEEN '20161001' AND '20180930'>= '20161001' AND < '20181001'是不同的

n7taea2i

n7taea2i2#

下面是一个简短的查询,它可以满足您的所有需求(所使用方法的描述是内联的):

declare @test table (
    customer_id int,
    order_purchase_timestamp date
)
-- some test data
insert into @test values
(1, '2016-09-04'),
(2, '2016-09-05'),
(3, '2016-09-05'),
(3, '2016-09-15'),
(1, '2016-10-04');

select
    customer_id,
    -- takes care of correct display of first_purchase
    format(first_purchase, 'yyyy-MM') first_purchase,
    -- used to get the difference in months
    datediff(m, first_purchase, order_purchase_timestamp) age_by_month,
    order_purchase_timestamp
from (
    select 
        *,
        -- window function used to find min value for given column within group
        -- for each row
        min(order_purchase_timestamp) over (partition by customer_id) first_purchase
    from @test
) a

相关问题