LeetCode:数据库技术【175-178】
LeetCode已经刷完200道题目,但这只是开始,下一段时间,仍然把刷题作为重点,争取再次完成200道,本篇博客将会带大家熟悉一些数据库面试题,从简单到复杂,并且会把相关知识点全部阐述一下。
175.组合两个表
题目描述
表1: Person
+-------------+---------+| 列名 | 类型 |+-------------+---------+| PersonId | int || FirstName | varchar || LastName | varchar |+-------------+---------+PersonId 是上表主键
表2: Address
+-------------+---------+| 列名 | 类型 |+-------------+---------+| AddressId | int || PersonId | int || City | varchar || State | varchar |+-------------+---------+AddressId 是上表主键
编写一个 SQL 查询,满足条件:无论 person 是否有地址信息,都需要基于上述两表提供 person 的以下信息:
FirstName, LastName, City, State
题目分析
首先题目说了,不管person是否有地址信息,都要显示4列信息,那么就是说第二张表允许显示NULL数据。这里涉及的知识点是联结查询中的外部联结:
外部联结
许多联结将一个表中的行与另一个表中的行相关联。但有时候会需要包含没有关联行的那些行。例如本题。联结包含了那些在相关表中没有关联行的行。这种 类型的联结称为外部联结。
SELECT FirstName, LastName, City, StateFROM Person LEFT OUTER JOIN AddressON Person.PersonId = Address.PersonId;
首先使用了关键字OUTER JOIN 来指定联结的类型为外部联结,必须使用RIGHT或LEFT关键字指定包括其所有行的表(RIGHT指出的是OUTER JOIN右边的表,而LEFT 指出的是OUTER JOIN左边的表)。也即是说,OUTER JOIN的两边分别是两张表,LEFT或RIGHT说了那张表的数据必须给出,另一张表的数据可以为NULL。
176.第二高的薪水
题目描述
编写一个 SQL 查询,获取 Employee
表中第二高的薪水(Salary) 。
+----+--------+| Id | Salary |+----+--------+| 1 | 100 || 2 | 200 || 3 | 300 |+----+--------+
例如上述 Employee
表,SQL查询应该返回 200
作为第二高的薪水。如果不存在第二高的薪水,那么查询应返回 null
。
+---------------------+| SecondHighestSalary |+---------------------+| 200 |+---------------------+
题目分析
找到第二高的工资,那么我们按照工资由高到低排序,再利用OFFSET、LIMIT取第2个记录即可,不是很简单吗?
SELECT Salary as SecondHighestSalary FROM Employee ORDER BY Salary DESC LIMIT 1 OFFSET 1;
但是到这里我们发现我们缺乏思考,第二高的薪水,那如果有两个人都是第二高,那么按照我们的算法来实际上就是取了第一高的工资。
此时,我们可以借助临时表将相同的工资剔除掉,然后再按照这个逻辑来写SQL:
SELECT ( SELECT DISTINCT Salary FROM Employee ORDER BY Salary DESC LIMIT 1 OFFSET 1)AS SecondHighestSalary
关于限制结果有两种写法,第一种是LIMIT X,Y指示MySQL返回从行X开始的Y行.即第一个参数为开始的位置,第二个为要获取的行数。
第二种写法是LIMIT 4 OFFSET 3意为从行3开始取4行,就像LIMIT 3, 4一样。切记先LIMIT后OFFSET,不要搞混!
注意:检索出来的 第一行为行0而不是行1。因此,LIMIT1,1 将检索出第二行而不是第一行。
177.第N高的薪水
题目描述
题目同上题,但是现在要求不太一样,我们要找的不是第2高了,而是第N高。
题目分析
题目既然这样写,肯定不是一条SELECT就可以解决的,首先是要创建自定义函数,这样才能进行参数和值的传递,结构LeetCode已经给出,我们只需要处理一下第N高,就是从N-1开始向后取一条这个关系即可。
CREATE FUNCTION getNthHighestSalary(N INT) RETURNS INTBEGIN SET N=N-1; RETURN ( # Write your MySQL query statement below. SELECT DISTINCT Salary FROM Employee ORDER BY Salary DESC LIMIT 1 OFFSET N );END
关于自定义函数和存储过程等内容,将会在接下来的博客中详细讲到。
这里补充一下存储过程与函数的区别:
区别:
1. 一般来说,存储过程实现的功能要复杂一点,而函数的实现的功能针对性比较强。
2. 当对数据库进行复杂操作时(如对多个表进行Update、Insert、Query、Delete时),可将此复杂操作用存储过程封装起来与数据库提供的事务处理结合一起使用。存储过程可以从自己的存储过程内引用其它存储过程,这可以简化一系列复杂语句. 3. 存储过程一般是作为一个独立的部分来执行,而函数可以作为查询语句的一个部分来调用,由于函数可以返回一个表对象,因此它可以在查询语句中位于FROM关键字的后面。4. 存储过程只在创造时进行编译,以后每次执行存储过程都不需再重新编译,而一般SQL语句每执行一次就编译一次,所以使用存储过程可提高数据库执行速度。
5. 存储过程可以接受参数、输出参数、返回单个或多个结果集以及返回值,可以向程序返回错误原因。但函数只能返回一个特定类型的值或者表对象。
6. 存储过程中的CRUD的操作会影响数据库状态,但函数却不能。
178.分数排名
题目描述
编写一个 SQL 查询来实现分数排名。如果两个分数相同,则两个分数排名(Rank)相同。请注意,平分后的下一个名次应该是下一个连续的整数值。换句话说,名次之间不应该有“间隔”。
+----+-------+| Id | Score |+----+-------+| 1 | 3.50 || 2 | 3.65 || 3 | 4.00 || 4 | 3.85 || 5 | 4.00 || 6 | 3.65 |+----+-------+
例如,根据上述给定的 Scores
表,你的查询应该返回(按分数从高到低排列):
+-------+------+| Score | Rank |+-------+------+| 4.00 | 1 || 4.00 | 1 || 3.85 | 2 || 3.65 | 3 || 3.65 | 3 || 3.50 | 4 |+-------+------+
题目分析
我们要对分数进行排名,就是要看有多少个分数大于或等于这个的,很明显用到了计数函数COUNT()
COUNT()函数进行计数。可利用COUNT()确定表中行的数目或符合特定条件的行的数目。 COUNT()函数有三种使用方式。
? 使用COUNT(*)对表中行的数目进行计数,不管表列中包含的是空 值(NULL)还是非空值。
? 使用COUNT(column)对特定列中具有值的行进行计数,忽略 NULL值。
? 使用COUNT(DISTINCT column)对特定列中具有独一无二的值的行进行计数,忽略 NULL值。
注意:
如果指定列名,则DISTINCT只能用于COUNT(),即COUNT(DISTINCT column)。DISTINCT 不能用于COUNT(*),因此不允许使用COUNT(DISTINCT *), 否则会产生错误。类似地,DISTINCT必须使用列名,不能用于计算或表达式。
SELECT Score,(SELECT COUNT(DISTINCT Score) FROM Scores WHERE Score>=s.Score) as Rank FROM Scores AS s ORDER BY Score DESC;
总结
到这里为止,我们完成了四道数据库的编程题,对这四道题目有一个简单的总结:
- 使用外部联结OUTER JOIN来处理没有关联行的行,LEFT指定了左表可以有NULL值,RIGHT则相反。
- 使用LIMIT OFFSET 来获取第N个数据开始的一条数据或多条数据,比如排名等待。
- 熟悉一下自定义函数的格式,以及其与存储过程的区别。
- 使用COUNT函数来计数,同时说明一下DISTINCT与COUNT搭配实现对唯一数据的技术。
- 多多实践,多多努力!