# 2025年10-12月 客户消费能力Top100(总表) ## 思考过程 以台费订单为基准汇总三类明细,满足应付金额口径,并输出Top100客户的消费/充值/助教偏好。按你的要求,先生成评价为空的版本,再在脚本末尾回填评价。 ## 查询说明 消费=台费(dwd_table_fee_log.ledger_amount)+助教(dwd_assistant_service_log.ledger_amount)+商品(dwd_store_goods_sale.ledger_amount),均为应付金额(不扣优惠),以台费订单为基准串联;充值=充值支付流水(dwd_payment.relate_type=5, pay_status=2, pay_amount>0)按支付时间切月;储值卡未使用金额=当前有效储值卡余额之和(dim_member_card_account.balance, member_card_type_name='储值卡');喜爱助教=基础课时长+附加课时长*1.5(income_seconds换算小时),总表按10-12月汇总Top5。 ## SQL ### Top100(按消费总额) ```sql with base_orders as ( select tfl.order_settle_id, max(tfl.member_id) as member_id, min(tfl.start_use_time) as order_start_time, max(tfl.ledger_end_time) as order_end_time, sum(tfl.ledger_amount) as table_amount, sum(tfl.real_table_use_seconds) as table_use_seconds from billiards_dwd.dwd_table_fee_log tfl where tfl.site_id = %(site_id)s and coalesce(tfl.is_delete,0) = 0 and tfl.start_use_time >= '2025-10-01 00:00:00+08'::timestamptz and tfl.start_use_time < '2026-01-01 00:00:00+08'::timestamptz group by tfl.order_settle_id ), assistant_info as ( select asl.order_settle_id, sum(asl.ledger_amount) as assistant_amount, min(asl.start_use_time) as assistant_start_time, max(asl.last_use_time) as assistant_end_time from billiards_dwd.dwd_assistant_service_log asl join base_orders bo on bo.order_settle_id = asl.order_settle_id where asl.site_id = %(site_id)s and coalesce(asl.is_delete,0) = 0 group by asl.order_settle_id ), goods_amount as ( select g.order_settle_id, sum(g.ledger_amount) as goods_amount from billiards_dwd.dwd_store_goods_sale g join base_orders bo on bo.order_settle_id = g.order_settle_id where g.site_id = %(site_id)s and coalesce(g.is_delete,0) = 0 group by g.order_settle_id ), orders as ( select bo.order_settle_id, bo.member_id, least(bo.order_start_time, coalesce(a.assistant_start_time, bo.order_start_time)) as order_start_time, greatest(bo.order_end_time, coalesce(a.assistant_end_time, bo.order_end_time)) as order_end_time, bo.table_use_seconds, coalesce(bo.table_amount,0) + coalesce(a.assistant_amount,0) + coalesce(g.goods_amount,0) as order_amount from base_orders bo left join assistant_info a on a.order_settle_id = bo.order_settle_id left join goods_amount g on g.order_settle_id = bo.order_settle_id ) select o.member_id, sum(o.order_amount) as consume_total, count(*) as order_cnt from orders o where o.member_id is not null and o.member_id <> 0 group by o.member_id order by consume_total desc limit 100; ``` ### 按月消费汇总 ```sql with base_orders as ( select tfl.order_settle_id, max(tfl.member_id) as member_id, min(tfl.start_use_time) as order_start_time, max(tfl.ledger_end_time) as order_end_time, sum(tfl.ledger_amount) as table_amount, sum(tfl.real_table_use_seconds) as table_use_seconds from billiards_dwd.dwd_table_fee_log tfl where tfl.site_id = %(site_id)s and coalesce(tfl.is_delete,0) = 0 and tfl.start_use_time >= '2025-10-01 00:00:00+08'::timestamptz and tfl.start_use_time < '2026-01-01 00:00:00+08'::timestamptz group by tfl.order_settle_id ), assistant_info as ( select asl.order_settle_id, sum(asl.ledger_amount) as assistant_amount, min(asl.start_use_time) as assistant_start_time, max(asl.last_use_time) as assistant_end_time from billiards_dwd.dwd_assistant_service_log asl join base_orders bo on bo.order_settle_id = asl.order_settle_id where asl.site_id = %(site_id)s and coalesce(asl.is_delete,0) = 0 group by asl.order_settle_id ), goods_amount as ( select g.order_settle_id, sum(g.ledger_amount) as goods_amount from billiards_dwd.dwd_store_goods_sale g join base_orders bo on bo.order_settle_id = g.order_settle_id where g.site_id = %(site_id)s and coalesce(g.is_delete,0) = 0 group by g.order_settle_id ), orders as ( select bo.order_settle_id, bo.member_id, least(bo.order_start_time, coalesce(a.assistant_start_time, bo.order_start_time)) as order_start_time, greatest(bo.order_end_time, coalesce(a.assistant_end_time, bo.order_end_time)) as order_end_time, bo.table_use_seconds, coalesce(bo.table_amount,0) + coalesce(a.assistant_amount,0) + coalesce(g.goods_amount,0) as order_amount from base_orders bo left join assistant_info a on a.order_settle_id = bo.order_settle_id left join goods_amount g on g.order_settle_id = bo.order_settle_id ) , x as ( select o.member_id, case when o.order_start_time >= '2025-10-01 00:00:00+08'::timestamptz and o.order_start_time < '2025-11-01 00:00:00+08'::timestamptz then '2025-10' when o.order_start_time >= '2025-11-01 00:00:00+08'::timestamptz and o.order_start_time < '2025-12-01 00:00:00+08'::timestamptz then '2025-11' when o.order_start_time >= '2025-12-01 00:00:00+08'::timestamptz and o.order_start_time < '2026-01-01 00:00:00+08'::timestamptz then '2025-12' else null end as month_key, o.order_amount from orders o where o.member_id is not null and o.member_id <> 0 ) select member_id, month_key, sum(order_amount) as consume_sum from x where month_key is not null group by member_id, month_key; ``` ### 按月充值汇总 ```sql with pay as ( select p.pay_time, r.member_id, p.pay_amount from billiards_dwd.dwd_payment p join billiards_dwd.dwd_recharge_order r on r.recharge_order_id = p.relate_id where p.site_id = %(site_id)s and p.relate_type = 5 and p.pay_status = 2 and p.pay_amount > 0 and p.pay_time >= %(window_start)s::timestamptz and p.pay_time < %(window_end)s::timestamptz ) , x as ( select member_id, case when pay_time >= '2025-10-01 00:00:00+08'::timestamptz and pay_time < '2025-11-01 00:00:00+08'::timestamptz then '2025-10' when pay_time >= '2025-11-01 00:00:00+08'::timestamptz and pay_time < '2025-12-01 00:00:00+08'::timestamptz then '2025-11' when pay_time >= '2025-12-01 00:00:00+08'::timestamptz and pay_time < '2026-01-01 00:00:00+08'::timestamptz then '2025-12' else null end as month_key, pay_amount from pay ) select member_id, month_key, sum(pay_amount) as recharge_sum from x where month_key is not null group by member_id, month_key; ``` ### 储值卡未使用金额(当前余额汇总) ```sql select tenant_member_id as member_id, sum(balance) as stored_value_balance from billiards_dwd.dim_member_card_account where scd2_is_current=1 and coalesce(is_delete,0)=0 and member_card_type_name='储值卡' and tenant_member_id = any(%(ids)s) group by tenant_member_id; ``` ### 喜爱助教Top5(10-12月) ```sql with x as ( select asl.tenant_member_id as member_id, asl.nickname as assistant_nickname, sum(case when asl.order_assistant_type=1 then asl.income_seconds else asl.income_seconds*1.5 end) / 3600.0 as weighted_hours from billiards_dwd.dwd_assistant_service_log asl where asl.site_id = %(site_id)s and coalesce(asl.is_delete,0)=0 and asl.tenant_member_id is not null and asl.tenant_member_id <> 0 and asl.start_use_time >= %(window_start)s::timestamptz and asl.start_use_time < %(window_end)s::timestamptz group by asl.tenant_member_id, asl.nickname ), ranked as ( select *, row_number() over(partition by member_id order by weighted_hours desc) as rn from x ) select member_id, string_agg(assistant_nickname || '(' || to_char(round(weighted_hours::numeric, 1), 'FM999999990.0') || 'h)', '、' order by weighted_hours desc) as fav5 from ranked where rn <= 5 group by member_id; ``` ### 评价画像:订单/时长/到店日期 ```sql with base_orders as ( select tfl.order_settle_id, max(tfl.member_id) as member_id, min(tfl.start_use_time) as order_start_time, max(tfl.ledger_end_time) as order_end_time, sum(tfl.ledger_amount) as table_amount, sum(tfl.real_table_use_seconds) as table_use_seconds from billiards_dwd.dwd_table_fee_log tfl where tfl.site_id = %(site_id)s and coalesce(tfl.is_delete,0) = 0 and tfl.start_use_time >= '2025-10-01 00:00:00+08'::timestamptz and tfl.start_use_time < '2026-01-01 00:00:00+08'::timestamptz group by tfl.order_settle_id ), assistant_info as ( select asl.order_settle_id, sum(asl.ledger_amount) as assistant_amount, min(asl.start_use_time) as assistant_start_time, max(asl.last_use_time) as assistant_end_time from billiards_dwd.dwd_assistant_service_log asl join base_orders bo on bo.order_settle_id = asl.order_settle_id where asl.site_id = %(site_id)s and coalesce(asl.is_delete,0) = 0 group by asl.order_settle_id ), goods_amount as ( select g.order_settle_id, sum(g.ledger_amount) as goods_amount from billiards_dwd.dwd_store_goods_sale g join base_orders bo on bo.order_settle_id = g.order_settle_id where g.site_id = %(site_id)s and coalesce(g.is_delete,0) = 0 group by g.order_settle_id ), orders as ( select bo.order_settle_id, bo.member_id, least(bo.order_start_time, coalesce(a.assistant_start_time, bo.order_start_time)) as order_start_time, greatest(bo.order_end_time, coalesce(a.assistant_end_time, bo.order_end_time)) as order_end_time, bo.table_use_seconds, coalesce(bo.table_amount,0) + coalesce(a.assistant_amount,0) + coalesce(g.goods_amount,0) as order_amount from base_orders bo left join assistant_info a on a.order_settle_id = bo.order_settle_id left join goods_amount g on g.order_settle_id = bo.order_settle_id ) select o.member_id, count(*) as orders, avg(o.order_amount) as avg_order, sum(o.table_use_seconds)/3600.0 as play_hours, avg(o.table_use_seconds)/3600.0 as avg_play_hours, min((o.order_start_time at time zone 'Asia/Shanghai')::date) as first_visit_day, max((o.order_start_time at time zone 'Asia/Shanghai')::date) as last_visit_day, count(distinct (o.order_start_time at time zone 'Asia/Shanghai')::date) as visit_days, sum(case when o.order_start_time >= '2025-10-01 00:00:00+08'::timestamptz and o.order_start_time < '2025-11-01 00:00:00+08'::timestamptz then 1 else 0 end) as orders_oct, sum(case when o.order_start_time >= '2025-11-01 00:00:00+08'::timestamptz and o.order_start_time < '2025-12-01 00:00:00+08'::timestamptz then 1 else 0 end) as orders_nov, sum(case when o.order_start_time >= '2025-12-01 00:00:00+08'::timestamptz and o.order_start_time < '2026-01-01 00:00:00+08'::timestamptz then 1 else 0 end) as orders_dec from orders o where o.member_id = any(%(ids)s) group by o.member_id; ``` ### 评价画像:到店/离店时间(小时) ```sql with base_orders as ( select tfl.order_settle_id, max(tfl.member_id) as member_id, min(tfl.start_use_time) as order_start_time, max(tfl.ledger_end_time) as order_end_time, sum(tfl.ledger_amount) as table_amount, sum(tfl.real_table_use_seconds) as table_use_seconds from billiards_dwd.dwd_table_fee_log tfl where tfl.site_id = %(site_id)s and coalesce(tfl.is_delete,0) = 0 and tfl.start_use_time >= '2025-10-01 00:00:00+08'::timestamptz and tfl.start_use_time < '2026-01-01 00:00:00+08'::timestamptz group by tfl.order_settle_id ), assistant_info as ( select asl.order_settle_id, sum(asl.ledger_amount) as assistant_amount, min(asl.start_use_time) as assistant_start_time, max(asl.last_use_time) as assistant_end_time from billiards_dwd.dwd_assistant_service_log asl join base_orders bo on bo.order_settle_id = asl.order_settle_id where asl.site_id = %(site_id)s and coalesce(asl.is_delete,0) = 0 group by asl.order_settle_id ), goods_amount as ( select g.order_settle_id, sum(g.ledger_amount) as goods_amount from billiards_dwd.dwd_store_goods_sale g join base_orders bo on bo.order_settle_id = g.order_settle_id where g.site_id = %(site_id)s and coalesce(g.is_delete,0) = 0 group by g.order_settle_id ), orders as ( select bo.order_settle_id, bo.member_id, least(bo.order_start_time, coalesce(a.assistant_start_time, bo.order_start_time)) as order_start_time, greatest(bo.order_end_time, coalesce(a.assistant_end_time, bo.order_end_time)) as order_end_time, bo.table_use_seconds, coalesce(bo.table_amount,0) + coalesce(a.assistant_amount,0) + coalesce(g.goods_amount,0) as order_amount from base_orders bo left join assistant_info a on a.order_settle_id = bo.order_settle_id left join goods_amount g on g.order_settle_id = bo.order_settle_id ) , t as ( select o.member_id, extract(hour from (o.order_start_time at time zone 'Asia/Shanghai')) + extract(minute from (o.order_start_time at time zone 'Asia/Shanghai'))/60.0 + extract(second from (o.order_start_time at time zone 'Asia/Shanghai'))/3600.0 as arrive_h, extract(hour from (o.order_end_time at time zone 'Asia/Shanghai')) + extract(minute from (o.order_end_time at time zone 'Asia/Shanghai'))/60.0 + extract(second from (o.order_end_time at time zone 'Asia/Shanghai'))/3600.0 as leave_h_raw from orders o where o.member_id = any(%(ids)s) ), tt as ( select member_id, arrive_h, case when leave_h_raw < arrive_h then leave_h_raw + 24 else leave_h_raw end as leave_h from t ) select member_id, avg(arrive_h) as arrive_avg_h, percentile_cont(0.5) within group (order by arrive_h) as arrive_med_h, avg(leave_h) as leave_avg_h, percentile_cont(0.5) within group (order by leave_h) as leave_med_h from tt group by member_id; ``` ### 评价画像:球台分区偏好(按时长) ```sql select tfl.member_id, coalesce(tfl.site_table_area_name,'') as site_table_area_name, sum(tfl.real_table_use_seconds)/3600.0 as hours from billiards_dwd.dwd_table_fee_log tfl where tfl.site_id=%(site_id)s and coalesce(tfl.is_delete,0)=0 and tfl.start_use_time >= %(window_start)s::timestamptz and tfl.start_use_time < %(window_end)s::timestamptz and tfl.member_id = any(%(ids)s) group by tfl.member_id, site_table_area_name; ``` ### 评价画像:商品明细(名称+数量) ```sql with base_orders as ( select order_settle_id, max(member_id) as member_id from billiards_dwd.dwd_table_fee_log where site_id=%(site_id)s and coalesce(is_delete,0)=0 and start_use_time >= %(window_start)s::timestamptz and start_use_time < %(window_end)s::timestamptz group by order_settle_id ) select bo.member_id, g.ledger_name, sum(g.ledger_count) as qty, sum(g.ledger_amount) as amount from base_orders bo join billiards_dwd.dwd_store_goods_sale g on g.order_settle_id = bo.order_settle_id where g.site_id=%(site_id)s and coalesce(g.is_delete,0)=0 and bo.member_id = any(%(ids)s) group by bo.member_id, g.ledger_name; ``` ### 评价画像:到店日期明细(用于周期/近期分析) ```sql with base_orders as ( select tfl.order_settle_id, max(tfl.member_id) as member_id, min(tfl.start_use_time) as order_start_time, max(tfl.ledger_end_time) as order_end_time, sum(tfl.ledger_amount) as table_amount, sum(tfl.real_table_use_seconds) as table_use_seconds from billiards_dwd.dwd_table_fee_log tfl where tfl.site_id = %(site_id)s and coalesce(tfl.is_delete,0) = 0 and tfl.start_use_time >= '2025-10-01 00:00:00+08'::timestamptz and tfl.start_use_time < '2026-01-01 00:00:00+08'::timestamptz group by tfl.order_settle_id ), assistant_info as ( select asl.order_settle_id, sum(asl.ledger_amount) as assistant_amount, min(asl.start_use_time) as assistant_start_time, max(asl.last_use_time) as assistant_end_time from billiards_dwd.dwd_assistant_service_log asl join base_orders bo on bo.order_settle_id = asl.order_settle_id where asl.site_id = %(site_id)s and coalesce(asl.is_delete,0) = 0 group by asl.order_settle_id ), goods_amount as ( select g.order_settle_id, sum(g.ledger_amount) as goods_amount from billiards_dwd.dwd_store_goods_sale g join base_orders bo on bo.order_settle_id = g.order_settle_id where g.site_id = %(site_id)s and coalesce(g.is_delete,0) = 0 group by g.order_settle_id ), orders as ( select bo.order_settle_id, bo.member_id, least(bo.order_start_time, coalesce(a.assistant_start_time, bo.order_start_time)) as order_start_time, greatest(bo.order_end_time, coalesce(a.assistant_end_time, bo.order_end_time)) as order_end_time, bo.table_use_seconds, coalesce(bo.table_amount,0) + coalesce(a.assistant_amount,0) + coalesce(g.goods_amount,0) as order_amount from base_orders bo left join assistant_info a on a.order_settle_id = bo.order_settle_id left join goods_amount g on g.order_settle_id = bo.order_settle_id ) select o.member_id, (o.order_start_time at time zone 'Asia/Shanghai')::date as visit_date, count(*) as orders, sum(o.order_amount) as amount from orders o where o.member_id = any(%(ids)s) group by o.member_id, visit_date order by o.member_id, visit_date; ```