首页 > 试题广场 >

10月的新户客单价和获客成本

[编程题]10月的新户客单价和获客成本
  • 热度指数:61141 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 256M,其他语言512M
  • 算法知识视频讲解

商品信息表tb_product_info

id product_id
shop_id tag int_ quantity release_time
1 8001 901 日用 60 1000 2020-01-01 10:00:00
2 8002 901 零食 140 500 2020-01-01 10:00:00
3 8003 901 零食 160 500 2020-01-01 10:00:00
4 8004 902 零食
130 500 2020-01-01 10:00:00
(product_id-商品ID, shop_id-店铺ID, tag-商品类别标签, in_price-进货价格, quantity-进货数量, release_time-上架时间)

订单总表tb_order_overall

id order_id uid event_time total_amount total_cnt status
1 301002 102 2021-10-01 11:00:00 235 2 1
2 301003 101 2021-10-02 10:00:00
300 2 1
3 301005 104 2021-10-03 10:00:00
160 1 1
(order_id-订单号, uid-用户ID, event_time-下单时间, total_amount-订单总金额, total_cnt-订单商品总件数, status-订单状态)

订单明细表tb_order_detail

id order_id product_id price cnt
1 301002 8001 85 1
2 301002
8003
180 1
3 301003
8004
140
1
4 301003
8003
180 1
5 301005
8003
180 1

(order_id-订单号, product_id-商品ID, price-商品单价, cnt-下单数量)


问题计算2021年10月商城里所有新用户的首单平均交易金额(客单价)和平均获客成本(保留一位小数)。

:订单的优惠金额 = 订单明细里的{该订单各商品单价×数量之和} - 订单总表里的{订单总金额} 。

输出示例
示例数据的输出结果如下
avg_amount avg_cost
231.7 23.3
解释:
2021年10月有3个新用户,102的首单为301002,订单金额为235,商品总金额为85+180=265,优惠金额为30;
101的首单为301003,订单金额为300,商品总金额为140+180=320,优惠金额为20;
104的首单为301005,订单金额为160,商品总金额为180,优惠金额为20;
平均首单客单价为(235+300+160)/3=231.7,平均获客成本为(30+20+20)/3=23.3
示例1

输入

DROP TABLE IF EXISTS tb_order_overall;
CREATE TABLE tb_order_overall (
    id INT PRIMARY KEY AUTO_INCREMENT COMMENT '自增ID',
    order_id INT NOT NULL COMMENT '订单号',
    uid INT NOT NULL COMMENT '用户ID',
    event_time datetime COMMENT '下单时间',
    total_amount DECIMAL NOT NULL COMMENT '订单总金额',
    total_cnt INT NOT NULL COMMENT '订单商品总件数',
    `status` TINYINT NOT NULL COMMENT '订单状态'
) CHARACTER SET utf8 COLLATE utf8_bin;

DROP TABLE IF EXISTS tb_product_info;
CREATE TABLE tb_product_info (
    id INT PRIMARY KEY AUTO_INCREMENT COMMENT '自增ID',
    product_id INT NOT NULL COMMENT '商品ID',
    shop_id INT NOT NULL COMMENT '店铺ID',
    tag VARCHAR(12) COMMENT '商品类别标签',
    in_price DECIMAL NOT NULL COMMENT '进货价格',
    quantity INT NOT NULL COMMENT '进货数量',
    release_time datetime COMMENT '上架时间'
) CHARACTER SET utf8 COLLATE utf8_bin;

DROP TABLE IF EXISTS tb_order_detail;
CREATE TABLE tb_order_detail (
    id INT PRIMARY KEY AUTO_INCREMENT COMMENT '自增ID',
    order_id INT NOT NULL COMMENT '订单号',
    product_id INT NOT NULL COMMENT '商品ID',
    price DECIMAL NOT NULL COMMENT '商品单价',
    cnt INT NOT NULL COMMENT '下单数量'
) CHARACTER SET utf8 COLLATE utf8_bin;

INSERT INTO tb_product_info(product_id, shop_id, tag, in_price, quantity, release_time) VALUES
  (8001, 901, '日用', 60, 1000, '2020-01-01 10:00:00'),
  (8002, 901, '零食', 140, 500, '2020-01-01 10:00:00'),
  (8003, 901, '零食', 160, 500, '2020-01-01 10:00:00'),
  (8004, 902, '零食', 130, 500, '2020-01-01 10:00:00');

INSERT INTO tb_order_overall(order_id, uid, event_time, total_amount, total_cnt, `status`) VALUES
  (301002, 102, '2021-10-01 11:00:00', 235, 2, 1),
  (301003, 101, '2021-10-02 10:00:00', 300, 2, 1),
  (301005, 104, '2021-10-03 10:00:00', 160, 1, 1);

INSERT INTO tb_order_detail(order_id, product_id, price, cnt) VALUES
  (301002, 8001, 85, 1),
  (301002, 8003, 180, 1),
  (301003, 8004, 140, 1),
  (301003, 8003, 180, 1),
  (301005, 8003, 180, 1);

输出

231.7|23.3
select round(avg(total_amount),1) avg_amount,round(avg(sum_price-total_amount),1) avg_cost
from
(
        select 
        tod.order_id
        ,uid
        ,product_id
        ,event_time
        ,total_amount
        ,row_number() over(partition by uid order by event_time) rk
        ,sum(price*cnt) over(partition by order_id) sum_price
        ,status
        from tb_order_detail tod
        left join tb_order_overall too
        on tod.order_id = too.order_id
        where status = 1
) t
where rk = 1
and date_format(event_time,'%Y-%m')='2021-10'

#需要聚合的变量不能和不需要聚合的一起算
#需要先在子表里聚合之后一起不聚合地算
#第九行不能用rank()得用row_number,因为体现不同的product_id只会用于计算price*cnt
#但这时还没筛选,所以不怕丢数据,算完乘积之后两行就没有区别了,要用row_number()去重

编辑于 2022-01-06 19:17:28 回复(9)
本题需要注意的点:
①10月份的新用户------即顾客的首次下单时间在‘2021-10’
②首单----10月份第一次下单的相关数据
select round(avg(total_amount),1) avg_amount,
round(avg(sum1-total_amount),1) avg_cost
from
(select order_id,uid,event_time,total_amount,
row_number() over (partition by uid order by event_time) t_rank
from tb_order_overall
where status=1
) as a
inner join
(select order_id,sum(price*cnt) sum1 from tb_order_detail
group by order_id) d on d.order_id=a.order_id
where a.t_rank=1
and date_format(a.event_time,'%Y-%m')='2021-10'


发表于 2021-12-07 08:48:58 回复(0)
1.先计算出每个uid在10月份最早购买的订单
2.求出需要的值,为后面的计算做准备
with t as(
	select
		order_id,
		avg(total_amount) total_amount,
		sum(price*cnt)-avg(total_amount) diff
	from tb_order_overall t1
	left join tb_order_detail t2 using(order_id)
	where month(event_time)='10' and (uid,event_time) in (
		select uid,min(event_time) from tb_order_overall group by uid
	) group by order_id
)
select 
	round(avg(total_amount),1) avg_amount,
	round(avg(diff),1) avg_cost from t;


发表于 2022-02-25 23:58:57 回复(1)
#2021年10月所有新用户和首单时间
with t as(select uid,min(event_time) as f_order_time
from tb_order_overall join tb_order_detail
using(order_id)
group by uid
having date_format(f_order_time,"%Y-%m")="2021-10"),

#客单价
t1 as(select round(sum(total_amount)/count(uid),1) as avg_amount
from t join tb_order_overall using(uid)),

#每个订单原总价          
t2 as(select order_id,sum(price*cnt) as total_price
from tb_order_detail join tb_order_overall using(order_id)
group by order_id),
          
#平均获客成本
t3 as(select round(sum(total_price-total_amount)/count(*),1) as avg_cost
from tb_order_overall join t2 using(order_id)
where (uid,event_time)in(select*from t))

select avg_amount,avg_cost
from t1,t3
请教大家一个问题,
为什么当查询一列时,avg_amount就是对的
select avg_amount
from t1,t3



而查询两列时,就是错的了呢
select avg_amount,avg_cost
from t1,t3


发表于 2022-02-19 11:31:42 回复(7)
比较简单的方法,值得一试
一.首单与用户
 (uid,event_time) in(select uid,min(event_time) as  first_order_time from tb_order_overall group by uid)
一.将tb_order_detail与tb_order_overall联结起来
with t1 as(select t3.order_id,max(total_amount) total_amount,sum(price*cnt) as total_price
                from tb_order_detail as t3 inner join  tb_order_overall as t2 on t3.order_id=t2.order_id
                where date_format(event_time,'%Y-%m')='2021-10' 
                           and (uid,event_time) in(select uid,min(event_time) as first_order_time from tb_order_overall group by uid)
               group by t3.order_id)
三.算出avg_amount与avg_cost
select round(avg(total_amount),1) as avg_amount,round(avg(total_price-total_amount),1) as avg_cost from t1

合并
with t1 as(select t3.order_id,max(total_amount) total_amount,sum(price*cnt) as total_price
                from tb_order_detail as t3 inner join  tb_order_overall as t2 on t3.order_id=t2.order_id
                where date_format(event_time,'%Y-%m')='2021-10' 
                           and (uid,event_time) in(select uid,min(event_time) as first_order_time from tb_order_overall group by uid)
               group by t3.order_id)
select round(avg(total_amount),1) as avg_amount,round(avg(total_price-total_amount),1) as avg_cost from t1;

发表于 2022-05-05 17:53:19 回复(0)
SELECT  round(sum(total_amount)/count(1),1),round(sum(f)/count(1),1)
FROM (SELECT order_id,uid,total_amount,s,s-total_amount f
FROM tb_order_overall
--联立订单明细表 算出优惠了多少
LEFT JOIN (
    --算出订单表的原本总额
    SELECT order_id,SUM(cnt*price) s
    FROM tb_order_detail
    GROUP BY order_id
    ) t1 USING(order_id)
     --过滤掉日期不对的和不是新用户订单的
    WHERE date_format(event_time,'%Y-%m')='2021-10'
    AND (uid,event_time) IN
        (SELECT uid,MIN(event_time) FROM tb_order_overall GROUP BY uid))t2;

发表于 2022-03-10 10:09:52 回复(0)
select round(avg(avg1),1) as avg_amount ,round(avg(sum2-avg1),1) as avg_cost from (
    select order_id,avg(total_amount)avg1,sum(price*cnt)sum2
    from 
        (select order_id,uid,total_amount,event_time,rank() over(partition by uid order by event_time) as ranking from tb_order_overall)t join tb_order_detail using(order_id)
        join tb_product_info using(product_id)
        where date_format(event_time,'%Y%m')='202110' and ranking=1
            and uid not in (select uid from tb_order_overall where date_format(event_time,'%Y%m')<'202110' )
        group by order_id)t1

思路就是算出10月每个人的首单,然后利用筛选去掉在10月前活跃过的用户。

然后计算他们的总额和优惠额即可。

发表于 2022-08-26 15:49:59 回复(0)
select 
    round(avg(price_1), 1) as avg_amount, 
    round(avg(price_0-price_1), 1) as avg_cost
from (
select uid, 
    sum(price*cnt) as price_0, 
    max(total_amount) as price_1
from (
select uid, a.order_id, event_time,
    a.product_id, price, cnt, total_amount,
    dense_rank() over(partition by uid order by event_time) as rnk
from tb_order_detail a left join tb_order_overall b 
on a.order_id = b.order_id
where status = 1
    ) t1
where rnk = 1
and date_format(event_time, '%Y-%m') = '2021-10'
group by uid
        ) t2

发表于 2022-04-06 21:18:55 回复(0)
with base as
(select order_id,uid,total_amount,left(event_time,7) month,
rank()over(partition by uid order by left(event_time,10)) arank
from tb_order_overall)

select round(sum(to_am)/count(uid),1) avg_amount,round(sum(pr_am)/count(uid),1) avg_cost
from
(select uid,sum(total_price) to_p,sum(total_amount) to_am,
sum(total_price)-sum(total_amount) pr_am from base
join 
(select order_id,sum(price*cnt) total_price from tb_order_detail
group by order_id)a using(order_id)
where month='2021-10'
and arank=1
group by uid)b

发表于 2022-03-28 22:09:30 回复(0)
with new_tab as (select uid,a.order_id,event_time,total_amount,price,cnt
,row_number()over(partition by uid order by event_time) as rank2
from tb_order_overall a 
left join tb_order_detail b on a.order_id=b.order_id)

select round(avg(amount),1),round(avg(cost),1)
from 
(select uid
,round(sum(total_amount)/count(uid)) as amount
,round(sum(price*cnt)-sum(total_amount)/count(uid)) as cost
from new_tab 
where (uid,order_id) in (select uid,order_id from new_tab where substring(event_time,1,7)='2021-10' and rank2=1)
group by uid,order_id) t

发表于 2022-03-22 16:21:05 回复(0)
with t as (
    select
        uid,
        event_time,
        total_amount,
        price,
        cnt,
        rank() over(partition by uid order by event_time) rk
    from tb_order_overall oo
    join tb_order_detail od
    on oo.order_id = od.order_id
)
select distinct
    round(avg(avg(total_amount)) over(),1) avg_amount,
    round(avg(sum(price * cnt) - avg(total_amount)) over(),1) avg_cost
from t
where date_format(event_time,'%Y-%m') = '2021-10' and rk = 1
group by uid

发表于 2023-09-14 21:34:05 回复(0)
跟一位发布的题解代码思路一样,他的代码可以通过,但是我的自测数据没问题,但是一提交就不行
select round(avg(total_amount),1) avg_amount,
     round(avg(cost),1) avg_cost
from(
select  b.order_id,
        a.total_amount,  #订单金额
        (sum(b.price*b.cnt)-a.total_amount)  as cost
from tb_order_overall a #订单总表
inner join tb_order_detail b #订单明细表
on a.order_id=b.order_id
where   (a.uid,a.event_time) in (select uid,min(a.event_time) dt
            from tb_order_overall
            group by uid) #新用户首单下单日期
    and date_format(a.event_time,'%Y-%m')='2021-10'
group by  b.order_id, a.total_amount )t;
发表于 2023-08-20 15:08:51 回复(1)
哪位大佬帮我看一下,最后几行代码,在round保留1位小数时始终得不到想要的值,但保留2位小数时,除了位数不对,其他都是对的,纳闷了好久,不知其解

with t1 as
(select uid, event_time,(row_number() over (partition by uid order by event_time desc)) as rn
from tb_order_overall
),

t2 as
(select uid,tb_order_overall.order_id as order_id,event_time,total_amount,product_id,price*cnt as sell_price,(dense_rank() over (partition by uid order by event_time desc)) as rn1
from tb_order_detail left join tb_order_overall on tb_order_detail.order_id=tb_order_overall.order_id
where uid in
(select uid
from t1
where rn=1 and date_format(event_time,'%Y%m')='202110')),

t3 as
(select uid,avg(total_amount) as amount,(sum(sell_price)-avg(total_amount)) as cost
from t2
where rn1=1
group by uid)

select
round(avg(amount),1) as avg_amout,
round(avg(cost),1) as avg_cost
from t3
编辑于 2023-06-04 01:24:55 回复(1)
主要是关系有点乱,需要理清楚。
我们需要的:该订单各商品单价*数量 来自tb_order_detail
平均交易金额 和 订单总金额 来自tb_order_overall
那么我们就从这两个表构造新表,通过所给公式计即可
with o1 as
(select order_id,uid,total_amount,event_time,total_cnt-- 首单的订单信息
from (select *,row_number() over(partition by uid order by event_time asc)rk from tb_order_overall) t1
where rk=1),
o2 as
(select order_id,sum(price*cnt) yf -- 每个订单的应付金额
from tb_order_detail
group by order_id)

select round(avg(total_amount),1) avg_amount, round(sum(yf-total_amount)/count(*),1) avg_cost
from o1 left join o2 on o1.order_id=o2.order_id
where date_format(event_time,'%Y-%m')='2021-10'


发表于 2023-03-13 17:36:57 回复(0)
发表于 2023-02-11 00:55:00 回复(0)

【场景】:新用户首次行为

【分类】:聚合函数、子查询

分析思路

难点:

1.获取新用户首单的信息。使用in子查询

(1)统计每个订单的商品总金额

  • [使用]:sum() group by

(2)计算2021年10月商城里所有新用户的首***均交易金额(客单价)和平均获客成本(保留一位小数)

连续的表示:日期减去排序的值相等;

  • [条件]:2021年10月;新用户首单

  • [使用]:(uid,event_time) in( )

最终结果

select 查询结果 [平均交易金额;平均获客成本]
from 从哪张表中查询数据[多表]
where 查询条件 [2021年10月;已付款:新用户首单]

求解代码

方法一:

with子句

with
    main as(
        #统计每个订单的商品总金额
        select
            order_id,
            sum(price*cnt) as uid_cost
        from tb_order_detail
        group by order_id
    )

#计算2021年10月商城里所有新用户的首***均交易金额(客单价)和平均获客成本(保留一位小数)
select
    round(avg(total_amount),1) as avg_amount,
    round(avg(uid_cost-total_amount),1) as avg_cost
from tb_order_overall
join main using(order_id)
where date_format(event_time,'%Y%m') = '202110'
and status = 1
and (uid,event_time) in(
    select
        uid,
        min(event_time) as event_time
    from tb_order_overall
    group by uid
)

方法二

多表嵌套

#计算2021年10月商城里所有新用户的首***均交易金额(客单价)和平均获客成本(保留一位小数)
select
    round(avg(total_amount),1) as avg_amount,
    round(avg(uid_cost-total_amount),1) as avg_cost
from tb_order_overall
join(
    #统计每个订单的商品总金额
    select
        order_id,
        sum(price*cnt) as uid_cost
    from tb_order_detail
    group by order_id
) main using(order_id)
where date_format(event_time,'%Y%m') = '202110'
and status = 1
and (uid,event_time) in(
    select
        uid,
        min(event_time) as event_time
    from tb_order_overall
    group by uid
)
发表于 2022-11-06 23:46:57 回复(0)
SELECT 
    ROUND(AVG(total_amount), 1) AS avg_amount,
    ROUND(AVG(total - total_amount), 1) AS avg_cost
FROM(
    SELECT 
        uid,
        event_time,
        ROW_NUMBER() OVER (PARTITION BY uid ORDER BY event_time) AS ranking,
        total_amount,
        SUM(price*cnt) OVER (PARTITION BY order_id) AS total
    FROM tb_order_detail
    JOIN tb_order_overall USING(order_id)
    WHERE status = 1) TB1
WHERE 
    ranking = 1
    AND DATE_FORMAT(event_time, '%Y%m') = 202110
这道题的概念理解太难了,看错了几百次:
  1. 客单价和获客成本的对象是:最小下单日期min(event_time)在10月份的用户uid的第一条order_id。根据row_number可以同时筛选出符合以上条件的记录,只能是row_number ,dense_rank,rank啥的都不行~
  2. 一个uid在一个order_id中可能有多条记录,所以要用SUM窗口函数,单纯的聚合有可能出现一行对应多行的错误。同时窗口聚合的分组依据应该是order_id而不是uid
发表于 2022-08-07 18:04:48 回复(0)
select round(sum(total_amount)/count(1),1) avg_amount
,round(sum(cost)/count(1),1) avg_cost 
from 
(SELECT uid,total_amount,sum(price*cnt)-total_amount cost
FROM tb_order_detail
join tb_product_info using (product_id)
join tb_order_overall using (order_id)
where date(event_time) like '%2021-10%'
and (uid,event_time) in (select uid ,min(event_time) from tb_order_overall GROUP BY uid )
group by uid,total_amount) t

发表于 2022-07-20 17:13:19 回复(1)
有哪个大神帮忙看看为什么这个代码通不过
with X as (
select t1.order_id
,        t2.uid
,        t2.total_amount
,        t1.price
,        row_number() over (partition by t2.uid order by t1.order_id) as rank_order
,        sum(price*cnt) over(partition by order_id) as sum_price
,        t2.status
from tb_order_detail t1
left join tb_order_overall t2 on t1.order_id=t2.order_id
left join tb_product_info t3 on t1.product_id=t3.product_id
where date_format(t2.event_time, '%Y-%m') = '2021-10' and status = 1)


select round(avg(total_amount), 1) as avg_amount
,        round(avg(sum_price-total_amount), 1) as avg_cost
from X
where rank_order=1

发表于 2022-05-09 22:44:17 回复(4)
# 超简单
#第一步找到新用户,获取必要信息
    select uid,order_id,event_time,total_amount
    from (
        select uid,order_id,event_time,total_amount,
        row_number() over(partition by uid order by event_time) as r
        from tb_order_overall
    )as aa
    where r=1
#第二步找到新用户详细订单信息
    select order_id,sum(price*cnt) as amount
    from tb_order_detail
    group by order_id
#第三步两个表相交,计算
select round(avg(total_amount),1)  as avg_amount, round(avg(amount-total_amount),1) as avg_cost
from (
    select uid,order_id,event_time,total_amount
    from (
        select uid,order_id,event_time,total_amount,row_number() over(partition by uid order by event_time) as r
        from tb_order_overall
    )as aa
    where r=1
)as a 
inner join (
    select order_id,sum(price*cnt) as amount
    from tb_order_detail
    group by order_id
)as b 
on a.order_id=b.order_id
where substr(event_time,1,7)='2021-10'
发表于 2022-03-15 20:47:14 回复(0)
  • 二维码

    扫描二维码,关注牛客网

  • 二维码

    下载牛客APP,随时随地刷题