SQL长征

新开一个帖子记录SQL的漫漫长征~

Day 1

leecode SQL 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

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/combine-two-tables

# Write your MySQL query statement below
SELECT FirstName, LastName, City, State
FROM Person as p LEFT JOIN Address as a ON p.PersonId = a.PersonId;

Left Join 可以存在null。

leecode SQL 176

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

+----+--------+
| Id | Salary |
+----+--------+
| 1 | 100 |
| 2 | 200 |
| 3 | 300 |
+----+--------+
例如上述 Employee 表,SQL查询应该返回 200 作为第二高的薪水。如果不存在第二高的薪水,那么查询应返回 null。

+---------------------+
| SecondHighestSalary |
+---------------------+
| 200 |
+---------------------+

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/second-highest-salary

# Write your MySQL query statement below
SELECT MAX(Salary) as SecondHighestSalary
FROM Employee
WHERE Salary < ( SELECT MAX(Salary)
                 FROM Employee
                 )

奇奇怪怪的思路,第二高就是把第一木大了。

Day 2

SQL 181

Employee 表包含所有员工,他们的经理也属于员工。每个员工都有一个 Id,此外还有一列对应员工的经理的 Id。

+----+-------+--------+-----------+
| Id | Name | Salary | ManagerId |
+----+-------+--------+-----------+
| 1 | Joe | 70000 | 3 |
| 2 | Henry | 80000 | 4 |
| 3 | Sam | 60000 | NULL |
| 4 | Max | 90000 | NULL |
+----+-------+--------+-----------+
给定 Employee 表,编写一个 SQL 查询,该查询可以获取收入超过他们经理的员工的姓名。在上面的表格中,Joe 是唯一一个收入超过他的经理的员工。

+----------+
| Employee |
+----------+
| Joe |
+----------+

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

(待解决!!!)

SELECT
    E.Name AS Employee
FROM
    Employee AS E
    INNER JOIN Employee AS M
    ON E.ManagerId = M.Id
    AND E.Salary > M.Salary;

这里自联结的表现比其他方式速度快很多。

SQL 182

编写一个 SQL 查询,查找 Person 表中所有重复的电子邮箱。

示例:

+----+---------+
| Id | Email |
+----+---------+
| 1 | [email protected] |
| 2 | [email protected] |
| 3 | [email protected] |
+----+---------+
根据以上输入,你的查询应返回以下结果:

+---------+
| Email |
+---------+
| [email protected] |
+---------+

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

# Write your MySQL query statement below
SELECT Email
FROM Person
GROUP BY Email
HAVING count(Email) > 1;

Day 3

image.png

SQL 183

某网站包含两个表,Customers 表和 Orders 表。编写一个 SQL 查询,找出所有从不订购任何东西的客户。

Customers 表:

+----+-------+
| Id | Name |
+----+-------+
| 1 | Joe |
| 2 | Henry |
| 3 | Sam |
| 4 | Max |
+----+-------+
Orders 表:

+----+------------+
| Id | CustomerId |
+----+------------+
| 1 | 3 |
| 2 | 1 |
+----+------------+
例如给定上述表格,你的查询应返回:

+-----------+
| Customers |
+-----------+
| Henry |
| Max |
+-----------+

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

# Write your MySQL query statement below
select Name Customers
from Customers c left join Orders o on c.Id = o.CustomerId 
where CustomerId is null;

SELECT NAME AS CUSTOMERS
FROM CUSTOMERS
WHERE ID NOT IN (SELECT CUSTOMERID
                 FROM ORDERS);

第二种方法速度更快!

SQL 196

编写一个 SQL 查询,来删除 Person 表中所有重复的电子邮箱,重复的邮箱里只保留 Id 最小 的那个。

+----+------------------+
| Id | Email |
+----+------------------+
| 1 | [email protected] |
| 2 | [email protected] |
| 3 | [email protected] |
+----+------------------+
Id 是这个表的主键。
例如,在运行你的查询语句之后,上面的 Person 表应返回以下几行:

+----+------------------+
| Id | Email |
+----+------------------+
| 1 | [email protected] |
| 2 | [email protected] |
+----+------------------+

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

# Write your MySQL query statement below
DELETE p1 
FROM Person p1, Person p2
WHERE
    p1.Email = p2.Email AND p1.Id > p2.Id;

可以先select找出,id较大的重复的邮件,随后删除。

Day 4

SQL 197

表 Weather

+---------------+---------+
| Column Name | Type |
+---------------+---------+
| id | int |
| recordDate | date |
| temperature | int |
+---------------+---------+
id 是这个表的主键
该表包含特定日期的温度信息

编写一个 SQL 查询,来查找与之前(昨天的)日期相比温度更高的所有日期的 id 。

返回结果 不要求顺序 。

查询结果格式如下例:

Weather
+----+------------+-------------+
| id | recordDate | Temperature |
+----+------------+-------------+
| 1 | 2015-01-01 | 10 |
| 2 | 2015-01-02 | 25 |
| 3 | 2015-01-03 | 20 |
| 4 | 2015-01-04 | 30 |
+----+------------+-------------+

Result table:
+----+
| id |
+----+
| 2 |
| 4 |
+----+
2015-01-02 的温度比前一天高(10 -> 25)
2015-01-04 的温度比前一天高(20 -> 30)

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

SELECT w2.Id
FROM Weather w1, Weather w2
WHERE DATEDIFF(w2.RecordDate, w1.RecordDate) = 1
AND w1.Temperature < w2.Temperature

DATEDIFF() 函数返回两个日期之间的天数。
(这里可用Join,也可以不用)
天数相差为一,同时温度大。

SQL 595

这里有张 World 表

+-----------------+------------+------------+--------------+---------------+
| name | continent | area | population | gdp |
+-----------------+------------+------------+--------------+---------------+
| Afghanistan | Asia | 652230 | 25500100 | 20343000 |
| Albania | Europe | 28748 | 2831741 | 12960000 |
| Algeria | Africa | 2381741 | 37100000 | 188681000 |
| Andorra | Europe | 468 | 78115 | 3712000 |
| Angola | Africa | 1246700 | 20609294 | 100990000 |
+-----------------+------------+------------+--------------+---------------+
如果一个国家的面积超过 300 万平方公里,或者人口超过 2500 万,那么这个国家就是大国家。

编写一个 SQL 查询,输出表中所有大国家的名称、人口和面积。

例如,根据上表,我们应该输出:

+--------------+-------------+--------------+
| name | population | area |
+--------------+-------------+--------------+
| Afghanistan | 25500100 | 652230 |
| Algeria | 37100000 | 2381741 |
+--------------+-------------+--------------+

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

# Write your MySQL query statement below
select name, population, area
from World
where area > 3000000 or population > 25000000;

SQL

有一个courses 表 ,有: student (学生) 和 class (课程)。

请列出所有超过或等于5名学生的课。

例如,表:

+---------+------------+
| student | class |
+---------+------------+
| A | Math |
| B | English |
| C | Math |
| D | Biology |
| E | Math |
| F | Computer |
| G | Math |
| H | Math |
| I | Math |
+---------+------------+
应该输出:

+---------+
| class |
+---------+
| Math |
+---------+

提示:

学生在每个课中不应被重复计算。

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

SELECT
    class
FROM
    courses
GROUP BY class
HAVING COUNT(DISTINCT student) >= 5
;

HAVING()函数:
在 SQL 中增加 HAVING 子句原因是,WHERE 关键字无法与聚合函数一起使用。

HAVING 子句可以让我们筛选分组后的各组数据。

Day 5

SQL 620

今天复习一下运算符的知识:

SQL 算术运算符


假设变量 a 的值是:10,变量 b 的值是:20,以下为各运算符执行结果:

| + 加法,执行加法运算。 | a + b 得到 30

| - 减法,执行减法运算。| a - b 得到 -10

| * 乘法,执行乘法运算。 | a * b 得到 200

| / 用左操作数除以右操作数。 | b / a 得到 2

| % 用左操作数除以右操作数并返回余数。| b % a 得到 0

SQL 比较运算符


假设变量 a 的值是:10,变量 b 的值是:20,以下为各运算符执行结果:

| 运算符 | 描述 | 例子 |
| = 检查两个操作数的值是否相等,如果是,则条件为真(true)。
| (a = b) is false. |
| != 检查两个操作数的值是否相等,如果值不相等则条件为真(true)。
| (a != b) is true. |
| <> 检查两个操作数的值是否相等,如果值不相等则条件为真(true)。 | (a <> b) is true. |
| > 检查左操作数的值是否大于右操作数的值,如果是,则条件为真(true)。
| (a > b) is false. |
| < 检查左操作数的值是否小于右操作数的值,如果是,则条件为真(true)。 | (a < b) is true. |
| >= 检查左操作数的值是否大于或等于右操作数的值,如果是,则条件为真(true)。 | (a >= b) is false |
| <= 检查左操作数的值是否小于或等于右操作数的值,如果是,则条件为真(true)。 | (a <= b) is true. |
| !< 检查左操作数的值是否不小于右操作数的值,如果是,则条件变为真(true)。 | (a !< b) is false. |
| !> 检查左操作数的值是否不大于右操作数的值,如果是,则条件变为真(true)。 | (a !> b) is true. |

SQL 逻辑运算符:


这是在 SQL 所有的逻辑运算符的列表。

| 运算符 | 描述 |
| ALL | ALL运算符用于将值与另一个值集中的所有值进行比较。 |
| AND | AND运算符允许在SQL语句的WHERE子句中指定多个条件。
|
| ANY | ANY运算符用于根据条件将值与列表中的任何适用值进行比较。 |
| BETWEEN | BETWEEN运算符用于搜索在给定最小值和最大值内的值。
|
| EXISTS | EXISTS运算符用于搜索指定表中是否存在满足特定条件的行。 |
| IN | IN运算符用于将值与已指定的文字值列表进行比较。 |
| LIKE | LIKE运算符用于使用通配符运算符将值与类似值进行比较。 |
| NOT | NOT运算符反转使用它的逻辑运算符的含义。 例如:NOT EXISTS, NOT BETWEEN, NOT IN等等,这是一个否定运算符。
|
| OR | OR运算符用于组合SQL语句的WHERE子句中的多个条件。 |
| IS NULL | IS NULL运算符用于将值与NULL值进行比较。 |
| UNIQUE | UNIQUE运算符搜索指定表的每一行的唯一性(无重复项)。 |

某城市开了一家新的电影院,吸引了很多人过来看电影。该电影院特别注意用户体验,专门有个 LED显示板做电影推荐,上面公布着影评和相关电影描述。

作为该电影院的信息部主管,您需要编写一个 SQL查询,找出所有影片描述为非 boring (不无聊) 的并且 id 为奇数 的影片,结果请按等级 rating 排列。

例如,下表 cinema:

+---------+-----------+--------------+-----------+
| id | movie | description | rating |
+---------+-----------+--------------+-----------+
| 1 | War | great 3D | 8.9 |
| 2 | Science | fiction | 8.5 |
| 3 | irish | boring | 6.2 |
| 4 | Ice song | Fantacy | 8.6 |
| 5 | House card| Interesting| 9.1 |
+---------+-----------+--------------+-----------+
对于上面的例子,则正确的输出是为:

+---------+-----------+--------------+-----------+
| id | movie | description | rating |
+---------+-----------+--------------+-----------+
| 5 | House card| Interesting| 9.1 |
| 1 | War | great 3D | 8.9 |
+---------+-----------+--------------+-----------+

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

# Write your MySQL query statement below
select id, movie, description, rating
from cinema
where description <> "boring" and id%2 <> 0
order by rating desc;

SQL 627

给定一个 salary 表,如下所示,有 m = 男性 和 f = 女性 的值。交换所有的 f 和 m 值(例如,将所有 f 值更改为 m,反之亦然)。要求只使用一个更新(Update)语句,并且没有中间的临时表。

注意,您必只能写一个 Update 语句,请不要编写任何 Select 语句。

例如:

id name sex salary
1 A m 2500
2 B f 1500
3 C m 5500
4 D f 500

运行你所编写的更新语句之后,将会得到以下表:

id name sex salary
1 A f 2500
2 B m 1500
3 C f 5500
4 D m 500

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

# Write your MySQL query statement below
update salary 
set sex = case sex
    when "m" then "f"
    else "m"
    end;

补充一下 “case when” 的用法:
推荐阅读这篇,这里就不ctrl + c 了
https://www.jianshu.com/p/113b21734353

Day 6

出发前的最后一晚了,刚好也是免费的题要刷完了。

SQL 1179

部门表 Department:

+---------------+---------+
| Column Name | Type |
+---------------+---------+
| id | int |
| revenue | int |
| month | varchar |
+---------------+---------+
(id, month) 是表的联合主键。
这个表格有关于每个部门每月收入的信息。
月份(month)可以取下列值 ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]。

编写一个 SQL 查询来重新格式化表,使得新的表中有一个部门 id 列和一些对应 每个月 的收入(revenue)列。

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

Department 表:
+------+---------+-------+
| id | revenue | month |
+------+---------+-------+
| 1 | 8000 | Jan |
| 2 | 9000 | Jan |
| 3 | 10000 | Feb |
| 1 | 7000 | Feb |
| 1 | 6000 | Mar |
+------+---------+-------+

查询得到的结果表:
+------+-------------+-------------+-------------+-----+-------------+
| id | Jan_Revenue | Feb_Revenue | Mar_Revenue | ... | Dec_Revenue |
+------+-------------+-------------+-------------+-----+-------------+
| 1 | 8000 | 7000 | 6000 | ... | null |
| 2 | 9000 | null | null | ... | null |
| 3 | null | 10000 | null | ... | null |
+------+-------------+-------------+-------------+-----+-------------+

注意,结果表有 13 列 (1个部门 id 列 + 12个月份的收入列)。

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

select id,
       sum(case  when month = 'Jan' then revenue end) as Jan_Revenue,
       sum(case  when month = 'Feb' then revenue end) as Feb_Revenue,
       sum(case  when month = 'Mar' then revenue end) as Mar_Revenue,
       sum(case  when month = 'Apr' then revenue end) as Apr_Revenue,
       sum(case  when month = 'May' then revenue end) as May_Revenue,
       sum(case  when month = 'Jun' then revenue end) as Jun_Revenue,
       sum(case  when month = 'Jul' then revenue end) as Jul_Revenue,
       sum(case  when month = 'Aug' then revenue end) as Aug_Revenue,
       sum(case  when month = 'Sep' then revenue end) as Sep_Revenue,
       sum(case  when month = 'Oct' then revenue end) as Oct_Revenue,
       sum(case  when month = 'Nov' then revenue end) as Nov_Revenue,
       sum(case  when month = 'Dec' then revenue end) as Dec_Revenue
from department
group by id;

上述代码的执行语序问题:

1). from子句,然后 group by 子句 ,大致结果是这样(引用图)


示例

2). select子句 , 共13个字段(列),第一列为 id , 即部门编号,这个较好理解。

3). 然后理解剩余12列的值,每有一个id必然会有其对应的12列(月份)的值。当id=1时,进入到经过分组的id为1 的表中,首先为jan_revenue列赋值,遍历该id表的所有行,通过case when 选定对应的revenue或null,然后将选定的这些值sum(详见注解) ,赋值给id的jan_revenue。如此,依次赋值给其他11列。同理,select 其他id值时进入对应id表内...
注解:
{ 遍历整个id表,针对每个月或者说针对每条case when 语句,其实只有一个renvenue或根本没有,即都是null值。
①'只有一个revenue' : 按照表的定义,每个id分组表内month列的值绝不会有重复,也就是说一个部门在某个月的的收入不会分为两条来记录,该说法也与题目中 'id与month 为联合主键'的说法一致(主键唯一)
②'都是null值':id表中没有一行的month值为当前case when月份所对应的,即在数据库中,该部门没有录入当月的收入。
③'使用sum聚合函数': 因为‘只有一个revenue’,'都是null值',所以使用sum,max,min 等聚合函数效果都是一样的。使用sum更易让人忽略细节,且更易于让人粗略的,模糊的结合group by 接受并理解。
④'不使用类似的聚合函数':不可以。group by 分组后,在mysql中,若不使用聚合函数来提取值,直接select只会select出当前id表的第一行(某个月份),这也意味着在为当前id的12列赋值时,每次都是遍历这一行。结果就是只有与与改行月份对应的那个case when 语句的列会被赋值为revenue,其他皆判断为不符合(即该id的其他列都是null值)
⑤其实不太能理清sum与case语句执行的先后关系,个人认定为先case ,后sum :推翻上述的过程' 3). ', 为某id部门的某个月(对应的列)赋值时,先进入对应id表,先case when ,遍历group by 后该id表的第一行(结合④理解),然后该id的该列被赋值为null或者revenue值,然后发现外面嵌套聚合函数sum,返回case when 完整的遍历该id表,最终根据具体的聚合函数形式(此处为sum)得到该id(部门)该列(月份)的最终正确值。该id其他列,以及其他id的各列皆是如此。}

作者:jiachang-a
链接:https://leetcode-cn.com/problems/reformat-department-table/solution/guan-yu-pu-bian-jiu-jie-de-group-by-case-d82v/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

你可能感兴趣的:(SQL长征)