MySQL练习

MySQL练习

1、组合两个表

  • 表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

Sql如下:

select p.FirstName, p.LastName, a.City, a.State 
from 
Person p left join Address a on p.PersonId=a.PersonId;

2、第二高的薪水

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

+----+--------+
| Id | Salary |
+----+--------+
| 1  | 100    |
| 2  | 200    |
| 3  | 300    |
+----+--------+

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

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

Sql如下:

select 
(select distinct Salary 
from 
employee order by Salary desc
limit 1 offset 1) as SecondHighestSalary;

备注分析:

用到升序 order by col desc
接着是limit 1,1  其中第一个1表示跳过数,第二个1表示输出多少数
可能考虑多个并列第二,用distinct去重

3、超过经理收入的员工

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      |
+----------+

sql如下:

select a.Name as Employee
from 
Employee a,
Employee b where 
a.ManagerId=b.Id and a.Salary>b.Salary;

4、查找重复的电子邮箱

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

+----+---------+
| Id | Email   |
+----+---------+
| 1  | a@b.com |
| 2  | c@d.com |
| 3  | a@b.com |
+----+---------+

根据以上输入,你的查询应返回以下结果:

+---------+
| Email   |
+---------+
| a@b.com |
+---------+

sql如下:

#select a.Email from (select Email,count(Email) c from Person group by Email having c>1) a;

select Email from Person group by Email having count(Email)>1;

备注分析:

用group by 统计Email 重复次数,过滤掉大于1的行,再查询单独列

5、从不订购的客户

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

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

例如给定上述表格,你的查询应返回:

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

sql如下:

select Name as Customers 
from Customers 
where Id not in (select CustomerId from Orders);

备注分析:

查询客户表的id不在订单表CustomerId的行

6、部门工资最高的员工

Employee 表包含所有员工信息,每个员工有其对应的 Id, salary 和 department Id。

+----+-------+--------+--------------+
| Id | Name  | Salary | DepartmentId |
+----+-------+--------+--------------+
| 1  | Joe   | 70000  | 1            |
| 2  | Jim   | 90000  | 1            |
| 3  | Henry | 80000  | 2            |
| 4  | Sam   | 60000  | 2            |
| 5  | Max   | 90000  | 1            |
+----+-------+--------+--------------+

Department 表包含公司所有部门的信息。

+----+----------+
| Id | Name     |
+----+----------+
| 1  | IT       |
| 2  | Sales    |
+----+----------+

编写一个 SQL 查询,找出每个部门工资最高的员工。对于上述表,您的 SQL 查询应返回以下行(行的顺序无关紧要)。

+------------+----------+--------+
| Department | Employee | Salary |
+------------+----------+--------+
| IT         | Max      | 90000  |
| IT         | Jim      | 90000  |
| Sales      | Henry    | 80000  |
+------------+----------+--------+

sql如下:

select  d.Name as Department,e.Name as Employee,e.Salary 
from Employee e
join Department d on e.DepartmentId=d.Id
where 
(e.DepartmentId,e.Salary) in 
(select  DepartmentId,Max(Salary) from Employee group by DepartmentId)

备注分析:

用到Max()函数取最大值,并对部门id进行分组,
join两表,where判断部门id和薪水 是否在最大值的分组表中存在

7、上升的温度

  • 表 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)

sql如下:

select w1.id from Weather w1
join Weather w2 on datediff(w1.recordDate,w2.recordDate)=1
where w1.Temperature>w2.Temperature

备注分析:

利用datediff(date1,date2)函数,返回date1-date2的天数做join判断,
再用where判断date1的温度大于date2的温度

8、大的国家

这里有张 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      |
+--------------+-------------+--------------+

sql如下:

select name,population,area from World
where area>3000000 or population>25000000

9、超过5名学生的课

有一个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    |
+---------+

sql如下:

select class
from courses group by class
having count(DISTINCT student)>=5

备注分析:

利用group by 分组,过滤count(student)大于等于5的,在having 中用distinct过滤同名学生

10、有趣的电影

某城市开了一家新的电影院,吸引了很多人过来看电影。该电影院特别注意用户体验,专门有个 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     |
+---------+-----------+--------------+-----------+

sql如下:

select  id,movie,description,rating  from cinema
where description!="boring" 
and id%2=1 
order by rating desc

备注分析:

where的条件判断!=(或者<>,sql2000中是不支持 != 的) 和 n%2=1(或者mod(id,2)=1),以及order by

11、性别更变

给定一个 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    |

sql如下:

update salary 
set sex= 
case sex
when 'm' then 'f' else 'm' end;

备注分析:

用到case x  when expr1  then expr2   else expr1  end

12、重新格式化部门表

部门表 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个月份的收入列)。

sql如下:

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

备注分析:

要统计每个部门的薪资,会用到对部门id进行 group by
之后会对每个月的薪资做展示,在select时对每个月的薪资进行sum,
并且在sum()中进行case 判断,返回当前月的薪资,无则返回空

13、第N高的薪水

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

+----+--------+
| Id | Salary |
+----+--------+
| 1  | 100    |
| 2  | 200    |
| 3  | 300    |
+----+--------+

例如上述 Employee 表,n = 2 时,应返回第二高的薪水 200。如果不存在第 n 高的薪水,那么查询应返回 null。

+------------------------+
| getNthHighestSalary(2) |
+------------------------+
| 200                    |
+------------------------+

sql如下:

CREATE FUNCTION getNthHighestSalary(N INT) RETURNS INT
BEGIN
    declare m int;
    set m = N -1;
  RETURN (
      # Write your MySQL query statement below.
      ifnull((select Salary from Employee group by Salary order by Salary desc limit m,1),null)
  );
END

备注分析:

本题涉及到mysql的自定义函数语法问题
1、没有第二高的就为空,用ifnull(expr,null)
2、用order by 排序,limit 输出N-1,1即跳过N-1个输出一个。
3、由于limit 后不能直接跟 N-1,所以一开始吧N-1赋值给m
4、由于考虑到只有两条数据,并且并列第一的情况,所以用group by对salary进行去重

14、分数排名

编写一个 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    |
+-------+------+

重要提示:对于 MySQL 解决方案,如果要转义用作列名的保留字,可以在关键字之前和之后使用撇号。例如 Rank

sql如下:

select *,dense_rank() OVER(order by Score desc) as Rank 
from Scores 

备注分析:

值得注意的三个窗口函数,现在给定五个成绩:99,99,85,80,75。
DENSE_RANK()  如果使用 DENSE_RANK() 进行排名会得到:1,1,2,3,4。
RANK()  如果使用 RANK() 进行排名会得到:1,1,3,4,5。
ROW_NUMBER()  如果使用 ROW_NUMBER() 进行排名会得到:1,2,3,4,5。
  • 第二种解法
select a.Score as Score,
(select count(distinct b.Score) 
    from Scores b 
    where b.Score >= a.Score) as `Rank`
from Scores a
order by a.Score DESC

备注分析:

where b.Score >= a.Score 时,才执行一次count(distinct b.Score) 
也就是说当b表的分数大于a表的分数,才计算一次count
也就是说,当b表的分数大于a表的分数时,计算b表分数时的个数,即为此分数的排名

即大于等于自己成绩的且去重的成绩个数就是自己排名