以Leetcode第176题“第二高的薪水”为例。
首先说一下这道题最容易理解、最容易实现的解法,就是比最大值小的值里最大的值。
SELECT MAX(salary) AS SecondHighestSalary
FROM Employee
WHERE salary < (SELECT MAX(salary)
FROM Employee)
通过这道题目,我来说明一下,什么时候结果集显示的是null,什么时候是空。下面这个是我们的测试用例,也就是只有一个值,第二高的薪水也就是null。
用上面的语句搜索,结果是下面这样的。
如果你把SQL改成下面这样,也就是去掉MAX函数,结果就变成了空。可以看到输出为null与MAX有关系。
SELECT salary AS SecondHighestSalary
FROM Employee
WHERE salary < (SELECT MAX(salary)
FROM Employee)
我们在利用这个题目试验一些别的东西,比如说取rownum为2的行,对于这个测试用例应该为null,可以发现输出的结果为空。(这道题不能这么做,因为还有重复值的问题,只是举个例子)
SELECT a.salary AS SecondHighestSalary
FROM (SELECT rownum r, e.*
FROM Employee e) a
WHERE a.r = 2
如果我们像上面一样加个MAX函数,输出结果就会为null。
SELECT MAX(a.salary) AS SecondHighestSalary
FROM (SELECT rownum r, e.*
FROM Employee e) a
WHERE a.r = 2
如果我们在套一个SELECT,也可以输出为null。
SELECT(
SELECT a.salary
FROM (SELECT rownum r, e.*
FROM Employee e) a
WHERE a.r = 2
) AS SecondHighestSalary
FROM dual
对于MySQL来说也是这样的,只不过由于语法原因,它不要FROM dual。这个评论区也有人询问,这个解释我觉得没解释出什么,但不知道为什么这么多赞。
那NVL不行吗?
可以看到这种方式也是输出为空。
SELECT NVL(a.salary, null) AS SecondHighestSalary
FROM (SELECT rownum r, e.*
FROM Employee e) a
WHERE a.r = 2
但是如果你像下面这么写,输出的则是null(确实是由于NVL而输出的,把NVL第二个参数改成别的输出的内容也会变),很奇怪。这也是这道题目Oracle解法大部分人都采用的办法。包括MySQL也是如此。我也不知道为什么,网络上也查不到,只能强行理解SELECT这个语句,放到一个函数里,自己可能也相当于是一个函数(因为NVL只会获取null,所以说明里面SELECT出来就已经是null了,也可以发现,NVL对于这道题没啥用,直接SELECT一个SELECT就可以了)。
SELECT NVL((SELECT a.salary
FROM (SELECT rownum r, e.*
FROM Employee e) a
WHERE a.r = 2), null) AS SecondHighestSalary
FROM dual
MySQL的例子:
我找了两道我用过NVL的题目,发现一个是左连接,一个全连接,它们这种在JOIN时没有值会直接补null,所以用NVL就可以。
SELECT name, NVL(SUM(distance), 0) AS travelled_distance
FROM Users u LEFT JOIN Rides r
ON u.id = r.user_id
GROUP BY u.id, u.name
ORDER BY travelled_distance DESC, u.name
----------------------------------------------------------
SELECT NVL(e.employee_id, s.employee_id) AS employee_id
FROM Employees e FULL OUTER JOIN Salaries s
ON e.employee_id = s.employee_id
WHERE e.name IS NULL OR s.salary IS NULL
ORDER BY 1
总结
如果一个查询是没有结果的,你如果想让输出是null,有两种方式
- 用一个函数,像MAX、MIN等,去包住这个SELECT的值。该怎么理解呢?我也不知道,我觉可以理解为一个函数的参数为空,输出结果就是null。
- 你可以在这个SELECT的外面在套一个SELECT,该怎么理解呢?我觉得就是SELECT空,输出就是null。
- 额外说一下NVL,NVL是遇见null才会起作用的,遇见空是不起作用的。如果想让它起作用可以在NVL里包一个SELECT语句,强行理解,可以说把这个语句变成“函数”,这样就会输出null。
补充
这个文章是说MySQL里ifnull的,也证明了我的猜想:地址。