目录
1.访问次数最多的5个用户
2.近 30 天的每日活跃用户数
3.获取1-180天注册活跃留存表
4.学生名次统计
5.最近连续3周连续活跃用户数
6.最近7天内连续3天活跃用户数
7.餐馆营业额变化增长
相关拓展
1.访问次数最多的5个用户
输出每个app 下访问次数最多的5个用户 返回字段app_id, top_5_user_id
app_id user_id datetime A
u1
2020-01-01 00:00:00
B
u2
2020-01-01 00:00:01
A
u1
2020-01-01 00:00:02
C
u2
2020-01-01 01:00:00
WITH ranked_users AS (
SELECT
app_id,
user_id,
COUNT(*) AS visit_count,
ROW_NUMBER() OVER (PARTITION BY app_id ORDER BY COUNT(*) DESC) AS rank
FROM your_table
GROUP BY app_id, user_id
)
SELECT
app_id,
STRING_AGG(user_id, ', ' ORDER BY visit_count DESC) AS top_5_user_id
FROM ranked_users
WHERE rank <= 5
GROUP BY app_id;
2.近 30
天的每日活跃用户数
表:
Activity
+---------------+---------+ | Column Name | Type | +---------------+---------+ | user_id | int | | session_id | int | | activity_date | date | | activity_type | enum | +---------------+---------+ 该表没有包含重复数据。 activity_type 列是 ENUM(category) 类型, 从 ('open_session', 'end_session', 'scroll_down', 'send_message') 取值。 该表记录社交媒体网站的用户活动。 注意,每个会话只属于一个用户。编写解决方案,统计截至
2019-07-27
(包含2019-07-27),近30
天的每日活跃用户数(当天只要有一条活动记录,即为活跃用户)。
SELECT
activity_date AS day,
COUNT(DISTINCT user_id) AS active_users
FROM
Activity
WHERE
activity_date BETWEEN DATE_SUB('2019-07-27', INTERVAL 29 DAY) AND '2019-07-27'
GROUP BY
activity_date
ORDER BY
activity_date;
3.获取1-180天注册活跃留存表
现有一个用户活跃表user_active、用户注册表user_regist,
用户字段均为user_id;
设计一张1-180天注册活跃留存表包括以下字段
regist_date -- 注册日期
date_diff -- 日期差
rate -- 留存率(从注册日期起往后180天内注册用户里的活跃用户数/注册日期当天注册的用户总数)用户注册表 (
user_regist
):
user_id regist_date 1 2021-01-01 2 2021-01-02 3 2021-01-03 用户活跃表 (
user_active
):
user_id active_date 1 2021-01-02 1 2021-01-10 2 2021-01-03 3 2021-01-03 3 2021-01-04 3 2021-02-02
-- 定义包含总分和用户排名的CTE(公共表表达式)
WITH TotalScore AS (
-- 计算每个用户的注册日期及注册总数
SELECT
user_id, -- 用户ID
regist_date, -- 注册日期
COUNT(*) OVER (PARTITION BY regist_date) AS regist_count -- 计算每个注册日期的用户数
FROM
user_regist -- 注册表
WHERE
dt >= DATE_SUB(current_date(), 180) -- 只考虑过去180天内的数据
),
RandTotalScore AS (
-- 对每个用户的活跃日期和注册日期计算日期差并统计用户数
SELECT
t1.regist_date, -- 注册日期
t1.regist_count, -- 当前日期的总注册用户
DATEDIFF(t2.active_date, t1.regist_date) AS date_diff, -- 计算活跃日期与注册日期的日期差
COUNT(*) AS user_count -- 统计每个日期差的用户总数
FROM
TotalScore t1 -- 引用先前计算的注册信息
LEFT JOIN
( -- 子查询:选择活跃用户及其活跃日期
SELECT
user_id, -- 用户ID
to_date(active_date) as active_date -- 活跃日期转换为日期格式
FROM
user_active -- 活跃表
GROUP BY
user_id, to_date(active_date) -- 按用户和活跃日期分组以避免重复
) t2 ON t1.user_id = t2.user_id -- 将注册表与活跃表连接
WHERE
DATEDIFF(t2.active_date, t1.regist_date) BETWEEN 1 AND 180 -- 筛选日期差在1到180天之间的数据
GROUP BY
t1.regist_date, DATEDIFF(t2.active_date, t1.regist_date) -- 按注册日期和日期差分组
)
-- 选择最终的结果集,包括日期差、用户数和留存率
SELECT
regist_date, -- 注册日期
date_diff, -- 日期差
user_count / regist_count AS rate -- 计算留存率
FROM
RandTotalScore; -- 引用计算好的活跃信息
4.学生名次统计
1)找出语数英每门课前三名的学生
2)单科分数有低于80分的学生的总分排名
1). 每门课前三名的学生
SELECT name, lesson, goal
FROM (
SELECT name, lesson, goal,
RANK() OVER (PARTITION BY lesson ORDER BY goal DESC) as rank
FROM scores
) ranked
WHERE rank <= 3;
2). 单科分数有低于80分的学生的总分排名
-- 首先,创建一个子查询或公共表表达式 (CTE),以获取得分低于80分的学生名单
WITH LowScorers AS (
SELECT DISTINCT name
FROM scores
WHERE goal < 80
)
-- 接下来,使用此子查询来计算这些学生的总分,并对其进行排名
SELECT
name,
SUM(goal) AS total_score,
RANK() OVER (ORDER BY SUM(goal) DESC) as rank
FROM
scores
WHERE
name IN (SELECT name FROM LowScorers)
GROUP BY
name
ORDER BY
total_score DESC;
5.最近连续3周连续活跃用户数
源表dws_uv_detail示例数据
mid_id dt wk_dt 1001 2019-02-06 2019-02-04_2019-02-10 1001 2019-02-07 2019-02-04_2019-02-10 1001 2019-02-08 2019-02-04_2019-02-10 1001 2019-02-09 2019-02-04_2019-02-10 1001 2019-02-10 2019-02-04_2019-02-10 1001 2019-02-11 2019-02-11_2019-02-17 1002 2019-02-12 2019-02-11_2019-02-17 1003 2019-02-10 2019-02-04_2019-02-10 1004 2019-02-09 2019-02-04_2019-02-10 1004 2019-02-10 2019-02-04_2019-02-10 1004 2019-02-11 2019-02-11_2019-02-17 1004 2019-02-12 2019-02-11_2019-02-17 1005 2019-02-08 2019-02-04_2019-02-10 1005 2019-02-09 2019-02-04_2019-02-10 1005 2019-02-10 2019-02-04_2019-02-10
目标表ads_continuity_wk_count示例数据
create table ads_continuity_wk_count(
dt wk_dt continuity_count 2019-02-12 2019-02-06_2019-02-12 156 2019-02-13 2019-02-07_2019-02-13 162 2019-02-14 2019-02-08_2019-02-14 170
dt string COMMENT '统计日期,一般用结束周周日日期,如果每天计算一次,可用当天日期',
wk_dtstring COMMENT '持续时间',
continuity_count bigint '最近3周的活跃总次数'
)
with dws_uv_detail_convert as(
select mid_id,wk_dt
from dws_uv_detail
where wk_dt>=concat(date_add(next_day(CURRENT_DATE, 'MO'), -7*3), '_', date_add(next_day(CURRENT_DATE, 'MO'), -7*2-1))
and wk_dt<=concat(date_add(next_day(CURRENT_DATE, 'MO'), -7), '_', date_add(next_day(CURRENT_DATE, 'MO'), -1))
group by mid_id,wk_dt
)
select
CURRENT_DATE,
concat(date_add(next_day(CURRENT_DATE, 'MO'), -7*3), '_', date_add(next_day(CURRENT_DATE, 'MO'), -1)),
wk_dt,
count(*) continuity_count
from
(
select mid_id
from dws_uv_detail_wk_convert
group by mid_id
having count(*) = 3
) t1;
where wk_dt>=concat(date_add(next_day(CURRENT_DATE, 'MO'), -7*3), '_', date_add(next_day(CURRENT_DATE, 'MO'), -7*2-1))
and wk_dt<=concat(date_add(next_day(CURRENT_DATE, 'MO'), -7), '_', date_add(next_day(CURRENT_DATE, 'MO'), -1))
next_day(CURRENT_DATE, 'MO')
的含义是:找到从当前日期(CURRENT_DATE)开始的下一个星期一(Monday)的日期。
这个where条件筛选出了连续三周的数据。具体来说:
- 第一周:从 3 周前的周一到 2 周前的周日
- 第二周:从 2 周前的周一到 1 周前的周日
- 第三周:从 1 周前的周一到本周的周日
concat确保生成的日期范围字符串格式与
wk_dt
列的格式一致,便于比较和匹配。
6.最近7天内连续3天活跃用户数
源表dws_uv_detail和目标表ads_continuity_uv_count和题5保持一致
-- 创建目标表ads_continuity_uv_count
CREATE TABLE IF NOT EXISTS ads_continuity_uv_count (
dt STRING COMMENT '统计日期',
wk_dt STRING COMMENT '最近7天日期范围',
continuity_count BIGINT COMMENT '连续3天活跃用户数'
) COMMENT '最近7天连续3天活跃用户数';
-- 插入数据到ads_continuity_uv_count表
INSERT OVERWRITE TABLE ads_continuity_uv_count
SELECT
CURRENT_DATE AS dt,
CONCAT(DATE_SUB(CURRENT_DATE, 6), '_', CURRENT_DATE) wk_dt,
COUNT(DISTINCT mid_id) AS continuity_count
FROM (
SELECT
mid_id
FROM (
SELECT
mid_id,
dt,
DATE_SUB(dt, RANK() OVER (PARTITION BY mid_id ORDER BY dt)) AS date_group
FROM dws_uv_detail
WHERE dt BETWEEN DATE_SUB(CURRENT_DATE, 6) AND CURRENT_DATE
-- 筛选最近7天的数据
) t1
GROUP BY mid_id, date_group
HAVING COUNT(*) >= 3
-- 找出连续活跃3天及以上的用户
) t2;
-- 解释:
-- 1. 首先在WHERE子句中筛选出最近7天的数据。
-- 2. 使用窗口函数RANK()给每个用户的活跃日期进行排序。
-- 3. 用日期减去排名,得到date_group。连续的日期会得到相同的date_group值。
-- 4. 按mid_id和date_group分组,统计每组的记录数。记录数>=3的就是连续3天及以上活跃的用户。
-- 5. 最后统计符合条件的不同用户数量,即为连续3天活跃的用户数。
-- 6. CURRENT_DATE用于动态获取当前日期,使查询可以自动适应不同的执行日期。
-- 7. CONCAT(DATE_SUB(CURRENT_DATE, 6), '_', CURRENT_DATE) 生成最近7天的日期范围字符串。
7.餐馆营业额变化增长
Customer
+---------------+---------+ | Column Name | Type | +---------------+---------+ | customer_id | int | | name | varchar | | visited_on | date | | amount | int | +---------------+---------+ 在 SQL 中,(customer_id, visited_on) 是该表的主键。 该表包含一家餐馆的顾客交易数据。 visited_on 表示 (customer_id) 的顾客在 visited_on 那天访问了餐馆。 amount 是一个顾客某一天的消费总额。你是餐馆的老板,现在你想分析一下可能的营业额变化增长(每天至少有一位顾客)。
计算以 7 天(某日期 + 该日期前的 6 天)为一个时间段的顾客消费平均值。
average_amount
要 保留两位小数。结果按
visited_on
升序排序。
WITH daily_totals AS (
SELECT visited_on, SUM(amount) as daily_amount
FROM Customer
GROUP BY visited_on
),
rolling_window AS (
SELECT
visited_on,
SUM(daily_amount) OVER (
ORDER BY visited_on
ROWS BETWEEN 6 PRECEDING AND CURRENT ROW
) as amount,
AVG(daily_amount) OVER (
ORDER BY visited_on
ROWS BETWEEN 6 PRECEDING AND CURRENT ROW
) as average_amount,
ROW_NUMBER() OVER (ORDER BY visited_on) as row_num
FROM daily_totals
)
SELECT
visited_on,
amount,
ROUND(average_amount, 2) as average_amount
FROM rolling_window
WHERE row_num >= 7
ORDER BY visited_on;
-- ORDER BY visited_on RANGE BETWEEN 6 PRECEDING AND CURRENT ROW 定义,意味着它将包括当前行的日期和之前6天的数据,从而确保每次计算都覆盖了7天的数据。
-- RDER BY visited_on RANGE BETWEEN CURRENT ROW AND INTERVAL 6 DAY FOLLOWING当前行的日期和之后6天的数据,从而确保每次计算都覆盖了7天的数据
- RANGE BETWEEN: 适用于处理连续的数值或日期范围,如时间序列数据。
这会计算当前行薪水前后1000范围内的薪水总和
SUM(salary) OVER (ORDER BY salary RANGE BETWEEN 1000 PRECEDING AND 1000 FOLLOWING)
- ROWS BETWEEN: 适用于需要精确指定行数的情况,不考虑数值的连续性
这会计算当前行前后各2行的薪水总和。
SUM(salary) OVER (ORDER BY salary ROWS BETWEEN 2 PRECEDING AND 2 FOLLOWING)
相关拓展
Hive SQL进阶:掌握间隔连续查询技巧,优化大数据分析(一)_hive查询结果中间无间隔-CSDN博客
17道Hive SQL经典训练提升题_15道hive sql经典训练提升题-CSDN博客
Hive SQL 五大经典面试题_hivesql面试题-CSDN博客