考察表的 join操作
select
FirstName,
LastName,
City,
State
from
Person
left join
Address
on Person.PersonId = Address.PersonId
考察distinct/ order by/ limit / offset/ IFNULL
select
IFNULL(
(select distinct Salary from Employee order by Salary DESC limit 1 offset 1)
,
NULL
)
as SecondHighestSalary
考察SQL里写带参数的函数function,用group by或者distinct去重。和176一致,只是增加了个参数。
这种解法形式最为简洁直观,但仅适用于查询全局排名问题,如果要求各分组的每个第N名,则该方法不适用。
CREATE FUNCTION getNthHighestSalary(N INT) RETURNS INT
BEGIN
set N := N-1;
RETURN (
# Write your MySQL query statement below.
select distinct Salary
from Employee
order by Salary DESC
limit 1 offset N
);
END
用2张表,对表1中的每个数据都遍历表2找出比之大的数据条数作为rank,考察两表的遍历和count
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
连续出现的意味着相同数字的 Id 是连着的,用个表找3个连号的数据是否相等
select
distinct l1.Num as ConsecutiveNums
from Logs l1, Logs l2, Logs l3
where l1.Id = l2.Id - 1 and l2.Id = l3.Id - 1
and l1.Num = l2.Num and l2.Num = l3.Num
找出表中连续3天流量字段不低于100的记录,选中3个表没有join条件得到笛卡尔积,选三者同时流量超过100的表项,然后选是连续3天的表项,然后去重
SELECT
DISTINCT s1.*
FROM stadium s1, stadium s2, stadium s3
WHERE s1.people >=100 AND s2.people >=100 AND s3.people >=100
AND
(
(s2.id - s1.id = 1 AND s3.id - s2.id = 1 AND s3.id - s1.id = 2)
OR
(s1.id - s2.id = 1 AND s3.id - s1.id = 1 AND s3.id - s2.id = 2)
OR
(s1.id - s2.id = 1 AND s2.id - s3.id = 1 AND s1.id - s3.id = 2)
)
ORDER BY s1.id;
需要两张表,一张表取id另一张表取manager id,可以直接取也可以拼接在一起。考察where
select
a.Name as Employee
from
Employee a, Employee b
where a.ManagerId = b.Id and a.Salary > b.Salary
# 用join,貌似效率更低
select
a.Name as Employee
from
Employee a join Employee b
where a.ManagerId = b.Id and a.Salary > b.Salary
使用group by然后用count计数,构造有这两个特征的临时表,然后用where选取符合条件的。
以上的语句可以用having得到简化。我理解having一般是跟在group by后面,where是筛选数据表中样本,having是筛选分组之后的符合条件的组
在 SQL 中增加 HAVING 子句原因是,WHERE 关键字无法与合计函数一起使用。
当同时含有where子句、group by 子句 、having子句及聚集函数时,执行顺序如下:
- 执行where子句查找符合条件的数据;
- 使用group by 子句对数据进行分组;对group by 子句形成的组运行聚集函数计算每一组的值;
- 最后用having 子句去掉不符合条件的组。
select dno,count(*)
from employee
group by dno
having count(*)>3
select Email from
(select Email, count(Email) as num from Person group by Email) statistic
where num > 1
# 简化后
select
Email
from Person
group by Email
having count(Email) > 1
选2个表,可以是直接取也可以join,然后选取id更小的
DELETE p1
FROM
Person p1, Person p2
WHERE
p1.Email = p2.Email AND p1.Id > p2.Id
# join
DELETE p1
FROM
Person p1 JOIN Person p2
ON p1.Email = p2.Email
WHERE p1.Id > p2.Id
考察not in的用法。
也可以用A left join B,以A为基准,B 中join不到的会呈现为NULL,这部分就是我们要的结果,只在表A不在表B的样本
select
Name as Customers
from Customers
where Id not in (select CustomerId from Orders)
# left join
select
Name as Customers
from Customers
left join
Orders
on Customers.Id = Orders.CustomerId
where Orders.CustomerId is Null
先选出符合结果format的表项,然后用where筛选,in的判断可以用多元组
select
Department.Name as Department,
Employee.Name as Employee,
Employee.Salary as Salary
from
Employee
join
Department
on
Employee.DepartmentId = Department.Id
where
(Employee.DepartmentId, Salary) IN
(
select
DepartmentId, MAX(Salary)
from
Employee
GROUP BY DepartmentId
)
解决思路有点类似 181. 超过经理收入的员工,就是对选出来的表项k要满足:同部门的所有表项salary比它大的数量要小于3
SELECT
Department.Name AS Department,
e1.Name AS Employee,
e1.Salary
From
Employee e1
JOIN
Department
ON
e1.DepartmentId = Department.Id
WHERE
3
>
(SELECT
count(distinct e2.Salary)
FROM
Employee e2
WHERE e2.Salary > e1.Salary AND e2.DepartmentID = e1.DepartmentId # 限定是同一个部门的
)
bonus for not using any built-in function in SQL
SELECT
id,
company,
salary
FROM
Employee e
WHERE 1 <= ABS(
(SELECT COUNT(*) FROM Employee e1 WHERE e.salary >= e1.salary)
-
(SELECT COUNT(*) FROM Employee e2 WHERE e.salary <= e2.salary)
)
GROUP BY company, salary
求出每个月,各个部门的平均工资比公司的平均工资高低。先求每个部门的平均工资,再求公司的平均工资,再join到一张表中用case when判断高还是低
SELECT
d1.pay_month,
d1.department_id,
CASE WHEN
d1.department_avg > c1.company_avg THEN 'higher'
WHEN
d1.department_avg < c1.company_avg THEN 'lower'
ELSE 'same'
END AS 'comparison'
FROM
(
(SELECT
LEFT(s1.pay_date, 7) AS `pay_month`,
e1.department_id, AVG(s1.amount) AS `department_avg`
FROM Salary s1
JOIN
Employee e1
ON
s1.employee_id = e1.employee_id
GROUP BY pay_month, e1.department_id
) d1
LEFT JOIN
(
SELECT
LEFT(pay_date, 7) AS `pay_month`,
AVG(amount) AS `company_avg`
FROM Salary
GROUP BY pay_month
) c1
ON
d1.pay_month = c1.pay_month
)
ORDER BY pay_month DESC, department_id;
要输出manager的名字而非ID
SELECT
`name`
FROM
Employee e1
JOIN
(
SELECT
manager_id
FROM
Employee e2
GROUP BY manager_id
HAVING COUNT(*) >= 5
) e2
ON e1.id = e2.manager_id
日期差一天用DATEDIFF函数,同样这道题有直接取和用join两种方法
SELECT
w1.Id
FROM
Weather w1, Weather w2
WHERE DATEDIFF(w1.RecordDate, w2.RecordDate) = 1
AND w1.Temperature > w2.Temperature
# join
SELECT
Weather.Id
FROM
Weather
JOIN
Weather w
ON DATEDIFF(Weather.RecordDate, w.RecordDate) = 1
AND Weather.Temperature > w.Temperature
group by后用聚合函数如sum之类的,注意Trips里司机和乘客共同构成一条数据,Users里司机和乘客ID是唯一的,是分开的,如何根据User里面的有效状态字段去筛选Trips里有效的样本,第一次join选司机的有效样本,第二次join选乘客的有效样本
SELECT
t.Request_at as Day,
ROUND(
SUM(
IF(t.STATUS = 'completed', 0, 1)
)
/
COUNT(t.STATUS),
2
) AS `Cancellation Rate`
FROM Trips t
JOIN
Users u1 ON (t.client_id = u1.users_id AND u1.banned = 'No')
JOIN
Users u2 ON (t.client_id = u2.users_id AND u2.banned = 'No')
WHERE t.request_at BETWEEN '2013-10-01' AND '2013-10-03'
GROUP BY t.request_at
考点判断id是奇数,用mod函数
SELECT
*
FROM cinema
WHERE description != 'boring' AND MOD(id, 2) = 1
ORDER BY rating DESC
改变表,相邻两个换座位,两两换,不是连续换。用if或者case when,比较麻烦的是要先算出来id总数
SELECT
IF(id%2=0, id-1,
IF(
id = (SELECT COUNT(DISTINCT id) FROM seat s),
id,
id+1
)
) AS id,
student
FROM seat
ORDER BY id;
考察的不是查询,是CRUD里的update
UPDATE salary
SET
sex = CASE sex
WHEN 'm' THEN 'f'
ELSE 'm'
END; # 这个end对应的是case when
# 简洁写法
UPDATE salary
SET
sex = CHAR(ASCII('m') + ASCII('f') - ASCII(sex));
2个表,一个send请求,一个接受请求,求接受率 = 接受数/发送数,注意同一个请求发送-接受多次只算一次
SELECT
IFNULL(
ROUND(
COUNT(DISTINCT requester_id, accepter_id)
/
COUNT(DISTINCT sender_id, sender_to_id)
,
2
)
,
0
) AS accept_rate
FROM
request_accepted, friend_request