跳到主要内容

携程大数据面试SQL-用户旅行目的地偏好分析

⚠️ 待修正

一、题目背景

这道题来自携程的数据分析岗面试。携程是在线旅游平台,数据分析师需要从海量业务数据中挖掘洞见。

业务场景:旅游出行相关需求在日常工作中频繁出现,这类型的SQL题是面试高频考点。

一、题目

携程需要为每个用户打上"首选目的地"标签,用于个性化推荐。给定 travel_order 表,记录了用户的旅行订单信息。

travel_order 用户旅行订单表:

order_iduser_iddestinationtravel_datedaysamount
ORD001U100三亚2024-01-1556200
ORD002U100丽江2024-03-2043800
ORD003U100三亚2024-07-1067200
ORD004U101成都2024-02-1032500
ORD005U101成都2024-05-1543100
ORD006U101重庆2024-08-2032200
ORD007U101成都2024-10-0154500
ORD008U102三亚2024-04-0544800
ORD009U102厦门2024-06-1832800
ORD010U102桂林2024-09-1243200
ORD011U103西安2024-03-0832100
ORD012U103丽江2024-07-2555200

要求:

  1. 找到每个用户去过次数最多的目的地(即"首选目的地")
  2. 如果有多个目的地并列第一,则取最近一次旅行的目的地
  3. 输出用户ID、首选目的地、去过的次数、最近一次前往日期、在该目的地的总消费金额

三、思路分析

本题是典型的"分组TopN"问题,考察 ROW_NUMBER() 窗口函数配合 PARTITION BY 进行组内排序的能力。关键在于多维度排序——先按次数降序,次数相同时按最近日期降序。

维度评分
题目难度⭐⭐⭐
题目清晰度⭐⭐⭐⭐
业务常见度⭐⭐⭐⭐⭐

解题思路:

  1. 第一步:按 (user_id, destination) 进行聚合,统计每个用户去每个目的地的次数、最近日期和总消费
  2. 第二步:使用 ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY visit_count DESC, last_visit_date DESC) 对每个用户的目的地进行排名
  3. 第三步:取 rn = 1 的记录即为每个用户的首选目的地
  4. 注意排序逻辑:visit_count DESC 保证次数最多的排前面,last_visit_date DESC 保证并列时取最近的

四、逐步推导

步骤1:按用户和目的地聚合统计

SELECT
user_id,
destination,
COUNT(*) AS visit_count,
MAX(travel_date) AS last_visit_date,
SUM(amount) AS total_amount
FROM travel_order
GROUP BY user_id, destination
ORDER BY user_id, visit_count DESC;

执行结果:

user_iddestinationvisit_countlast_visit_datetotal_amount
U100三亚22024-07-1013400
U100丽江12024-03-203800
U101成都32024-10-0110100
U101重庆12024-08-202200
U102三亚12024-04-054800
U102厦门12024-06-182800
U102桂林12024-09-123200
U103丽江12024-07-255200
U103西安12024-03-082100

步骤2:取每个用户的首选目的地

WITH dest_stats AS (
SELECT
user_id,
destination,
COUNT(*) AS visit_count,
MAX(travel_date) AS last_visit_date,
SUM(amount) AS total_amount
FROM travel_order
GROUP BY user_id, destination
),
ranked AS (
SELECT
user_id,
destination,
visit_count,
last_visit_date,
total_amount,
ROW_NUMBER() OVER (
PARTITION BY user_id
ORDER BY visit_count DESC, last_visit_date DESC
) AS rn
FROM dest_stats
)
SELECT
user_id,
destination AS top_destination,
visit_count,
last_visit_date,
total_amount
FROM ranked
WHERE rn = 1
ORDER BY user_id;

执行结果:

user_idtop_destinationvisit_countlast_visit_datetotal_amount
U100三亚22024-07-1013400
U101成都32024-10-0110100
U102桂林12024-09-123200
U103丽江12024-07-255200

五、常见坑点

坑1:NULL值的隐式处理 — 聚合函数跳过NULL,窗口函数不跳过。WHERE需显式过滤或用COALESCE替代。

坑2:数据类型不一致导致JOIN失效 — string vs int隐式转换可能触发全表扫描,性能骤降。

六、举一反三

  1. 增加时间维度对比:按天/周/月分组,观察指标的趋势和季节性波动

  2. 增加分组维度:按品类/地区/渠道拆解,发现差异化的业务洞察

  3. 设置预警阈值:在WHERE中加阈值判断,自动筛选异常数据

七、知识点总结

考点说明
ROW_NUMBER + 差值法连续问题经典解法:日期-行号=分组标识,相同差值同一连续段
SUM() OVER 累积求和窗口聚合实现滚动累计,配合ORDER BY实现时间轴展开
GROUP BY + 聚合函数分组聚合是数据分析的基础,配合HAVING筛选分组结果
日期函数DATEDIFF / DATE_ADD / unix_timestamp处理日期运算

八、建表语句和数据插入

点击展开 DDL & DML
CREATE TABLE travel_order (
order_id VARCHAR(10) PRIMARY KEY,
user_id VARCHAR(10),
destination VARCHAR(20),
travel_date DATE,
days INT,
amount DECIMAL(10,2)
);

INSERT INTO travel_order VALUES
('ORD001', 'U100', '三亚', '2024-01-15', 5, 6200),
('ORD002', 'U100', '丽江', '2024-03-20', 4, 3800),
('ORD003', 'U100', '三亚', '2024-07-10', 6, 7200),
('ORD004', 'U101', '成都', '2024-02-10', 3, 2500),
('ORD005', 'U101', '成都', '2024-05-15', 4, 3100),
('ORD006', 'U101', '重庆', '2024-08-20', 3, 2200),
('ORD007', 'U101', '成都', '2024-10-01', 5, 4500),
('ORD008', 'U102', '三亚', '2024-04-05', 4, 4800),
('ORD009', 'U102', '厦门', '2024-06-18', 3, 2800),
('ORD010', 'U102', '桂林', '2024-09-12', 4, 3200),
('ORD011', 'U103', '西安', '2024-03-08', 3, 2100),
('ORD012', 'U103', '丽江', '2024-07-25', 5, 5200);
📱关注公众号

「数据仓库技术」文章同步更新,不错过每一篇干货

微信公众号二维码
💬加群交流

备注「数据仓库技术」加入社群,每日一道大厂SQL真题

交流微信二维码

你可能还想看