SQL总结-排名的使用

image.png

##一、通过排名或者范围条件连表筛选特殊行

  • 第一行
  • 最后一行
  • 区间(第一行到第二行或者连续区间)
  • 找中位数
  • 通过排名进行分组或者连续区间

######1.使用条件筛选连表找区间

Employee 表保存了一年内的薪水信息。

请你编写 SQL 语句,来查询每个员工每个月最近三个月的累计薪水(不包括当前统计月,不足三个月也要计算)。

结果请按 ‘Id’ 升序,然后按 ‘Month’ 降序显示。

示例:
输入:

IdMonthSalary
1120
2120
1230
2230
3240
1340
3360
1460
3470
输出:
IdMonthSalary
1390
1250
1120
2120
33100
3240

解释:

员工 ‘1’ 除去最近一个月(月份 ‘4’),有三个月的薪水记录:月份 ‘3’ 薪水为 40,月份 ‘2’ 薪水为 30,月份 ‘1’ 薪水为 20。

所以近 3 个月的薪水累计分别为 (40 + 30 + 20) = 90,(30 + 20) = 50 和 20。

IdMonthSalary
1390
1250
1120
员工 ‘2’ 除去最近的一个月(月份 ‘2’)的话,只有月份 ‘1’ 这一个月的薪水记录。
IdMonthSalary
2120
员工 ‘3’ 除去最近一个月(月份 ‘4’)后有两个月,分别为:月份 ‘4’ 薪水为 60 和 月份 ‘2’ 薪水为 40。所以各月的累计情况如下:
IdMonthSalary
33100
3240

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/find-cumulative-salary-of-an-employee
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

#代码中使用一个范围连表,在使用最小月份去重最后一个月

# Write your MySQL query statement below
select d1.Id,d1.Month,d1.Salary
from

  (select a.Id,a.Month,sum(b.Salary) Salary
  from Employee a 
  left join Employee  b
  on a.Id = b.Id and a.month >= b.month and b.month >=a.month-2
  group by a.Id,a.Month
  ) d1

left join

  (select c.Id,Max(c.Month) maxMonth
  from Employee c
  group by c.Id) d2

on d1.id = d2.id
where d1.Month < d2.maxMonth
order by d1.id asc,d1.Month desc

######2.使用排名来找中位数
Employee 表包含所有员工。Employee 表有三列:员工Id,公司名和薪水。

±----±-----------±-------+
|Id | Company | Salary |
±----±-----------±-------+
|1 | A | 2341 |
|2 | A | 341 |
|3 | A | 15 |
|4 | A | 15314 |
|5 | A | 451 |
|6 | A | 513 |
|7 | B | 15 |
|8 | B | 13 |
|9 | B | 1154 |
|10 | B | 1345 |
|11 | B | 1221 |
|12 | B | 234 |
|13 | C | 2345 |
|14 | C | 2645 |
|15 | C | 2645 |
|16 | C | 2652 |
|17 | C | 65 |
±----±-----------±-------+
请编写SQL查询来查找每个公司的薪水中位数。挑战点:你是否可以在不使用任何内置的SQL函数的情况下解决此问题。

±----±-----------±-------+
|Id | Company | Salary |
±----±-----------±-------+
|5 | A | 451 |
|6 | A | 513 |
|12 | B | 234 |
|9 | B | 1154 |
|14 | C | 2645 |
±----±-----------±-------+

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/median-employee-salary
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

#下面先进行排名,然后使用in()进行偶数个数组的中位数和奇数个数组的中位数

# Write your MySQL query statement below
select c1.id,c1.company,c1.salary
from 
  (select a.id,a.Company,a.salary,
  case @cp when a.Company then @rk:=@rk+1 else @rk:=1 end rk,
  @cp:=a.Company
  from Employee a,(select @rk:=0,@cp:='') b
  order by a.Company,a.salary) c1
left join
  (select company,count(1)/2 p
  from Employee
  group by company ) c2
on c1.company=c2.company
where c1.rk in (c2.p+0.5,c2.p+1,c2.p)

######3.使用排名找特定的某一行
表: Users

±---------------±--------+
| Column Name | Type |
±---------------±--------+
| user_id | int |
| join_date | date |
| favorite_brand | varchar |
±---------------±--------+
user_id 是该表的主键
表中包含一位在线购物网站用户的个人信息,用户可以在该网站出售和购买商品。
表: Orders

±--------------±--------+
| Column Name | Type |
±--------------±--------+
| order_id | int |
| order_date | date |
| item_id | int |
| buyer_id | int |
| seller_id | int |
±--------------±--------+
order_id 是该表的主键
item_id 是 Items 表的外键
buyer_id 和 seller_id 是 Users 表的外键
表: Items

±--------------±--------+
| Column Name | Type |
±--------------±--------+
| item_id | int |
| item_brand | varchar |
±--------------±--------+
item_id 是该表的主键

写一个 SQL 查询确定每一个用户按日期顺序卖出的第二件商品的品牌是否是他们最喜爱的品牌。如果一个用户卖出少于两件商品,查询的结果是 no 。

题目保证没有一个用户在一天中卖出超过一件商品

下面是查询结果格式的例子:

Users table:
±--------±-----------±---------------+
| user_id | join_date | favorite_brand |
±--------±-----------±---------------+
| 1 | 2019-01-01 | Lenovo |
| 2 | 2019-02-09 | Samsung |
| 3 | 2019-01-19 | LG |
| 4 | 2019-05-21 | HP |
±--------±-----------±---------------+

Orders table:
±---------±-----------±--------±---------±----------+
| order_id | order_date | item_id | buyer_id | seller_id |
±---------±-----------±--------±---------±----------+
| 1 | 2019-08-01 | 4 | 1 | 2 |
| 2 | 2019-08-02 | 2 | 1 | 3 |
| 3 | 2019-08-03 | 3 | 2 | 3 |
| 4 | 2019-08-04 | 1 | 4 | 2 |
| 5 | 2019-08-04 | 1 | 3 | 4 |
| 6 | 2019-08-05 | 2 | 2 | 4 |
±---------±-----------±--------±---------±----------+

Items table:
±--------±-----------+
| item_id | item_brand |
±--------±-----------+
| 1 | Samsung |
| 2 | Lenovo |
| 3 | LG |
| 4 | HP |
±--------±-----------+

Result table:
±----------±-------------------+
| seller_id | 2nd_item_fav_brand |
±----------±-------------------+
| 1 | no |
| 2 | yes |
| 3 | yes |
| 4 | no |
±----------±-------------------+

id 为 1 的用户的查询结果是 no,因为他什么也没有卖出
id为 2 和 3 的用户的查询结果是 yes,因为他们卖出的第二件商品的品牌是他们自己最喜爱的品牌
id为 4 的用户的查询结果是 no,因为他卖出的第二件商品的品牌不是他最喜爱的品牌

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/market-analysis-ii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

#代码中先进行排序,直接使用序号进行过滤

# Write your MySQL query statement below
select u.user_id seller_id,if(i.item_brand=u.favorite_brand,'yes','no') 2nd_item_fav_brand
from Users u  left join
   (select d.seller_id,d.order_date,d.rank
   from
       (select c.seller_id,c.order_date,case when @seller=c.seller_id then @rk:=@rk+1 else @rk:=1 end rank,   @seller:=c.seller_id temp
       from
          (select * from Orders order by seller_id,order_date) c,(select @seller:=-1,@rk:=-1) b
       ) d 
   where d.rank =2) o 
on u.user_id = o.seller_id
left join Orders t on o.seller_id = t.seller_id and o.order_date = t.order_date
left join Items i on t.item_id = i.item_id
order by u.user_id

##二、通过排名进行分组或者连续区间
######为什么要使用这种方式分组,因为有时候你的分组维度是不存在的,你必须自己制造这个维度!比如如下这题:

Table: Failed

±-------------±--------+
| Column Name | Type |
±-------------±--------+
| fail_date | date |
±-------------±--------+
该表主键为 fail_date。
该表包含失败任务的天数.
Table: Succeeded

±-------------±--------+
| Column Name | Type |
±-------------±--------+
| success_date | date |
±-------------±--------+
该表主键为 success_date。
该表包含成功任务的天数.

系统 每天 运行一个任务。每个任务都独立于先前的任务。任务的状态可以是失败或是成功。

编写一个 SQL 查询 2019-01-01 到 2019-12-31 期间任务连续同状态 period_state 的起止日期(start_date 和 end_date)。即如果任务失败了,就是失败状态的起止日期,如果任务成功了,就是成功状态的起止日期。

最后结果按照起始日期 start_date 排序

查询结果样例如下所示:

Failed table:
±------------------+
| fail_date |
±------------------+
| 2018-12-28 |
| 2018-12-29 |
| 2019-01-04 |
| 2019-01-05 |
±------------------+

Succeeded table:
±------------------+
| success_date |
±------------------+
| 2018-12-30 |
| 2018-12-31 |
| 2019-01-01 |
| 2019-01-02 |
| 2019-01-03 |
| 2019-01-06 |
±------------------+

Result table:
±-------------±-------------±-------------+
| period_state | start_date | end_date |
±-------------±-------------±-------------+
| succeeded | 2019-01-01 | 2019-01-03 |
| failed | 2019-01-04 | 2019-01-05 |
| succeeded | 2019-01-06 | 2019-01-06 |
±-------------±-------------±-------------+

结果忽略了 2018 年的记录,因为我们只关心从 2019-01-01 到 2019-12-31 的记录
从 2019-01-01 到 2019-01-03 所有任务成功,系统状态为 “succeeded”。
从 2019-01-04 到 2019-01-05 所有任务失败,系统状态为 “failed”。
从 2019-01-06 到 2019-01-06 所有任务成功,系统状态为 “succeeded”。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/report-contiguous-dates
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

#####代码中使用一个排名的序号和最小值得差值,进行分组!只要是连续的区间的,这个差值一定是相同

/* Write your PL/SQL query statement below */
SELECT *
FROM (
	SELECT 'failed' AS period_state, to_char(MIN(fail_date), 'yyyy-MM-dd') AS start_date
		, to_char(MAX(fail_date), 'yyyy-MM-dd') AS end_date
	FROM (
		SELECT a.fail_date, fail_date - minDate - rn AS groupId
		FROM (
			SELECT a.fail_date, rownum AS rn, MIN(fail_date) OVER (ORDER BY fail_date) AS minDate
			FROM Failed a
			WHERE a.fail_date >= '2019-01-01'
				AND a.fail_date <= '2019-12-31'
		) a
	) a
	GROUP BY a.groupId
	UNION ALL
	SELECT 'succeeded' AS period_state, to_char(MIN(success_date), 'yyyy-MM-dd') AS start_date
		, to_char(MAX(success_date), 'yyyy-MM-dd') AS end_date
	FROM (
		SELECT a.success_date, success_date - minDate - rn AS groupId
		FROM (
			SELECT a.success_date, rownum AS rn, MIN(success_date) OVER (ORDER BY success_date) AS minDate
			FROM Succeeded a
			WHERE a.success_date >= '2019-01-01'
				AND a.success_date <= '2019-12-31'
		) a
	) a
	GROUP BY a.groupId
) a
ORDER BY a.start_date

######找连续区间
表:Logs

±--------------±--------+
| Column Name | Type |
±--------------±--------+
| log_id | int |
±--------------±--------+
id 是上表的主键。
上表的每一行包含日志表中的一个 ID。

后来一些 ID 从 Logs 表中删除。编写一个 SQL 查询得到 Logs 表中的连续区间的开始数字和结束数字。

将查询表按照 start_id 排序。

查询结果格式如下面的例子:

Logs 表:
±-----------+
| log_id |
±-----------+
| 1 |
| 2 |
| 3 |
| 7 |
| 8 |
| 10 |
±-----------+

结果表:
±-----------±-------------+
| start_id | end_id |
±-----------±-------------+
| 1 | 3 |
| 7 | 8 |
| 10 | 10 |
±-----------±-------------+
结果表应包含 Logs 表中的所有区间。
从 1 到 3 在表中。
从 4 到 6 不在表中。
从 7 到 8 在表中。
9 不在表中。
10 在表中。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/find-the-start-and-end-number-of-continuous-ranges
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

# Write your MySQL query statement below
select Min(b.log_id) start_id,Max(b.log_id) end_id       
from 
(select l.log_id,if(@last_vaule=-1 or l.log_id-@last_vaule<>1,@group_id:=@group_id+1,@group_id) group_id,@last_vaule:=l.log_id
from Logs l,(select @last_vaule:=-1,@group_id:=0) a) b 
group by b.group_id
<think>好的,我现在需要帮助用户解决在SQL中实现分数排名的问题,特别是关于使用的语法和特殊字符。首先,我得回忆一下SQL中处理排名的常用方法,可能涉及到窗口函数,比如RANK()、DENSE_RANK()或者ROW_NUMBER()这些函数。 用户提到的特殊字符或语法,可能是指在使用这些窗口函数时需要用到的一些符号或关键字。比如,在窗口函数中,通常会用到OVER子句,以及PARTITION BY和ORDER BY这样的关键字。例如,RANK() OVER (ORDER BY score DESC)这样的结构。这里的关键点可能是OVER、PARTITION BY和ORDER BY的使用,以及是否需要使用特定的符号,比如括号或者分号。 接下来,我需要确认具体的语法结构。比如,RANK()函数后面必须跟OVER子句,而OVER里面需要指定排序的方式。如果有分组的需求,还需要使用PARTITION BY来分区。例如,如果要对不同班级的学生成绩进行排名,可能会写成RANK() OVER (PARTITION BY class_id ORDER BY score DESC)。这时候,PARTITION BY和ORDER BY都是必须的关键字,而括号内的内容定义了窗口的范围和排序方式。 另外,用户提供的引用中有提到一些SQL语句,比如在引用[2]中的条件查询,虽然不直接涉及排名,但可以辅助理解SQL的基本结构。引用[3]中的Scores表结构显示有id和score字段,这可能是一个示例表,用来演示如何为分数排名。例如,针对这个表,排名可能需要根据score字段降序排列,使用RANK()函数。 我还需要考虑不同的排名函数之间的区别。比如,RANK()会产生并列排名,并且会跳过后续的排名,而DENSE_RANK()则不会跳过排名。ROW_NUMBER()则会给每一行一个唯一的序号,即使分数相同。用户可能需要了解这些函数的不同之处,以便选择合适的函数。 关于特殊字符,可能需要提到在SQL语句中使用括号来包裹窗口函数的定义,例如RANK() OVER (ORDER BY score DESC)。另外,分号作为语句结束符可能在某些数据库中是可选的,但在某些情况下是必须的。此外,反引号或者双引号可能用于字段名或表名,特别是当名称包含特殊字符或保留字时,比如引用[2]中的`name`字段使用了反引号。 然后,我需要结合用户提供的引用内容。例如,引用[3]中的Scores表结构,可以构造一个实际的例子。比如,给出一个具体的SQL查询使用RANK()函数为score字段生成排名,并解释各个部分的作用。例如: SELECT id, score, RANK() OVER (ORDER BY score DESC) AS rank FROM Scores; 这里,RANK()是函数,OVER后面的括号内定义了排序方式,ORDER BY score DESC表示按分数降序排列,AS rank是别名的使用。这个例子可以展示如何实现分数排名,并指出使用的语法和特殊字符,如OVER、ORDER BY和括号。 此外,用户可能需要知道如何分区排名,比如在每个班级内排名,这时候PARTITION BY就派上用场了。但根据用户的问题,可能主要关注全局排名,所以可能不需要分区。 还要注意,不同的数据库系统可能会有细微的语法差异,但窗口函数的标准语法在大多数现代数据库(如MySQL 8.0+、PostgreSQLSQL Server等)中是支持的。需要确认用户使用数据库是否支持窗口函数,尤其是像MySQL在8.0之前不支持RANK()函数。 最后,总结使用的特殊字符和语法,包括函数名后的括号,OVER子句的结构,ORDER BY的使用,以及可能的PARTITION BY。同时,提到别名AS关键字的使用,以及分号作为语句结束符的情况。</think>在SQL中实现分数排名主要依赖窗口函数,需要掌握以下特殊语法和关键字符: ### 核心语法结构 ```sql SELECT column1, column2, RANK() OVER (ORDER BY score_column DESC) AS rank FROM table; ``` ### 关键语法元素 1. **窗口函数**:$RANK()$、$DENSE\_RANK()$、$ROW\_NUMBER()$ - `RANK()`:并列排名会跳过后续名次(如:1,1,3) - `DENSE_RANK()`:并列排名不跳号(如:1,1,2) - `ROW_NUMBER()`:强制连续编号(如:1,2,3) 2. **OVER子句**:`OVER()`是窗口函数的核心标识符,必须与窗口函数成对出现 3. **排序控制**: - `ORDER BY score DESC`:指定排名依据字段和排序方向 - `DESC`/`ASC`:决定降序/升序排列 4. **分组排名**: ```sql RANK() OVER (PARTITION BY group_column ORDER BY score DESC) ``` - `PARTITION BY`:实现分组内的独立排名(如按班级分组排名) ### 特殊符号说明 - **括号`()`**:包裹窗口函数参数和OVER子句内容 - **分号`;`**:SQL语句结束符(某些数据库可省略) - **反引号```**:处理保留字字段名(如`` `rank` ``)[^2] - **下划线`_`**:函数名中的连接符(如$DENSE\_RANK()$) ### 完整示例 ```sql SELECT id, score, DENSE_RANK() OVER (ORDER BY score DESC) AS ranking FROM Scores; ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值