select t.num as ConsecutiveNums from Logs t join Logs t2 on t.id = t2.id -1and t.num = t2.num join Logs t3 on t.id = t3.id -2and t.num = t3.num groupby1
将表自连接三次,分别表示连续的三行
连接条件:id连续,数字相同
使用group by去重(比如连续出现4次”1”会产生多条记录)
👍方法二(通用常用):
1 2 3 4 5 6 7 8 9
with tmp as( select id, num, id -row_number() over(partitionby num orderby id) as grp from Logs ) select distinct num as ConsecutiveNums from tmp groupby num, grp havingcount(*) >=3
==这种求连续xx的题目,都有一个套路,就是利用row_number(),作差==
row_number(partition by num order by id)为每个数字按id排序编号
with tmp as( select t2.name as Department ,t1.name as Employee ,t1.salary as Salary ,dense_rank() over(partitionby t2.name orderby t1.salary desc) as dense_rnk from Employee t1 leftjoin Department t2 on t1.departmentID = t2.id ) select Department ,Employee ,Salary from tmp where dense_rnk <=3 ;
with tmp as( select a.status,a.request_at from Trips a join Users b on a.client_id=b.users_id and b.banned='NO' join Users c on a.driver_id=c.users_id and c.banned='NO' where a.request_at>='2013-10-01'and a.request_at<='2013-10-03' ) select request_at asDay ,round( sum(casewhen status in('cancelled_by_driver','cancelled_by_client') then1else0end)*1.0/count(*) ,2 ) as `Cancellation Rate` from tmp groupby1
select customer_number from Orders groupby1 orderbycount(*) DESC limit 1
limit 1只能用在无并列的情况下
进阶:
如果有多位顾客订单数并列最多,你能找到他们所有的 customer_number 吗?
1 2 3 4 5 6 7 8 9 10 11
with tmp as( select customer_number ,count(*) as cnt ,rank() over(orderbycount(*) DESC) as rnk from Orders groupby1 ) select customer_number from tmp where rnk=1
with qualified as ( select*,id -row_number() over(orderby id) as grp from ( select*from Stadium where people>=100 ) t ) ,grp_cnt as( select*,count(*) over(partitionby grp) as cnt from qualified ) select id,visit_date,people from grp_cnt where cnt>=3 orderby visit_date
select t.name from SalesPerson t leftjoin ( selectdistinct a.sales_id from Orders a join Company b on a.com_id=b.com_id where b.name='RED' )t2 on t.sales_id=t2.sales_id where t2.sales_id isnull
一定要加 distinct,防止同一个销售多条 RED 订单重复匹配
方法2:not exists
1 2 3 4 5 6 7 8 9
select t.name from SalesPerson t wherenotexists( select1 from Orders a join Company b on a.com_id=b.com_id where b.name='RED' and a.sales_id=t.sales_id )
先想思路,不要全部连接,运用子查询更简单
not in和not exists的语法和区别?
一、NOT IN 语法
1 2 3
SELECT 列名 FROM 表A WHERE 列名 NOTIN (子查询结果集);
示例:
1 2 3 4 5 6 7 8
SELECT name FROM SalesPerson WHERE sales_id NOTIN ( SELECT sales_id -- 子查询必须返回单列 FROM Orders o JOIN Company c ON o.com_id = c.com_id WHERE c.name ='RED' );
⚠️ 重要注意:
子查询返回的列中不能有 NULL,否则结果会是空集(因为与NULL比较返回UNKNOWN)
适用于子查询结果集较小的情况
二、NOT EXISTS 语法
1 2 3
SELECT 列名 FROM 表A WHERENOTEXISTS (子查询);
示例:
1 2 3 4 5 6 7 8 9
SELECT name FROM SalesPerson s -- 需要给主查询表起别名 WHERENOTEXISTS ( SELECT1-- SELECT什么不重要,通常写1或* FROM Orders o JOIN Company c ON o.com_id = c.com_id WHERE o.sales_id = s.sales_id -- 关键:这里要关联主查询的表 AND c.name ='RED' );
关键点:
子查询中必须有关联条件(如 o.sales_id = s.sales_id)
子查询返回空结果时,NOT EXISTS 为真
不受 NULL 影响
针对本题的NOT EXISTS 写法(推荐,更安全)
1 2 3 4 5 6 7 8 9
SELECT name FROM SalesPerson s WHERENOTEXISTS ( SELECT1 FROM Orders o JOIN Company c ON o.com_id = c.com_id WHERE o.sales_id = s.sales_id AND c.name ='RED' );
执行逻辑图解
1 2 3 4 5 6 7 8 9 10
NOT IN 逻辑: 1. 先执行子查询,得到集合:{1, 4} (向RED销售的sales_id) 2. 检查主查询:sales_id 是否在 {1, 4} 中? 3. 返回不在集合中的:2, 3, 5 → Amy, Mark, Alex
select activity_date asday ,count(distinct user_id) as active_users from Activity where activity_date >'2019-07-27'-interval30day and activity_date <='2019-07-27' groupby1
select id ,sum(casemonthwhen'Jan'then revenue end) as Jan_Revenue ,sum(casemonthwhen'Feb'then revenue end) as Feb_Revenue ,sum(casemonthwhen'Mar'then revenue end) as Mar_Revenue ,sum(casemonthwhen'Apr'then revenue end) as Apr_Revenue ,sum(casemonthwhen'May'then revenue end) as May_Revenue ,sum(casemonthwhen'Jun'then revenue end) as Jun_Revenue ,sum(casemonthwhen'Jul'then revenue end) as Jul_Revenue ,sum(casemonthwhen'Aug'then revenue end) as Aug_Revenue ,sum(casemonthwhen'Sep'then revenue end) as Sep_Revenue ,sum(casemonthwhen'Oct'then revenue end) as Oct_Revenue ,sum(casemonthwhen'Nov'then revenue end) as Nov_Revenue ,sum(casemonthwhen'Dec'then revenue end) as Dec_Revenue from Department groupby1
注意:聚合函数,sum作用是为了group by,选择max,min,avg同样可以
表 Department:
1 2 3 4 5 6 7
+---------------+---------+ | Column Name | Type | +---------------+---------+ | id | int | | revenue | int | | month | varchar | +---------------+---------+
select*from( select product_id, 'store1'as store, store1 as price from Products
unionall
select product_id, 'store2'as store, store2 as price from Products
unionall
select product_id, 'store3'as store, store3 as price from Products ) t where price isnot null
经典的 SQL 数据转换问题,需要将列转行(宽表转长表)
union all不去重效率更高
表:Products
1 2 3 4 5 6 7 8
+-------------+---------+ | Column Name | Type | +-------------+---------+ | product_id | int | | store1 | int | | store2 | int | | store3 | int | +-------------+---------+
with ranked as ( select employee_id ,rating as r1 ,lead(rating,1) over(partitionby employee_id orderby review_date DESC) as r2 ,lead(rating,2) over(partitionby employee_id orderby review_date DESC) as r3 ,row_number() over(partitionby employee_id orderby review_date DESC) as rn from performance_reviews ) select t.employee_id ,t2.name ,t.r1-t.r3 as improvement_score from ranked t join employees t2 on t.employee_id=t2.employee_id where t.rn=1 and t.r3 isnot null and t.r1 > t.r2 and t.r2 > t.r3 orderby improvement_score DESC,t2.name ASC;
with week_sum as( select employee_id ,date_sub(meeting_date, interval weekday(meeting_date) day) as week_start ,sum(duration_hours) as sum_hours from meetings groupby1,2 ) select t.employee_id ,t2.employee_name ,t2.department ,sum(casewhen sum_hours >20then1else0end) as meeting_heavy_weeks from week_sum t join employees t2 on t.employee_id=t2.employee_id groupby1,2,3 having meeting_heavy_weeks >=2 orderby meeting_heavy_weeks DESC,employee_name ASC
with customer_metrics as( select customer_id ,count(order_id) as total_orders ,sum( casewhen (TIME(order_timestamp) between'11:00:00'and'14:00:00' orTIME(order_timestamp) between'18:00:00'and'21:00:00') then1end ) as peak_cnt ,sum(if(order_rating isnot null,1,0)) as rated_cnt ,sum(order_rating) as sum_rating from restaurant_orders groupby1 having total_orders>=3 and rated_cnt>0 and peak_cnt*10>= total_orders*6 and rated_cnt*2>= total_orders ) select customer_id ,total_orders ,round(peak_cnt*100.0/ total_orders, 0) as peak_hour_percentage ,round(sum_rating*1.0/ rated_cnt, 2) as average_rating from customer_metrics where sum_rating*1.0/rated_cnt >=4.0 orderby average_rating DESC,customer_id DESC;
思路拆解
CTE 分组统计每个客户指标
total_orders:总订单数(COUNT (order_id))
peak_cnt:高峰时段订单数(小时 11-14 或 18-21)
rated_cnt:有评分的订单数(order_rating IS NOT NULL)
sum_rating:所有有效评分总和
衍生计算:
peak_hour_percentage = 高峰订单 / 总订单 *100,保留 0 位小数
average_rating = 总分 / 有效评分数,保留 2 位小数
小时提取:TIME(order_timestamp) 直接获取时间戳的 时间部分
百分比计算:必须乘 1.0 / 100.0 浮点运算,避免整数除法丢失小数
NULL 评分处理:SUM(CASE WHEN order_rating IS NOT NULL) 只统计有效评分
with user_latest as( select*from( select user_id ,plan_name as current_plan ,monthly_amount as current_monthly_amount ,event_type as last_event_type ,row_number() over(partitionby user_id orderby event_date DESC) as rn from subscription_events ) t where rn=1 ) ,user_agg as( select t1.user_id ,t2.current_plan ,t2.current_monthly_amount ,t2.last_event_type ,sum(casewhen t1.event_type='downgrade'then1else0end) as cnt_down ,max(t1.monthly_amount) as max_historical_amount ,datediff(max(event_date),min(event_date)) as days_as_subscriber from subscription_events t1 innerjoin user_latest t2 on t1.user_id=t2.user_id groupby1,2,3,4 ) select user_id ,current_plan ,current_monthly_amount ,max_historical_amount ,days_as_subscriber from user_agg where last_event_type !='cancel' and cnt_down >=1 and current_monthly_amount*2< max_historical_amount and days_as_subscriber >=60 orderby days_as_subscriber DESC, user_id ASC;
with tmp as( -- 按日期和类别聚合,计算每天的点赞数和转发数 select t2.tag ,date(t1.start_time) as dt ,sum(t1.if_like) as daily_likes ,sum(t1.if_retweet) as daily_retweets from tb_user_video_log t1 join tb_video_info t2 on t1.video_id = t2.video_id groupby1,2 ) , tmp2 as( -- 使用窗口函数计算近7天的滑动窗口指标 select* -- 近7天总点赞量(当前行及前6行) ,sum(daily_likes) over(partitionby tag orderby dt rowsbetween6 preceding andcurrentrow) as week_likes -- 近7天最大单日转发量 ,max(daily_retweets) over(partitionby tag orderby dt rowsbetween6 preceding andcurrentrow) as max_retweets from tmp ) -- 筛选国庆头3天,按视频类别降序、日期升序排序 select tag ,dt ,week_likes ,max_retweets from tmp2 where dt >='2021-10-01' and dt <='2021-10-03' orderby tag DESC,dt ASC