表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
DROP TABLE IF EXISTS Person;
CREATE TABLE Person (
PersonId INT,
FirstName VARCHAR(255),
LastName VARCHAR(255)
);
DROP TABLE IF EXISTS Address;
CREATE TABLE Address (
AddressId INT,
PersonId INT,
City VARCHAR(255),
State VARCHAR(255)
);
INSERT INTO
Person(PersonId, LastName, FirstName)
VALUES
(1, 'Wang', 'Allen');
INSERT INTO
Address(AddressId, PersonId, City, State)
VALUES
(1, 2, 'New York City', 'New York');
# 思路:左外连接
SELECT
p.FirstName,
p.LastName,
a.City,
a.State
FROM
Person p LEFT JOIN Address a
ON p.PersonId = a.PersonId;
编写一个 SQL 查询,获取 Employee 表中第二高的薪水(Salary) 。
+----+--------+
| Id | Salary |
+----+--------+
| 1 | 100 |
| 2 | 200 |
| 3 | 300 |
+----+--------+
例如上述 Employee 表,SQL查询应该返回 200 作为第二高的薪水。如果不存在第二高的薪水,那么查询应返回 null。
+---------------------+
| SecondHighestSalary |
+---------------------+
| 200 |
+---------------------+
DROP TABLE IF EXISTS Employee;
CREATE TABLE Employee (
Id INT,
Salary INT
);
INSERT INTO
Employee (Id, Salary)
VALUES
(1, 100),
(2, 200),
(3, 300);
# 查询所有 salary 按照降序排列
SELECT
DISTINCT Salary
FROM
Employee
ORDER BY
Salary DESC;
# 要获取第二高的的薪水,就是获取第二个元素,
# 使用 limit start,count; start:开始查询的位置,count 是查询多少条语句
SELECT
DISTINCT Salary
FROM
Employee
ORDER BY
Salary DESC
LIMIT
1, 1;
# Write your MySQL query statement below
SELECT
(
SELECT
DISTINCT Salary
FROM
Employee
ORDER BY
Salary DESC
LIMIT
1, 1
) SecondHighestSalary
编写一个 SQL 查询,获取 Employee 表中第 n 高的薪水(Salary)。
+----+--------+
| Id | Salary |
+----+--------+
| 1 | 100 |
| 2 | 200 |
| 3 | 300 |
+----+--------+
例如上述 Employee 表,n = 2 时,应返回第二高的薪水 200。如果不存在第 n 高的薪水,那么查询应返回 null,因此再嵌套一层子查询。
+------------------------+
| getNthHighestSalary(2) |
+------------------------+
| 200 |
+------------------------+
CREATE FUNCTION getNthHighestSalary(N INT) RETURNS INT
BEGIN
# 注意 LIMIT 的 start 是从 0 开始的,第 N 实际上是第 (N-1)
SET
N = N - 1;
RETURN
(
SELECT
DISTINCT Salary
FROM
Employee
ORDER BY
Salary DESC
LIMIT
N, 1
);
END
编写一个 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
DROP TABLE IF EXISTS Scores;
CREATE TABLE Scores
(
Id INT,
Score DECIMAL(3, 2)
);
INSERT INTO
Scores(Id, Score)
VALUES
(1, 3.5),
(2, 3.65),
(3, 4.0),
(4, 3.85),
(5, 4.0),
(6, 3.65);
SELECT
a.Score AS Score,
(
SELECT
COUNT(DISTINCT b.Score)
FROM
Scores b
WHERE
b.Score >= a.Score
) AS 'Rank'# 由于Rank为关键字,所以必须加``
FROM
Scores a
ORDER BY
a.Score DESC
编写一个 SQL 查询,查找所有至少连续出现三次的数字。
+----+-----+
| Id | Num |
+----+-----+
| 1 | 1 |
| 2 | 1 |
| 3 | 1 |
| 4 | 2 |
| 5 | 1 |
| 6 | 2 |
| 7 | 2 |
+----+-----+
例如,给定上面的 Logs 表, 1 是唯一连续出现至少三次的数字。
+-----------------+
| ConsecutiveNums |
+-----------------+
| 1 |
+-----------------+
DROP TABLE IF EXISTS LOGS;
CREATE TABLE LOGS
(
Id INT,
Num INT
);
INSERT INTO
LOGS (Id, Num)
VALUES
(1, 1),
(2, 1),
(3, 1),
(4, 2),
(5, 1),
(6, 2),
(7, 2);
# 思路:要求是连续出现 3 次,可使用 3 张该表
# 判断条件 Id 是不相同的,但是 Num 是相同的,并且 Id 是连续变化的
# 需要 DISTINCT 进行去重
SELECT
DISTINCT L1.Num ConsecutiveNums
FROM
Logs L1,
Logs L2,
Logs L3
WHERE
L2.Id = L1.Id + 1
AND L3.Id = L2.Id + 1
AND L2.Num = L1.Num
AND L3.Num = L2.Num;
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 |
+----------+
DROP TABLE IF EXISTS Employee;
CREATE TABLE Employee (
Id INT,
Name VARCHAR(255),
Salary INT,
ManagerId INT
);
INSERT INTO
Employee (Id, NAME, Salary, ManagerId)
VALUES
(1, 'Joe', 70000, 3),
(2, 'Henry', 80000, 4),
(3, 'Sam', 60000, NULL),
(4, 'Max', 90000, NULL);
# 思路:Employee e1 INNER JOIN Employee e2 ON e1.managerid = e2.id 比如
+----+-------+--------+-----------+
| Id | Name | Salary | ManagerId |
+----+-------+--------+-----------+
| 1 | Joe | 70000 | 3 |
| 2 | Henry | 80000 | 4 |
| 3 | Sam | 60000 | NULL |
| 4 | Max | 90000 | NULL |
+----+-------+--------+-----------+
根据 e1.managerid = e2.id 条件进行内连接后,得到
+----+-------+--------+-----------+-------+--------+-----------+
| Id | Name | Salary | ManagerId | Name | Salary | ManagerId |
+----+-------+--------+-----------+-------+--------+-----------+
| 1 | Joe | 70000 | 3 | Sam | 60000 | NULL |
| 2 | Henry | 80000 | 4 | Max | 90000 | NULL |
+----+-------+--------+-----------+-------+--------+-----------+
SELECT
e1.Name AS Employee
FROM
Employee e1, Employee e2
WHERE
e1.ManagerId = e2.id
AND e1.Salary > e2.Salary;
编写一个 SQL 查询,查找 Person 表中所有重复的电子邮箱。
示例:
+----+---------+
| Id | Email |
+----+---------+
| 1 | a@b.com |
| 2 | c@d.com |
| 3 | a@b.com |
+----+---------+
根据以上输入,你的查询应返回以下结果:
+---------+
| Email |
+---------+
| a@b.com |
+---------+
DROP TABLE IF EXISTS Person;
CREATE TABLE Person
(
Id INT,
Email VARCHAR(255)
);
INSERT INTO
Person (Id, Email)
VALUES
(1, '[email protected]'),
(2, '[email protected]'),
(3, '[email protected]');
# 思路:与前题类似
# 1、按照 mail 进行分组
# 2、统计出现次数 >=2 就是重复的邮件
SELECT
Email
FROM
Person
GROUP BY
Email
HAVING
COUNT(Id) >= 2;
某网站包含两个表,Customers 表和 Orders 表。编写一个 SQL 查询,找出所有从不订购任何东西的客户。
Customers 表:
+----+-------+
| Id | Name |
+----+-------+
| 1 | Joe |
| 2 | Henry |
| 3 | Sam |
| 4 | Max |
+----+-------+
Orders 表:
+----+------------+
| Id | CustomerId |
+----+------------+
| 1 | 3 |
| 2 | 1 |
+----+------------+
例如给定上述表格,你的查询应返回:
+-----------+
| Customers |
+-----------+
| Henry |
| Max |
+-----------+
DROP TABLE IF EXISTS Customers;
CREATE TABLE Customers
(
Id INT,
Name VARCHAR(255)
);
DROP TABLE IF EXISTS Orders;
CREATE TABLE Orders
(
Id INT,
CustomerId INT
);
INSERT INTO
Customers(Id, NAME)
VALUES
(1, 'Joe'),
(2, 'Henry'),
(3, 'Sam'),
(4, 'Max');
INSERT INTO
Orders(Id, CustomerId)
VALUES
(1, 3),
(2, 1);
# 解法一:左外连接
SELECT
Name AS Customers
FROM
Customers LEFT JOIN Orders
ON Customers.Id = Orders.CustomerId
WHERE
Orders.CustomerId IS NULL;
# 解法二:子查询方式
SELECT
Name AS Customers
FROM
Customers
WHERE
Id NOT IN
(
SELECT
CustomerId
FROM
Orders
);
Employee 表包含所有员工信息,每个员工有其对应的 Id, Salary 和 DepartmentId。
+----+-------+--------+--------------+
| 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 |
+------------+----------+--------+
解释:
DROP TABLE IF EXISTS Employee;
CREATE TABLE Employee
(
Id INT,
Name VARCHAR(255),
Salary INT,
DepartmentId INT
);
DROP TABLE IF EXISTS Department;
CREATE TABLE Department
(
Id INT,
Name VARCHAR(255)
);
INSERT INTO
Employee(Id, Name, Salary, DepartmentId)
VALUES
(1, 'Joe', 70000, 1),
(2, 'Henry', 80000, 2),
(3, 'Sam', 60000, 2),
(4, 'Max', 90000, 1);
INSERT INTO
Department(Id, Name)
VALUES
(1, 'IT'),
(2, 'Sales');
# 创建一个临时表,包含了部门员工的最大薪资。
SELECT
DepartmentId,
MAX(Salary) Salary
FROM
Employee
GROUP BY
DepartmentId;
# 结果:
+--------------+--------+
| DepartmentId | Salary |
+--------------+--------+
| 1 | 90000 |
| 2 | 80000 |
+--------------+--------+
使用连接找到一个部门中薪资等于临时表中最大薪资的员工。
SELECT
d.Name as Department,
e.Name as Employee,
m.Salary
FROM
Employee e,
Department d,
(
SELECT
DepartmentId,
MAX(Salary) Salary
FROM
Employee
GROUP BY
DepartmentId
) m
WHERE
e.DepartmentId = m.DepartmentId
AND e.Salary = m.Salary
AND d.Id = e.DepartmentId;
编写一个 SQL 查询,来删除 Person 表中所有重复的电子邮箱,重复的邮箱里只保留 Id 最小 的那个。
+----+------------------+
| Id | Email |
+----+------------------+
| 1 | john@example.com |
| 2 | bob@example.com |
| 3 | john@example.com |
+----+------------------+
Id 是这个表的主键。
例如,在运行你的查询语句之后,上面的 Person 表应返回以下几行:
+----+------------------+
| Id | Email |
+----+------------------+
| 1 | john@example.com |
| 2 | bob@example.com |
+----+------------------+
提示:
思路一
# 思路一:将一张表看成两张表来进行操作
DELETE
p1
FROM
Person p1,
Person p2
WHERE
p1.Email = p2.Email
AND p1.Id > p2.Id
思路二
# 思路二:
# 第一步:根据 email 进行分组,获取 email 对应的最小 id,一个 email 对应一个最小的 id
SELECT MIN(Id) AS Id FROM Person GROUP BY Email;
# 第二步:删除不在该 id 集合中的数据
DELETE
Person
FROM
Person
WHERE
Id NOT IN
(
SELECT
Id
FROM
(
SELECT
MIN(Id) AS Id
FROM
Person
GROUP BY
Email
) m
);
# 应该注意的是上述解法额外嵌套了一个 SELECT 语句。
# 如果不这么做,会出现错误:You can't specify target table 'Person' for update in FROM clause。
这里有张 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 |
+--------------+-------------+--------------+
DROP TABLE IF EXISTS World;
CREATE TABLE World
(
name VARCHAR(255),
continent VARCHAR(255),
area INT,
population INT,
gdp INT
);
INSERT INTO
World (name, continent, area, population, gdp)
VALUES
('Afghanistan', 'Asia', '652230', '25500100', '203430000'),
('Albania', 'Europe', '28748', '2831741', '129600000'),
('Algeria', 'Africa', '2381741', '37100000', '1886810000'),
('Andorra', 'Europe', '468', '78115', '37120000'),
('Angola', 'Africa', '1246700', '20609294', '1009900000');
# 思路:
# 1、根据样例,我们知道。查询字段是 name population 和 area
# 2、查询条件是 area > 3000000 || population > 25000000
SELECT
name,population,area
FROM
World
WHERE
area > 3000000
OR population > 25000000;
有一个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 |
+---------+
提示:
DROP TABLE IF EXISTS courses;
CREATE TABLE courses
(
student VARCHAR(255),
class VARCHAR(255)
);
INSERT INTO
courses(student, class)
VALUES
( 'A', 'Math' ),
( 'B', 'English' ),
( 'C', 'Math' ),
( 'D', 'Biology' ),
( 'E', 'Math' ),
( 'F', 'Computer' ),
( 'G', 'Math' ),
( 'H', 'Math' ),
( 'I', 'Math' );
# 思路:
# 1、很显然要按照 class 进行分组
# 2、然后按照分组后的 class 来统计学生的人数
SELECT
class
FROM
courses
GROUP BY
class
HAVING
COUNT(DISTINCT student) >= 5;
某城市开了一家新的电影院,吸引了很多人过来看电影。该电影院特别注意用户体验,专门有个 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 |
+---------+-----------+--------------+-----------+
DROP TABLE IF EXISTS cinema;
CREATE TABLE cinema
(
id INT,
movie VARCHAR(255),
description VARCHAR(255),
rating FLOAT(2,1)
);
INSERT INTO
cinema(id, movie, description, rating)
VALUES
(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);
#思路:
#1、观察测试用例的查询结果,我们知道,其实查询的是所有的字段
#2、id 为奇数,则查询条件为 id % 2 = 1
SELECT
id,movie,description,rating
FROM
cinema
WHERE
id % 2 = 1
AND description != 'boring'
ORDER BY
rating DESC;
小美是一所中学的信息科技老师,她有一张 seat 座位表,平时用来储存学生名字和与他们相对应的座位 id。
其中纵列的 id 是连续递增的
小美想改变相邻俩学生的座位。
你能不能帮她写一个 SQL query 来输出小美想要的结果呢?
示例:
+---------+---------+
| id | student |
+---------+---------+
| 1 | Abbot |
| 2 | Doris |
| 3 | Emerson |
| 4 | Green |
| 5 | Jeames |
+---------+---------+
假如数据输入的是上表,则输出结果如下:
+---------+---------+
| id | student |
+---------+---------+
| 1 | Doris |
| 2 | Abbot |
| 3 | Green |
| 4 | Emerson |
| 5 | Jeames |
+---------+---------+
注意:
如果学生人数是奇数,则不需要改变最后一个同学的座位。
DROP TABLE IF EXISTS seat;
CREATE TABLE seat
(
id INT,
student VARCHAR(255)
);
INSERT INTO
seat(id, student)
VALUES
('1', 'Abbot'),
('2', 'Doris'),
('3', 'Emerson'),
('4', 'Green'),
('5', 'Jeames');
# 对于所有座位 id 是奇数的学生,修改其 id 为 id+1,如果最后一个座位 id 也是奇数,则最后一个座位 id 不修改。
# 对于所有座位 id 是偶数的学生,修改其 id 为 id-1。
SELECT
(
CASE
WHEN MOD(id, 2) = 0 THEN id - 1
WHEN MOD(id, 2) != 0 AND id != counts THEN id + 1
ELSE id
END
) AS id,
student
FROM
seat,
(
SELECT
COUNT(*) AS counts
FROM
seat
) AS seat_counts
ORDER BY
id ASC;
给定一个 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 |
DROP TABLE IF EXISTS salary;
CREATE TABLE salary
(
id INT,
NAME VARCHAR(100),
sex CHAR(1),
salary INT
);
INSERT INTO
salary(id, NAME, sex, salary)
VALUES
('1', 'A', 'm', '2500'),
('2', 'B', 'f', '1500'),
('3', 'C', 'm', '5500'),
('4', 'D', 'f', '500');
# 思路:
# l、利用位运算:若x = a,则x ^ b ^ a = b;若x = b,则x ^ b ^ a = a;
# 2、利用 ASCII 函数将字符转换为数值进行运算,然后再利用 CHAR 函数将数值转换为字符
UPDATE
salary
SET
sex = CHAR(ASCII(sex) ^ ASCII('m') ^ ASCII('f'));