[关闭]
@adamhand 2019-03-04T09:21:17.000000Z 字数 12535 阅读 787

leetcode之mysql题解


176. Second Highest Salary

  1. Write a SQL query to get the second highest salary from the Employee table.
  1. +----+--------+
  2. | Id | Salary |
  3. +----+--------+
  4. | 1 | 100 |
  5. | 2 | 200 |
  6. | 3 | 300 |
  7. +----+--------+
  1. For example, given the above Employee table, the query should return 200 as the second highest salary. If there is no second highest salary, then the query should return null.
  1. +---------------------+
  2. | SecondHighestSalary |
  3. +---------------------+
  4. | 200 |
  5. +---------------------+

需要注意对查询条件为null这种情况的处理。下面给出建表、插入数据和选择语句。

  1. create table Employee(
  2. Id bigint not null primary key auto_increment,
  3. Salary bigint not null
  4. )engine=InnoDB;
  5. insert into Employee (Id,Salary)values(1,100),(2,200),(3,300);
  6. select
  7. ifnull(
  8. (select distinct Salary
  9. from Employee
  10. order by Salary desc
  11. limit 1 offset 1),null)
  12. as SecondHighestSalary;

ifnull()(expr1,expr2)函数的含义是:如果第一个参数不为空,则返回第一个参数,否则返回第二个参数。

196. Delete Duplicate Emails

题目描述:

  1. Write a SQL query to delete all duplicate email entries in a table named Person, keeping only unique emails based on its smallest Id.
  1. +----+------------------+
  2. | Id | Email |
  3. +----+------------------+
  4. | 1 | john@example.com |
  5. | 2 | bob@example.com |
  6. | 3 | john@example.com |
  7. +----+------------------+
  1. Id is the primary key column for this table.
  2. For example, after running your query, the above Person table should have the following rows:
  1. +----+------------------+
  2. | Id | Email |
  3. +----+------------------+
  4. | 1 | john@example.com |
  5. | 2 | bob@example.com |
  6. +----+------------------+
  1. Note:
  2. Your output is the whole Person table after executing your sql. Use delete statement.

思路:
这个题可以使用cross join来解决。cross join其实就是笛卡尔积,执行cross join时会将两个表相乘。它的写法有:

  1. SELECT * FROM table1 CROSS JOIN table2
  2. SELECT * FROM table1,table2

比如,有一个Person表和一个Employee表,它们的结构分别如下:

  1. # Person
  2. +----+------------------+
  3. | Id | Email |
  4. +----+------------------+
  5. | 1 | john@example.com |
  6. | 2 | bob@example.com |
  7. | 3 | john@example.com |
  8. +----+------------------+
  9. #Employee
  10. +----+--------+
  11. | Id | Salary |
  12. +----+--------+
  13. | 1 | 100 |
  14. | 2 | 200 |
  15. | 3 | 300 |
  16. +----+--------+

当执行

  1. select* from Employee e, Person p;

时的结果如下:

  1. +----+--------+----+------------------+
  2. | Id | Salary | Id | Email |
  3. +----+--------+----+------------------+
  4. | 1 | 100 | 1 | john@example.com |
  5. | 2 | 200 | 1 | john@example.com |
  6. | 3 | 300 | 1 | john@example.com |
  7. | 1 | 100 | 2 | bob@example.com |
  8. | 2 | 200 | 2 | bob@example.com |
  9. | 3 | 300 | 2 | bob@example.com |
  10. | 1 | 100 | 3 | john@example.com |
  11. | 2 | 200 | 3 | john@example.com |
  12. | 3 | 300 | 3 | john@example.com |
  13. +----+--------+----+------------------+

可以看到,最终得到的表结构是将前一个表的每一行分别和后一个表的第一行、第二行...连接形成的

对于本题而言,就可以按照如下语句选择出合适的行:

  1. SELECT p1.*
  2. FROM Person p1,
  3. Person p2
  4. WHERE
  5. p1.Email = p2.Email AND p1.Id > p2.Id
  6. ;

select语句替换成delete语句就可以了:

  1. delete p1 from Person p1,
  2. Person p2
  3. where
  4. p1.Email = p2.Email and p1.Id > p2.Id;

最后附上建表和插入语句:

  1. drop table if exists Person;
  2. create table Person(
  3. Id tinyint not null primary key auto_increment,
  4. Email varchar(30) not null
  5. )engine=InnoDB;
  6. insert into Person (Id, Email)values(1,'john@example.com'),(2,'bob@example.com'),(3,'john@example.com');

mysql中的join小结

join称为连接,连接的主要作用是根据两个或多个表中的列之间的关系,获取存在于不同表中的数据。连接可以分为以下几类:

下面通过具体的例子来分析。假如有两个表t1t2,建表和插入语句如下:

  1. drop table if exists t1;
  2. create table t1(
  3. id tinyint not null primary key auto_increment,
  4. name varchar(30) not null,
  5. description varchar(255) default null
  6. )engine=InnoDB;
  7. create table t2 like t1;
  8. insert into t1 values(1,'林丹','获得12奥运会冠军'),(2,'李宗伟','获得12奥运会亚军'),(3,'谌龙','获得12奥运会季军');
  9. insert into t2 values(1,'谌龙','获得16奥运会冠军'),(2,'李宗伟','获得16奥运会亚军'),(3,'安赛龙','获得16奥运会季军');

两个表的结构分别如下:


t1


t2

交叉连接(cross join)

cross join指的就是两个表的笛卡尔积。上面说了,在Mysql中,Cross JoinInner Join 是等价的,但是在标准SQL中,它们并不等价,Inner Join 用于带有on表达式的连接,反之用Cross Join

以下几种方式均可以产生笛卡尔积:

  1. select * from t1 cross join t2;
  2. select * from t1 inner join t2;
  3. select * from t1 join t2;
  4. select * from t1,t2;
  5. select * from t1 nature join t2;
  6. select * from t1 natura join t2;

产生的结果集为:



内连接(inner join)

通过上面的可以看到,内连接和交叉连接在mysql中的效果是一样的。但是内连接一般会配合on一起使用,用于在结果选出符合某个条件的记录,即两个表的交集。



下面的语句是在cross join集中选择name字段相等的记录。

  1. select * from t1 inner join t2 on t1.name=t2.name;

产生的结果集如下:



下面的语句是只在t1中选择,而不是在cross join集中。tt1tt2是交叉表中左右两个表的名字。

  1. select tt1.*
  2. from
  3. t1 tt1
  4. inner join
  5. t2 tt2
  6. on tt1.name=tt2.name
  7. ;
  8. # 不取实例,和上面语句的结果一样
  9. select t1.*
  10. from
  11. t1
  12. inner join
  13. t2
  14. on t1.name=t2.name
  15. ;

产生的结果集如下:



左外连接(left join)

从左表产生一套完整的记录,还有右边匹配的记录,如果没有匹配就包含null。



  1. select *
  2. from
  3. t1 tt1
  4. left join
  5. t2 tt2 on tt1.name=tt2.name;

产生的结果集如下:



只查询左表的数据,不包含右表的,使用where 限制右表key为null



  1. select *
  2. from
  3. t1 tt1
  4. left join
  5. t2 tt2
  6. on tt1.name=tt2.name
  7. where tt2.name is null;

产生的结果集如下:



右外连接(right join)

从右表产生一套完整的记录,还有左边匹配的记录,如果没有匹配就包含null。和上述左连接类似。



需要注意的是,左连接表1、表2等价于右连接表2、表1

  1. #这两个语句等价
  2. select * from t2 right join t1 using(name);
  3. select * from t1 left join t2 using(name);

产生的结果集如下:



只查询右表的数据,不包含左表的,使用where 限制右表key为null



  1. select *
  2. from
  3. t1 tt1
  4. right join
  5. t2 tt2
  6. on tt1.name=tt2.name
  7. where tt1.name is null;

产生的结果集如下:



全连接(full join)

使用union语句实现全连接。



  1. select *
  2. from
  3. t1
  4. left join
  5. t2
  6. on t1.name=t2.name
  7. union
  8. select *
  9. from
  10. t1
  11. right join
  12. t2
  13. on t1.name=t2.name;

产生的结果集如下:



求差集

两表的全连接中除去重合的部分,即两张表分别的特有部分的合集。



  1. select *
  2. from
  3. t1
  4. left join
  5. t2
  6. on t1.name=t2.name
  7. where t2.name is null
  8. union
  9. select *
  10. from
  11. t1
  12. right join
  13. t2
  14. on t1.name=t2.name
  15. where t1.name is null;

产生的结果集如下:



参考

MySQL的join关键字详解
MySQL之join语句
MySQL的JOIN用法
MySQL的Join使用


197. Rising Temperature

描述:

  1. Given a Weather table, write a SQL query to find all dates' Ids with higher temperature compared to its previous (yesterday's) dates.
  1. +---------+------------------+------------------+
  2. | Id(INT) | RecordDate(DATE) | Temperature(INT) |
  3. +---------+------------------+------------------+
  4. | 1 | 2015-01-01 | 10 |
  5. | 2 | 2015-01-02 | 25 |
  6. | 3 | 2015-01-03 | 20 |
  7. | 4 | 2015-01-04 | 30 |
  8. +---------+------------------+------------------+
  9. For example, return the following Ids for the above Weather table:
  1. +----+
  2. | Id |
  3. +----+
  4. | 2 |
  5. | 4 |
  6. +----+

思路:
使用inner join。下面使用到的TO_DAYS函数返回一个天数: 从年份0开始的天数。
比如SELECT TO_DAYS(‘1997-10-07′);的结果Wie729669,就是从0年开始 到1997107号之间的天数。

  1. SELECT t1.Id
  2. FROM
  3. Weather t1
  4. INNER JOIN
  5. Weather t2
  6. ON TO_DAYS(t1.RecordDate) = TO_DAYS(t2.RecordDate) + 1
  7. WHERE t1.Temperature > t2.Temperature;

还可以使用datediff()函数。它的语法为DATEDIFF(date1,date2)。比如SELECT DATEDIFF('2008-12-30','2008-12-29') AS DiffDate的结果为1

  1. select
  2. w1.Id as 'id'
  3. from
  4. Weather w1
  5. inner join
  6. Weather w2
  7. on datediff(w1.RecordDate, w2.RecordDate)=1
  8. where w1.Temperature > w2.Temperature
  9. ;

627. Swap Salary

描述:

  1. Given a table salary, such as the one below, that has m=male and f=female values. Swap all f and m values (i.e., change all f values to m and vice versa) with a single update query and no intermediate temp table.

For example:

  1. | id | name | sex | salary |
  2. |----|------|-----|--------|
  3. | 1 | A | m | 2500 |
  4. | 2 | B | f | 1500 |
  5. | 3 | C | m | 5500 |
  6. | 4 | D | f | 500 |

After running your query, the above salary table should have the following rows:

  1. | id | name | sex | salary |
  2. |----|------|-----|--------|
  3. | 1 | A | f | 2500 |
  4. | 2 | B | m | 1500 |
  5. | 3 | C | f | 5500 |
  6. | 4 | D | m | 500 |

思路:
可以使用case-when-else语句。

  1. UPDATE salary
  2. SET sex = (CASE WHEN sex = 'm'
  3. THEN 'f'
  4. ELSE 'm'
  5. END);

还可以使用if语句。

  1. UPDATE salary SET sex = IF(sex = 'm', 'f', 'm')

case语句小结

case 具有两种格式:

  1. # 简单case函数
  2. case input_expression
  3. when when_expression_1 then
  4. result_expression_1
  5. when when_expression_2 then
  6. result_expression_2
  7. else
  8. else_result_expression
  9. end
  10. # case搜索函数
  11. case
  12. when Boolean_expression_1 then
  13. result_expression_1
  14. when Boolean_expression_2 then
  15. result_expression_2
  16. else
  17. else_result_expression
  18. end

下面举个例子,首先建立casetest表并插入语句,建表语句和插入数据语句如下:

  1. drop table if exists casetest;
  2. create table casetest(
  3. id tinyint not null primary key auto_increment,
  4. name varchar(30) not null,
  5. gender varchar(13) not null,
  6. birthday date not null
  7. )engine=InnoDB;
  8. insert into casetest values(1,'Bob','male','1895-03-26'),(2,'Alice','female','1999-3-10'),(3,'Tom','male','1995-02-21'),(4,'Jerry','male','1885-03-14'),(5,'Dog','female','1996-05-23');

表的结构如下:

  1. +----+-------+--------+------------+
  2. | id | name | gender | birthday |
  3. +----+-------+--------+------------+
  4. | 1 | Bob | male | 1895-03-26 |
  5. | 2 | Alice | female | 1999-03-10 |
  6. | 3 | Tom | male | 1995-02-21 |
  7. | 4 | Jerry | male | 1885-03-14 |
  8. | 5 | Dog | female | 1996-05-23 |
  9. +----+-------+--------+------------+

使用简单case函数进行选择

  1. select *,
  2. case
  3. when birthday<'1981' then 'old'
  4. when birthday>'1988' then 'yong'
  5. else 'ok' end yorn
  6. from casetest
  7. ;

结果集如下:

  1. | id | name | gender | birthday | YORN |
  2. +----+-------+--------+------------+------+
  3. | 1 | Bob | male | 1895-03-26 | old |
  4. | 2 | Alice | female | 1999-03-10 | yong |
  5. | 3 | Tom | male | 1995-02-21 | yong |
  6. | 4 | Jerry | male | 1885-03-14 | old |
  7. | 5 | Dog | female | 1996-05-23 | yong |
  8. +----+-------+--------+------------+------+

使用case搜索函数进行选择

  1. select *,
  2. case name
  3. when 'Bob' then 'old'
  4. when 'Alice' then 'yong'
  5. when 'Tom' then 'old'
  6. else 'ok' end yorn
  7. from casetest
  8. ;

结果集如下:

  1. +----+-------+--------+------------+------+
  2. | id | name | gender | birthday | YORN |
  3. +----+-------+--------+------------+------+
  4. | 1 | Bob | male | 1895-03-26 | old |
  5. | 2 | Alice | female | 1999-03-10 | yong |
  6. | 3 | Tom | male | 1995-02-21 | old |
  7. | 4 | Jerry | male | 1885-03-14 | ok |
  8. | 5 | Dog | female | 1996-05-23 | ok |
  9. +----+-------+--------+------------+------+

使用cas搜索函数进行更新

下面语句的功能是将表中的malefemale互换。

  1. update casetest
  2. set gender = (
  3. case gender
  4. when 'male' then 'female'
  5. else 'male'
  6. end
  7. )
  8. ;

之后使用select进行选择,结果集如下:

  1. +----+-------+--------+------------+
  2. | id | name | gender | birthday |
  3. +----+-------+--------+------------+
  4. | 1 | Bob | female | 1895-03-26 |
  5. | 2 | Alice | male | 1999-03-10 |
  6. | 3 | Tom | female | 1995-02-21 |
  7. | 4 | Jerry | female | 1885-03-14 |
  8. | 5 | Dog | male | 1996-05-23 |
  9. +----+-------+--------+------------+

if语句小结

基本语法为:

  1. IF(condition, value_if_true, value_if_false)

IF函数根据条件的结果为true或false,返回第一个值,或第二个值。例子如下。

  1. select *,if(gender='male',1,2) as gender_id from casetest;
  2. +----+-------+--------+------------+-----------+
  3. | id | name | gender | birthday | gender_id |
  4. +----+-------+--------+------------+-----------+
  5. | 1 | Bob | female | 1895-03-26 | 2 |
  6. | 2 | Alice | male | 1999-03-10 | 1 |
  7. | 3 | Tom | female | 1995-02-21 | 2 |
  8. | 4 | Jerry | female | 1885-03-14 | 2 |
  9. | 5 | Dog | male | 1996-05-23 | 1 |
  10. +----+-------+--------+------------+-----------+

184. Department Highest Salary

描述:

  1. The Employee table holds all employees. Every employee has an Id, a salary, and there is also a column for the department Id.
  1. +----+-------+--------+--------------+
  2. | Id | Name | Salary | DepartmentId |
  3. +----+-------+--------+--------------+
  4. | 1 | Joe | 70000 | 1 |
  5. | 2 | Henry | 80000 | 2 |
  6. | 3 | Sam | 60000 | 2 |
  7. | 4 | Max | 90000 | 1 |
  8. +----+-------+--------+--------------+
  1. The Department table holds all departments of the company.
  1. +----+----------+
  2. | Id | Name |
  3. +----+----------+
  4. | 1 | IT |
  5. | 2 | Sales |
  6. +----+----------+
  7. Write a SQL query to find employees who have the highest salary in each of the departments. For the above tables, Max has the highest salary in the IT department and Henry has the highest salary in the Sales department.
  8. +------------+----------+--------+
  9. | Department | Employee | Salary |
  10. +------------+----------+--------+
  11. | IT | Max | 90000 |
  12. | Sales | Henry | 80000 |
  13. +------------+----------+--------+

思路:可以使用group byin来操作。先给出建表语句和插入行的语句:

  1. drop table if exists employee;
  2. create table employee(
  3. id tinyint not null primary key auto_increment,
  4. name varchar(30) not null,
  5. salary int not null,
  6. departmentid tinyint not null
  7. )engine=InnoDB;
  8. drop if exists department;
  9. create table department(
  10. id tinyint not null primary key auto_increment,
  11. name varchar(30) not null
  12. )engine=InnoDB;
  13. insert into employee values(1,'Joe',70000,1),(2,'Henry',80000,2),(3,'Sam',60000,2),(4,'Max',90000,1);
  14. insert into department values(1,'IT'),(2,'Sales');

使用

  1. select
  2. departmentid as departmentid,
  3. max(salary) as maxsalary
  4. from
  5. employee
  6. group by departmentid;

可以查找到如下结果集:

  1. +--------------+-----------+
  2. | departmentid | maxsalary |
  3. +--------------+-----------+
  4. | 1 | 90000 |
  5. | 2 | 80000 |
  6. +--------------+-----------+

将这个结果集作为in的一部分,可以得到最终语句:

  1. select
  2. department.name as Department,
  3. employee.name as Employee,
  4. employee.salary as Salary
  5. from
  6. employee
  7. join
  8. department on employee.departmentid=department.id
  9. where
  10. (employee.departmentid, salary) in
  11. (
  12. select
  13. departmentid, max(salary)
  14. from
  15. employee group by departmentid
  16. )
  17. ;

178. Rank Scores

  1. Write a SQL query to rank scores. If there is a tie between two scores, both should have the same ranking. Note that after a tie, the next ranking number should be the next consecutive integer value. In other words, there should be no "holes" between ranks.
  2. +----+-------+
  3. | Id | Score |
  4. +----+-------+
  5. | 1 | 3.50 |
  6. | 2 | 3.65 |
  7. | 3 | 4.00 |
  8. | 4 | 3.85 |
  9. | 5 | 4.00 |
  10. | 6 | 3.65 |
  11. +----+-------+
  12. For example, given the above Scores table, your query should generate the following report (order by highest score):
  13. +-------+------+
  14. | Score | Rank |
  15. +-------+------+
  16. | 4.00 | 1 |
  17. | 4.00 | 1 |
  18. | 3.85 | 2 |
  19. | 3.65 | 3 |
  20. | 3.65 | 3 |
  21. | 3.50 | 4 |
  22. +-------+------+

先给出建表语句:

  1. drop table if exists scores;
  2. create table scores(
  3. id int not null primary key auto_increment,
  4. score float not null
  5. )engine=InnoDB;
  6. insert into scores values(1,3.50),(2,3.65),(3,4.00),(4,3.85),(5,4.00),(6,3.65);

解法:

  1. select score,
  2. (select count(distinct score) from scores where score>=s.score) as `rank`
  3. from scores s order by score desc;

分析:这个语句中有4个score,先不看中间嵌套的select语句,这个语句的主干是:select score from scores s order by score desc;,所以,第一个score是属于表s的;而第二个和第三个score是数据中间嵌套的select语句的,这样这个语句就分解清楚了,搜索结果包括两列:第一列:分数第二列:大于等于此分数的分数值的不重复个数;按分数降序排列。

180. Consecutive Numbers

  1. Write a SQL query to find all numbers that appear at least three times consecutively.
  2. +----+-----+
  3. | Id | Num |
  4. +----+-----+
  5. | 1 | 1 |
  6. | 2 | 1 |
  7. | 3 | 1 |
  8. | 4 | 2 |
  9. | 5 | 1 |
  10. | 6 | 2 |
  11. | 7 | 2 |
  12. +----+-----+
  13. For example, given the above Logs table, 1 is the only number that appears consecutively for at least three times.
  14. +-----------------+
  15. | ConsecutiveNums |
  16. +-----------------+
  17. | 1 |
  18. +-----------------+

思路,还是用join。一个join走天下。使用

  1. select *
  2. from
  3. logs l1,
  4. logs l2,
  5. logs l3
  6. where
  7. l1.id=l2.id-1
  8. and l2.id=l3.id-1
  9. and l1.num=l2.num
  10. and l2.num=l3.num;

语句之后的结果如下:

  1. +----+-----+----+-----+----+-----+
  2. | id | num | id | num | id | num |
  3. +----+-----+----+-----+----+-----+
  4. | 1 | 1 | 2 | 1 | 3 | 1 |
  5. +----+-----+----+-----+----+-----+

将上面的语句稍微改变一下就得到该题的结果:

  1. select distinct l1.num as `ConsecutiveNums`
  2. from
  3. logs l1,
  4. logs l2,
  5. logs l3
  6. where
  7. l1.id=l2.id-1
  8. and l2.id=l3.id-1
  9. and l1.num=l2.num
  10. and l2.num=l3.num;
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注