​LeetCode刷题四部曲之SQL篇(一)

简介: ​LeetCode刷题四部曲之SQL篇(一)

前言

这周先开个头,看看能不能做到每日一题,这个系列会放在GitHub上。前文我们已经重新梳理了对SQL模型的理解, 这里我们刷题,增进一下对SQL的理解。在实践中丰富我们的SQL模型,重在体会思想。尽量直接在LeetCode提交SQL, 盲写。

今天的题目

176. Second Highest Salary  第二高的薪水

编写一个 SQL 查询,获取 Employee 表中第二高的薪水(Salary) 。

Id Salary
1 100
2 200
3 300

例如上述 Employee 表,SQL查询应该返回 200 作为第二高的薪水。如果不存在第二高的薪水,那么查询应返回 null。

SecondHighestSalary
200

第一种解法解读

解法思考,这道题目用程序做的话,无法是排个序然后去数组下标为1的元素即可。这是程序的思路,那SQL呢,但是SQL中没有下标,但是我们有分页函数。

所以我的第一版提交长这个样子:

SELECT DISTINCT Salary  AS SecondHighestSalary FROM Employee ORDER BY Salary  DESC LIMIT 1, OFFSET 1

但是答案是不对的:

image.png

我输出的是个空?不是NULL,这看来又增进了我对MySQL的理解啊, 只有一条的话,跳过这条,取后面的就是空而不是NULL。那这个是为什么呢。我们来用命令行来看一下这个结果, 我装MySQL的时候忘记配置环境变量,但是现在还不想配置,但是可以通过图形化工具将命令行带出来:

1.image.png

  1. image.png3 . 然后我们查询student表中不存在的记录,看看返回什么:

image.png

我们在SQL查询模型和子查询再学习中将SQL的每一个执行过程会输出一个虚拟表,最终得到的也是一个虚拟表, 这里我们在复习一下:

image.png

所以我们在student查询一个不存在的记录,返回为空集,这个是在预期的,符合我们的SQL查询模型的。那如果我们将这个查询当作一个表达式来使用呢,也就是像下面这样:

SELECT (SELECT NAME FROM Student where id = 3) name;

输出了为NULL:

image.png

在oracle下其实要这么写,才能过关:

SELECT (SELECT NAME FROM Student where id = 3) name FROM Dual

那为什么这个会输出NULL呢,原因在于此时SELECT (SELECT NAME FROM Student where id = 3) 被当作表达式解析求值,对于表达式求值来说, 要么有值,要么为NULL。上面的那种写法被称为标量子查询,

明白了这个,我们就可以将SQL改写为:

SELECT
(SELECT distinct  salary  AS SecondHighestSalary   FROM  Employee  ORDER BY  salary DESC  LIMIT 1 OFFSET 1) AS
SecondHighestSalary

那这个distinct怎么理解, 不加distinct又报错, 为什么加上了distinct就可以,  出现这种问题来源于我们错误的SQL查询模型,在上面的SQL查询模型中,distinct在SELECT之后执行, 事实上他应该在SELECT之前执行, distinct对结果集去重, 然后SELECT取出结果集。所以上面我们的SQL查询模型可以修正为下面这样:

image.png

有了这个新的模型,我们就不难理解这个了这个SQL查询模型了,先按照salary倒序排, 然后再去重(防止表里只出现两条相同的最大的), 取第二条。这里我们在给出等价的oracle写法:

SELECT 
(SELECT DISTINCT(salary) AS SecondHighestSalary FROM 
(SELECT  salary,ROWNUM AS rowno FROM Employee WHERE ROWNUM <= 2) table_alias
WHERE table_alias.rowno > 1) AS SecondHighestSalary FROM DUAL

第二种解法解读

第一种解法相对来说更贴进去程序语言像Java之类的, 我们可以其实换一种思路来解决这个问题, 借助于SQL的强大统计特性, 也就是先找出最大的salary, 然后再从表里面小于这个最大的salary的,这也就是第二大的salary。这种思路更贴向SQL, 而且跨数据库, 我们来直接写一下:

SELECT max(salary) AS SecondHighestSalary    FROM Employee WHERE salary <  (SELECT max(salary) FROM Employee)

但这种解法并没有第一种快。

标量子查询简介[1]

这里关于标量子查询全部引自亚马逊SQL参考, 文末放有链接。

标量子查询是圆括号中的常规 SELECT 查询,仅返回一个值:带有一个列的一行。将执行此查询,返回值将在外部查询中使用。如果子查询返回零行,则子查询表达式的值为 null。如果它返回多行,则 Amazon Redshift 将返回错误。子查询可引用父查询中的变量,这将在子查询的任何一次调用中充当常量。

标量子查询在下列情况下是无效表达式:

  • 作为表达式的默认值
  • 在 GROUP BY 和 HAVING 子句中

以下子查询计算 2008 年全年的每笔销售支付的平均价格,然后外部查询使用输出中的值来比较每个季度每笔销售的平均价格:

select qtr, avg(pricepaid) as avg_saleprice_per_qtr,
select qtr, avg(pricepaid) as avg_saleprice_per_qtr,
(select avg(pricepaid)
from sales join date on sales.dateid=date.dateid
where year = 2008) as avg_saleprice_yearly
from sales join date on sales.dateid=date.dateid
where year = 2008
group by qtr
order by qtr;
qtr  | avg_saleprice_per_qtr | avg_saleprice_yearly
-------+-----------------------+----------------------
1     |                647.64 |               642.28
2     |                646.86 |               642.28
3     |                636.79 |               642.28
4     |                638.26 |               642.28
(4 rows)


相关文章
|
2月前
|
Unix Shell Linux
LeetCode刷题 Shell编程四则 | 194. 转置文件 192. 统计词频 193. 有效电话号码 195. 第十行
本文提供了几个Linux shell脚本编程问题的解决方案,包括转置文件内容、统计词频、验证有效电话号码和提取文件的第十行,每个问题都给出了至少一种实现方法。
LeetCode刷题 Shell编程四则 | 194. 转置文件 192. 统计词频 193. 有效电话号码 195. 第十行
|
3月前
|
Python
【Leetcode刷题Python】剑指 Offer 32 - III. 从上到下打印二叉树 III
本文介绍了两种Python实现方法,用于按照之字形顺序打印二叉树的层次遍历结果,实现了在奇数层正序、偶数层反序打印节点的功能。
57 6
|
3月前
|
Python
【Leetcode刷题Python】剑指 Offer 26. 树的子结构
这篇文章提供了解决LeetCode上"剑指Offer 26. 树的子结构"问题的Python代码实现和解析,判断一棵树B是否是另一棵树A的子结构。
50 4
|
3月前
|
搜索推荐 索引 Python
【Leetcode刷题Python】牛客. 数组中未出现的最小正整数
本文介绍了牛客网题目"数组中未出现的最小正整数"的解法,提供了一种满足O(n)时间复杂度和O(1)空间复杂度要求的原地排序算法,并给出了Python实现代码。
114 2
|
21天前
|
机器学习/深度学习 人工智能 自然语言处理
280页PDF,全方位评估OpenAI o1,Leetcode刷题准确率竟这么高
【10月更文挑战第24天】近年来,OpenAI的o1模型在大型语言模型(LLMs)中脱颖而出,展现出卓越的推理能力和知识整合能力。基于Transformer架构,o1模型采用了链式思维和强化学习等先进技术,显著提升了其在编程竞赛、医学影像报告生成、数学问题解决、自然语言推理和芯片设计等领域的表现。本文将全面评估o1模型的性能及其对AI研究和应用的潜在影响。
16 1
|
2月前
|
数据采集 负载均衡 安全
LeetCode刷题 多线程编程九则 | 1188. 设计有限阻塞队列 1242. 多线程网页爬虫 1279. 红绿灯路口
本文提供了多个多线程编程问题的解决方案,包括设计有限阻塞队列、多线程网页爬虫、红绿灯路口等,每个问题都给出了至少一种实现方法,涵盖了互斥锁、条件变量、信号量等线程同步机制的使用。
LeetCode刷题 多线程编程九则 | 1188. 设计有限阻塞队列 1242. 多线程网页爬虫 1279. 红绿灯路口
|
1月前
|
SQL 安全 数据挖掘
牛客网刷题之SQL篇:非技术快速入门39T
这篇文章是关于牛客网上的SQL刷题教程,涵盖了基础的SQL运算符和多个实际的数据分析场景,旨在帮助非技术人员快速入门SQL。
80 0
牛客网刷题之SQL篇:非技术快速入门39T
|
3月前
|
索引 Python
【Leetcode刷题Python】从列表list中创建一颗二叉树
本文介绍了如何使用Python递归函数从列表中创建二叉树,其中每个节点的左右子节点索引分别是当前节点索引的2倍加1和2倍加2。
56 7
|
3月前
|
Python
【Leetcode刷题Python】剑指 Offer 30. 包含min函数的栈
本文提供了实现一个包含min函数的栈的Python代码,确保min、push和pop操作的时间复杂度为O(1)。
28 4
|
3月前
|
Python
【Leetcode刷题Python】剑指 Offer 22. 链表中倒数第k个节点
Leetcode题目"剑指 Offer 22. 链表中倒数第k个节点"的Python解决方案,使用双指针法找到并返回链表中倒数第k个节点。
54 5