重要知识点(题目中提取)
UNION/UNION ALL
SELECT column1, column2, ...
FROM table1
UNION
SELECT column1, column2, ...
FROM table2;
SELECT column1, column2, ...
FROM table1
UNION ALL
SELECT column1, column2, ...
FROM table2;
两者主要的区别在于是否重复
IFNULL
IFNULL(expression, alt_value)
定义变量
MySQL 中的变量
用户变量(会话级)
-
以
@开头,无需声明,直接赋值使用。 -
作用域为当前会话。
sql
SET @var = 10;
SELECT @var; -- 输出 10
SELECT @var := column_name FROM table_name LIMIT 1;
局部变量(存储过程/函数中)
- 使用
DECLARE声明,作用域限于代码块。
sql
DELIMITER $$
CREATE PROCEDURE example()
BEGIN
DECLARE v_count INT DEFAULT 0;
SET v_count = 10;
SELECT v_count;
END$$
DELIMITER ;
系统变量
- 以
@@开头,分为全局和会话级。
sql
SELECT @@version; -- 当前版本
SET GLOBAL max_connections = 200;
时间函数
1. 获取当前时间
用于获取服务器当前的日期或时间。
| 数据库 | 函数/语句 | 说明 |
|---|---|---|
| MySQL | NOW(), CURDATE(), CURTIME() |
分别获取日期时间、日期、时间 |
| SQL Server | GETDATE(), SYSDATETIME() |
获取当前数据库系统时间 |
| Oracle | SYSDATE, SYSTIMESTAMP |
分别获取日期时间和带时戳时间 |
| PostgreSQL | NOW(), CURRENT_DATE |
标准 SQL 兼容性好 |
示例:
sql
SELECT NOW(); -- MySQL/PG
SELECT GETDATE(); -- SQL Server
SELECT SYSDATE FROM DUAL; -- Oracle
2. 提取时间部分
从时间字段中提取年、月、日、小时等。
-
MySQL:
YEAR(date),MONTH(date),DAY(date),HOUR(time) -
SQL Server:
DATEPART(YEAR, date),YEAR(date) -
Oracle:
EXTRACT(YEAR FROM date),TO_CHAR(date, 'YYYY') -
PostgreSQL:
EXTRACT(YEAR FROM date),DATE_PART('year', date)
示例(获取年份):
sql
SELECT YEAR(order_date) FROM orders; -- MySQL/SQL Server
SELECT EXTRACT(YEAR FROM order_date) FROM orders; -- Oracle/PG
3. 格式化与转换
将时间转为字符串,或将字符串转为时间类型。
| 数据库 | 时间转字符串 | 字符串转时间 |
|---|---|---|
| MySQL | DATE_FORMAT(date, '%Y-%m-%d') |
STR_TO_DATE(str, '%Y-%m-%d') |
| SQL Server | CONVERT(VARCHAR, date, 120) |
CONVERT(DATETIME, str, 120) |
| Oracle | TO_CHAR(date, 'YYYY-MM-DD') |
TO_DATE(str, 'YYYY-MM-DD') |
| PostgreSQL | TO_CHAR(date, 'YYYY-MM-DD') |
TO_TIMESTAMP(str, 'YYYY-MM-DD') |
示例:
sql
-- MySQL: 将时间格式化为 '2023-10-01'
SELECT DATE_FORMAT(NOW(), '%Y-%m-%d');
4. 时间计算(加减与差值)
用于计算时间间隔或增减时间。
-
增加/减少时间:
-
MySQL:
DATE_ADD(date, INTERVAL 7 DAY),date + INTERVAL 1 MONTH -
SQL Server:
DATEADD(DAY, 7, date) -
Oracle:
date + 7(天),ADD_MONTHS(date, 1) -
PostgreSQL:
date + INTERVAL '7 days'
-
-
计算差值:
-
MySQL:
DATEDIFF(date1, date2)(返回天数) -
SQL Server:
DATEDIFF(DAY, date1, date2) -
Oracle:
date1 - date2(返回天数,含小数) -
PostgreSQL:
AGE(date1, date2)或直接相减
-
示例(查询最近 7 天的数据):
sql
-- MySQL
SELECT * FROM table WHERE date_col >= DATE_SUB(NOW(), INTERVAL 7 DAY);
-- SQL Server
SELECT * FROM table WHERE date_col >= DATEADD(DAY, -7, GETDATE());
-- Oracle
SELECT * FROM table WHERE date_col >= SYSDATE - 7;
LeetCodeSQL 重要50题重点习题(考点:高级查询连接和子查询 难度:简单-中等 完成进度:41/50)
1.1164指定日期的产品价格

sql
-- 这个是我自己写的答案,对照着官方题解,需要进行的改进
-- 更改的地方在于直接使用子查询,不应该使用连接
-- 直接写ifnull()
select
Product_ID.product_id,
(case when Price_Table.new_price is not NULL then Price_Table.new_price else 10 end) as price
from (select distinct
product_id,
(case when 1=1 then 10 end) as origin_price
from Products) as Product_ID
left join (select
Max_Date.product_id,
Products.new_price
from
(select
product_id,
max(date(change_date)) as max_date
from Products
where date(change_date) <= date('2019-08-16')
group by product_id) as Max_Date
left join Products
on Max_Date.product_id=Products.product_id
and Max_Date.max_date=Products.change_date) as Price_Table
on Product_ID.product_id=Price_Table.product_id
;
-- 官方题解
select p1.product_id, ifnull(p2.new_price, 10) as price
from (
select distinct product_id
from products
) as p1 -- 所有的产品
left join (
select product_id, new_price
from products
where (product_id, change_date) in (
select product_id, max(change_date)
from products
where change_date <= '2019-08-16'
group by product_id
)
) as p2 -- 在 2019-08-16 之前有过修改的产品和最新的价格
on p1.product_id = p2.product_id;
2.1907按分类统计薪水

sql
-- 这道题需要掌握的是UNION,记得抽空复习一下呀
SELECT
'Low Salary' AS category,
SUM(CASE WHEN income < 20000 THEN 1 ELSE 0 END) AS accounts_count
FROM
Accounts
UNION
SELECT
'Average Salary' category,
SUM(CASE WHEN income >= 20000 AND income <= 50000 THEN 1 ELSE 0 END)
AS accounts_count
FROM
Accounts
UNION
SELECT
'High Salary' category,
SUM(CASE WHEN income > 50000 THEN 1 ELSE 0 END) AS accounts_count
FROM
Accounts
3.1204最后一个能进入巴士的人

sql
select
person_name
from Queue
where turn=(
select max(t)
from(select
turn as t,
person_id,
weight,
(select sum(weight) from Queue where turn<=t) as total_weight
from Queue
having total_weight<=1000
order by t) as T
)
-- 改进
SELECT a.person_name
FROM Queue a, Queue b
WHERE a.turn >= b.turn
GROUP BY a.person_id HAVING SUM(b.weight) <= 1000
ORDER BY a.turn DESC
LIMIT 1
-- 把定义变量的方式进行学习一下
SELECT a.person_name
FROM (
SELECT person_name, @pre := @pre + weight AS weight
FROM Queue, (SELECT @pre := 0) as tmp
ORDER BY turn
) as s
WHERE a.weight <= 1000
ORDER BY a.weight DESC
LIMIT 1
4.1978上级经理已离职员工

python
# Write your MySQL query statement below
select
employee_id
from Employees
where salary<30000
and manager_id not in (select employee_id from Employees)
order by employee_id;
5.626换座位

sql
# Write your MySQL query statement below
#复习一下union用法
/*
这是对于具有重复值
SELECT column1, column2, ...
FROM table1
UNION ALL
SELECT column1, column2, ...
FROM table2;
这是不具有重复值
SELECT column1, column2, ...
FROM table1
UNION
SELECT column1, column2, ...
FROM table2;
*/
#直接加1
/*
*/
SELECT
case
when id = (SELECT max(id) FROM Seat) then id else id+1 end as id,
student
FROM Seat
WHERE id%2 <> 0
UNION
SELECT
id-1 as id,
student
FROM Seat
WHERE id%2 = 0
order by id;
6.1341电影评分

sql
#查找评论电影数量最多的用户名。如果出现平局,返回字典序较小的用户名。
#使用count
/*
#查找在 February 2020 平均评分最高 的电影名称。如果出现平局,返回字典序较小的电影名称。
*/
(SELECT
name as results
FROM (
SELECT
MovieRating.user_id,
Users.name,
COUNT(MovieRating.user_id) as id_count
FROM MovieRating
left join Users
on MovieRating.user_id=Users.user_id
GROUP BY user_id
ORDER BY id_count DESC,name) as Max_Comments
limit 1)
UNION ALL
(SELECT
title as results
FROM(
SELECT
MovieRating.movie_id,
Movies.title,
avg(MovieRating.rating) as avg_rating
FROM MovieRating
left join Movies
on MovieRating.movie_id=Movies.movie_id
WHERE date(created_at) >= date('2020-02-01') and date(created_at) <= date('2020-02-29')
GROUP BY movie_id
ORDER BY avg_rating DESC, title) as Max_Rating
limit 1)
7.1321餐馆营业额变化增长

sql
# Write your MySQL query statement below
# 这道题和之前那道题目有共同点
# 先算一下一共可以有多少个星期:以案列为例子,假设有11天,那一共有四个日期可以算星期,所以可以有
/*SELECT
visited_on,
amount,
round(amount/7, 2) as average_amount
FROM (
SELECT
v_end as visited_on,
(SELECT sum(amount) FROM Customer WHERE DATE(visited_on) BETWEEN DATE(v_begin) AND DATE(v_end)) as amount
FROM (
SELECT DISTINCT
visited_on as v_end,
DATE_SUB(visited_on, INTERVAL 6 DAY) as V_begin
FROM Customer
WHERE DATE(visited_on)>DATE_SUB((SELECT MAX(DATE(visited_on)) FROM Customer), INTERVAL 4 DAY)
ORDER BY visited_on DESC
) as Date_week) as DATE_Amount
ORDER BY visited_on
*/
SELECT
visited_on,
amount,
round(amount/7, 2) as average_amount
FROM (
SELECT
v_end as visited_on,
(SELECT sum(amount) FROM Customer WHERE DATE(visited_on) BETWEEN DATE(V_begin) AND DATE(v_end)) as amount
FROM (
SELECT DISTINCT
visited_on as v_end,
DATE_SUB(visited_on, INTERVAL 6 DAY) as V_begin
FROM Customer
WHERE DATE_SUB(visited_on, INTERVAL 6 DAY) in (SELECT visited_on FROM Customer)
ORDER BY visited_on DESC) as Date_week) as DATE_Amount
ORDER BY visited_on
8.1667修复表中的名字

以上SQL语句使用了三个函数来实现首字母大写的效果:
-- SUBSTRING(name, 1, 1):提取出字符串的第一个字符
-- UPPER(SUBSTRING(name, 1, 1)):将第一个字符转换为大写
-- LOWER(SUBSTRING(name, 2)):将第二个字符开始的字符串转换为小写
-- CONCAT函数将前两个处理后的字符串拼接在一起,得到首字母大写的字符串
sql
# Write your MySQL query statement below
SELECT
user_id,
CONCAT(UPPER(SUBSTRING(name, 1, 1)), LOWER(SUBSTRING(name, 2))) as name
FROM Users
ORDER BY user_id