【SQL】刷题记录
文章目录
- 一、SQL语法、日期函数等
- 1、语法
- 1.1. join
- left join
- 注意:left join 的其他作用
- 1.2 as
- 1.3 using
- 1. **`USING` 的语法**(注意使用括号)
- 2. **`USING` 的作用**
- 1.4 where VS HAVING
- 1.5 CASE WHEN
- 1.6 if else
- 1.7 IFNULL
- 1.8 OVER 窗口函数
- 1.9 limit 常常与order by一起使用
- 1.10 CONCAT
- 使用示例
- 1.11 `LIKE` 操作符
- 功能
- 语法
- 使用示例 [1527. 患某种疾病的患者](https://leetcode.cn/problems/patients-with-a-condition/)
- 1.12 RECURSIVE
- 举例:生成 **2017 年所有日期**,并统计每周几(星期一到星期日)出现的次数
- **关键部分解析**
- **1. `WITH RECURSIVE` 递归 CTE**
- **2. `SELECT` 主查询**
- **执行过程**
- **1. 递归生成日期**
- **2. 统计星期几**
- **3. 排序**
- **示例结果**
- **注意事项**
- **总结**
- 1.13 ORDER BY
- **排序逻辑总结**
- 1.14 LEAD
- 语法
- 示例
- 1.15 DENSE_RANK RANK ROW_NUMBER
- **三种函数的区别总结**
- **如何选择?**
- 1.16 尽量使用union all而 不使用union
- 1.17 not in 遇到null的问题,使用exists改写
- 2.算术函数
- (1)count (注意加不加group by )
- count sum
- (2)date MySQL 中的日期和时间函数
- 1. 获取当前日期和时间
- 2. 提取日期和时间的部分
- DAYNAME(date)
- 3. 日期和时间的计算
- (3)ROUND AVG
- (4)mod
- (5) 除法
- 3.字符串
- SUBSTRING
- SUBSTRING_INDEX
- 4、表操作
- 4.1 insert into ,replace into
- 4.2 update
- 4.3 delete 删除表中的行(的数据)
- 4.4 truncate table 删除表中数据
- 4.5 create table
- 4.6 alter table
- 4.7 DROP table
- 4.8 创建索引
- 4.9 删除索引
- 5、细节
- 尽量写具体
- 二、leetcode 、牛客题目
- [1280. 学生们参加各科测试的次数](https://leetcode.cn/problems/students-and-examinations/)
- [1934. 确认率](https://leetcode.cn/problems/confirmation-rate/)
- 1. **`AVG(c.action='confirmed')`**
- 2. **`IFNULL(AVG(c.action='confirmed'), 0)`**
- [1251. 平均售价](https://leetcode.cn/problems/average-selling-price/)
- [1633. 各赛事的用户注册率](https://leetcode.cn/problems/percentage-of-users-attended-a-contest/)
- [1211. 查询结果的质量和占比](https://leetcode.cn/problems/queries-quality-and-percentage/)
- AVG 和 SUM/COUNT
- [1193. 每月交易 I](https://leetcode.cn/problems/monthly-transactions-i/)
- [1174. 即时食物配送 II](https://leetcode.cn/problems/immediate-food-delivery-ii/)
- [1141. 查询近30天活跃用户数](https://leetcode.cn/problems/user-activity-for-the-past-30-days-i/)
- [619. 只出现一次的最大数字](https://leetcode.cn/problems/biggest-single-number/)
- [1045. 买下所有产品的客户](https://leetcode.cn/problems/customers-who-bought-all-products/)
- [1731. 每位经理的下属员工数量](https://leetcode.cn/problems/the-number-of-employees-which-report-to-each-employee/description/)
- [610. 判断三角形](https://leetcode.cn/problems/triangle-judgement/)
- case when
- if else
- [180. 连续出现的数字](https://leetcode.cn/problems/consecutive-numbers/)
- 三表连接 自身连接 连续出现
- [1164. (重要)指定日期的产品价格](https://leetcode.cn/problems/product-price-at-a-given-date/)
- [1204. 最后一个能进入巴士的人](https://leetcode.cn/problems/last-person-to-fit-in-the-bus/)
- `SUM(weight) OVER(ORDER BY turn) AS total_weight` 中的 `ORDER BY turn`
- `ORDER BY turn` 在子查询末尾
- [1907. 按分类统计薪水](https://leetcode.cn/problems/count-salary-categories/)
- 错误解法
- 正确code
- leetcode官方解法
- [1978. 上级经理已离职的公司员工](https://leetcode.cn/problems/employees-whose-manager-left-the-company/)
- [626. 换座位](https://leetcode.cn/problems/exchange-seats/)
- [1341. 电影评分](https://leetcode.cn/problems/movie-rating/)
- [1667. 修复表中的名字](https://leetcode.cn/problems/fix-names-in-a-table/)
- [1527. 患某种疾病的患者](https://leetcode.cn/problems/patients-with-a-condition/)
- [1321. 餐馆营业额变化增长](https://leetcode.cn/problems/restaurant-growth/)
- [585. 2016年的投资](https://leetcode.cn/problems/investments-in-2016/)
- [176. 第二高的薪水](https://leetcode.cn/problems/second-highest-salary/)
- [184. 部门工资最高的员工](https://leetcode.cn/problems/department-highest-salary/)(每个部门)
- 错误写法
- 正确写法
- [185. 部门工资前三高的所有员工](https://leetcode.cn/problems/department-top-three-salaries/)
- 注意下面的错误:
- **SQL125** **得分不小于平均分的最低分**
- 错误代码
- 正确代码
- **SQL124** **统计作答次数**
- **SQL126** **平均活跃天数和月活人数**
- **SQL127** **月总刷题数和日均刷题数**
- 求每月的最大天数
- **SQL128** (重要)**未完成试卷数大于1的有效用户**
- [607. 销售员](https://leetcode.cn/problems/sales-person/)
- [608. 树节点](https://leetcode.cn/problems/tree-node/)
- [627. 变更性别](https://leetcode.cn/problems/swap-salary/)
- [1070. 产品销售分析 III](https://leetcode.cn/problems/product-sales-analysis-iii/)
- [1179. 重新格式化部门表](https://leetcode.cn/problems/reformat-department-table/)
- **SQL211** **获取每个部门中薪水最高的员工相关信息**
- **SQL232** **创建一个actor表,包含如下列信息**
- 关于创建数据表的小提示
- 在表上创建索引
- **SQL266** **牛客每个人最近的登录日期(二)**
- **SQL267** **牛客每个人最近的登录日期(三)**
- **SQL268** **牛客每个人最近的登录日期(四)**
- **SQL269** **牛客每个人最近的登录日期(五)**
- **SQL241** **删除emp_no重复的记录,只保留最小的id对应的**
- [1393. 股票的资本损益](https://leetcode.cn/problems/capital-gainloss/)
- 行转列 union all [1795. 每个产品在不同商店的价格](https://leetcode.cn/problems/rearrange-products-table/)
- code
- [1965. 丢失信息的雇员](https://leetcode.cn/problems/employees-with-missing-information/)
- 正则表达式
- [3465. 查找具有有效序列号的产品](https://leetcode.cn/problems/find-products-with-valid-serial-numbers/)
- 三、数据库课程笔记
- 1、 王珊老师数据库
- 是3NF不是BCNF 的举例
- 求最小函数依赖集
- (1)
- (2)
- 求候选码的方法
- (1)L R LR N
- (2)画有向图
- 分解到3NF
- 例题:
- 分解到BCNF
一、SQL语法、日期函数等
1、语法
https://leetcode.cn/studyplan/sql-free-50/
# Write your MySQL query statement below
select name
from Customer
where referee_id <> '2' or referee_id is null
- 不等于 <>
- null 使用is 不使用=
select distinct author_id as id
from Views
where author_id =viewer_id
order by id
- 消除取值重复的行 distinct
- 统计varchar 类型长度 length(content)>15
1.1. join
(1)left join (当某个表有多余的值需要使用,需要使用外连接,不忽略悬浮元组)
展示每位用户的 唯一标识码(unique ID );如果某位员工没有唯一标识码,使用 null 填充即可。

左连接,保留左侧表的全部行
# Write your MySQL query statement below
select Employees.name,EmployeeUNI.unique_id
from Employees left join EmployeeUNI
on Employees.id = EmployeeUNI.id
(2)显式内连接join ,隐式内连接where p.product_id = s.product_id;
隐式内连接
是一种比较早期的写法。(书上就有 可以使用)


select product_name, year, price
from Product p, Sales s
where p.product_id = s.product_id;
等价的显式内连接写法
select product_name, year, price
from Product p
join Sales s on p.product_id = s.product_id;
left join
当筛选条件在外连接中的"大表(含有悬浮元祖)"的时候,放在where中
https://www.nowcoder.com/practice/53235096538a456b9220fce120c062b3?tpId=199&tags=&title=&difficulty=0&judgeStatus=0&rp=0&sourceUrl=%2Fexam%2Foj%3FquestionJobId%3D10%26subTabName%3Donline_coding_page



注意:left join 的其他作用

1.2 as
AS 关键字主要用于为表或者列指定别名,
对于表别名而言,AS 关键字是可选的,
不过对于列别名,虽然大多数数据库系统里 AS 也是可选的,但为了增强代码的可读性,建议在指定列别名时使用 AS 关键字。
1.3 using
在 SQL 中,USING 是用于简化 JOIN 操作的关键字。当两个表在连接时使用相同名称的列作为连接条件时,可以使用 USING 来替代 ON,从而使代码更简洁。
1. USING 的语法(注意使用括号)
SELECT columns
FROM table1
JOIN table2
USING (column_name);
2. USING 的作用
USING会自动根据指定的列名在两个表中查找匹配的列,并将其作为连接条件。- 使用
USING时,查询结果中只会保留一个连接列(而不是像ON那样保留两个表的列)。
1.4 where VS HAVING

注意,WHERE 子句中是不能用聚集函数作为条件表达式的。 聚集函数只能用于SELECT 子句和 GROUP BY 中的 HAVING 子句。
HAVINGvsWHERE:WHERE用于过滤行,HAVING用于在分组操作之后,对分组进行过滤,选出合适的组,- 在这个查询中,
COUNT是聚合函数,必须与HAVING一起使用。
举例:


1.5 CASE WHEN
输入:
Triangle 表:
+----+----+----+
| x | y | z |
+----+----+----+
| 13 | 15 | 30 |
| 10 | 20 | 15 |
+----+----+----+
输出:
+----+----+----+----------+
| x | y | z | triangle |
+----+----+----+----------+
| 13 | 15 | 30 | No |
| 10 | 20 | 15 | Yes |
+----+----+----+----------+
对每三个线段报告它们是否可以形成一个三角形。
SELECT
x,
y,
z,
CASE
WHEN x + y > z AND x + z > y AND y + z > x THEN 'Yes'
ELSE 'No'
END AS 'triangle'
FROM
triangle
;
1.6 if else
IF(condition, value_if_true, value_if_false)
# Write your MySQL query statement below
# 使用IF函数嵌套and条件,判断任意两边之和大于第三边:IF( expr1 AND expr2 AND expr3, trueResult, falseResult)
SELECT
x,
y,
z,
IF
( x + y > z AND x + z > y AND y + z > x, 'Yes', 'No' ) AS triangle
FROM
Triangle;
1.7 IFNULL
IFNULL(expression, replacement_value)---如果expression为null 就是replacement_value
expression:需要进行检查的表达式或列名。该表达式可能返回NULL值。replacement_value:当expression的结果为NULL时,要替换成的值。这个值的数据类型应该和expression结果的数据类型兼容。
1.8 OVER 窗口函数
| person_name | weight | turn |
|---|---|---|
| Alice | 50 | 1 |
| Bob | 60 | 2 |
| Charlie | 70 | 3 |
在计算 SUM(weight) OVER(ORDER BY turn) 时,会按照 turn 从小到大的顺序依次计算累计重量。
- 对于
turn为 1 的行,累计重量就是该行的weight(50); - 对于
turn为 2 的行,累计重量是前两行weight的和(50 + 60 = 110); - 对于
turn为 3 的行,累计重量是前三行weight的和(50 + 60 + 70 = 180)。
1.9 limit 常常与order by一起使用
SELECT column1, column2, ...
FROM table_name
WHERE condition
LIMIT number;
其中 number 表示要返回的行数,当 number 为 1 时,即 LIMIT 1,表示只返回一行记录。
LIMIT 1 常用于以下几种场景:
- 获取单条记录:当你只需要查询结果中的一条记录时,例如获取表中的第一条记录(需要使用
ORDER BY子句)、最新的一条记录(照某个表示时间的列(如create_time、update_time等)降序排列后的第一条记录)或者满足特定条件的某一条记录
1.10 CONCAT
CONCAT 函数用于将多个字符串连接成一个字符串。
在大多数数据库(如 MySQL、PostgreSQL 等)中,CONCAT 函数的基本语法如下:
CONCAT(string1, string2, ..., string_n);
其中 string1 到 string_n 是要连接的字符串,可以是列名、常量字符串或其他表达式。
使用示例
假设我们有一个 employees 表,包含 first_name 和 last_name 两列,我们可以使用 CONCAT 函数将员工的名字和姓氏连接起来:
-- 创建示例表
CREATE TABLE employees (
first_name VARCHAR(50),
last_name VARCHAR(50)
);
-- 插入示例数据
INSERT INTO employees (first_name, last_name) VALUES ('John', 'Doe');
INSERT INTO employees (first_name, last_name) VALUES ('Jane', 'Smith');
-- 使用 CONCAT 函数连接名字和姓氏
SELECT CONCAT(first_name, ' ', last_name) AS full_name
FROM employees;
上述代码中,CONCAT 函数将 first_name、一个空格和 last_name 连接成一个完整的姓名。
1.11 LIKE 操作符
功能
LIKE 操作符用于在 WHERE 子句中搜索列中的指定模式。它通常与通配符一起使用,以实现模糊匹配。
语法
LIKE 操作符的基本语法如下:
column_name LIKE pattern;
其中 column_name 是要搜索的列名,pattern 是要匹配的模式。常用的通配符有:
%:匹配任意数量(包括零个)的任意字符。_:匹配单个任意字符。
使用示例 1527. 患某种疾病的患者
SELECT patient_id, patient_name, conditions
FROM Patients
WHERE conditions LIKE 'DIAB1%' OR conditions LIKE '% DIAB1%';
1.12 RECURSIVE
RECURSIVE是用于处理递归查询的关键字,从 MySQL 8.0.1 递归查询功能被引入
WITH RECURSIVE cte_name AS (
-- 初始查询部分,定义递归的基础条件
initial_query
UNION [ALL]
-- 递归查询部分,定义递归的规则
recursive_query
)
SELECT * FROM cte_name;
举例:生成 2017 年所有日期,并统计每周几(星期一到星期日)出现的次数
WITH RECURSIVE dates_2017 AS (
SELECT '2017-01-01' AS date
UNION ALL
SELECT DATE_ADD(date, INTERVAL 1 DAY)
FROM dates_2017
WHERE date < '2017-12-31'
)
SELECT
DAYNAME(date) AS weekday,
COUNT(*) AS count
FROM dates_2017
GROUP BY weekday
ORDER BY count DESC;
关键部分解析
1. WITH RECURSIVE 递归 CTE
-
WITH RECURSIVE:定义一个递归的公共表表达式(CTE),用于生成 2017 年所有日期。 -
初始查询:
SELECT '2017-01-01' AS date生成初始日期
2017-01-01。 -
递归查询:
SELECT DATE_ADD(date, INTERVAL 1 DAY) FROM dates_2017 WHERE date < '2017-12-31'- 每次递归调用,将当前日期加 1 天(
DATE_ADD(date, INTERVAL 1 DAY))。 - 递归终止条件是日期达到
2017-12-31(WHERE date < '2017-12-31')。
- 每次递归调用,将当前日期加 1 天(
2. SELECT 主查询
DAYNAME(date):获取日期的星期几名称(如Monday、Tuesday等)。COUNT(*):统计每个星期几出现的次数。GROUP BY weekday:按星期几分组。ORDER BY count DESC:按次数从高到低排序。
执行过程
1. 递归生成日期
递归 CTE 会生成 2017 年所有日期,结果如下:
| date |
|---|
| 2017-01-01 |
| 2017-01-02 |
| 2017-01-03 |
| … |
| 2017-12-31 |
2. 统计星期几
对生成的日期表,使用 DAYNAME(date) 获取星期几,并统计次数:
| weekday | count |
|---|---|
| Monday | 52 |
| Tuesday | 52 |
| Wednesday | 52 |
| Thursday | 52 |
| Friday | 52 |
| Saturday | 52 |
| Sunday | 52 |
3. 排序
按 count 从高到低排序(本例中所有星期几的次数相同,排序结果不变)。
示例结果
| weekday | count |
|---|---|
| Monday | 52 |
| Tuesday | 52 |
| Wednesday | 52 |
| Thursday | 52 |
| Friday | 52 |
| Saturday | 52 |
| Sunday | 52 |
注意事项
-
递归深度:
- 递归 CTE 的默认深度限制可能较低(如 MySQL 默认 1000 次递归)。
- 如果递归次数超过限制,需要调整数据库配置:
SET SESSION cte_max_recursion_depth = 10000;
-
日期范围:
- 确保初始日期和终止日期正确,否则可能生成错误日期范围。
-
星期几名称:
DAYNAME(date)返回的星期几名称是英文(如Monday),如需中文需额外处理。
-
性能优化:
- 递归 CTE 适用于小规模日期生成(如一年)。
- 如果需要生成更长时间的日期,建议使用预先生成的日期表。
总结
- 该查询通过递归 CTE 生成 2017 年所有日期,并统计每周几出现的次数。
- 递归 CTE 是生成序列数据(如日期、数字)的强大工具。
- 结果按次数排序,适合分析日期分布规律。
1.13 ORDER BY
ORDER BY HIREDATE, EMPNO 是按照什么排序的
排序逻辑总结
- 首先按
HIREDATE排序,确保员工按入职日期从早到晚排列。 - 如果多个员工的
HIREDATE相同,则按EMPNO排序,确保顺序稳定且唯一。
1.14 LEAD
在MySQL中,LEAD是一个窗口函数,用于在结果集中访问当前行之后的行数据,以下是其详细介绍:
语法
LEAD(expr[, offset[, default]]) OVER (PARTITION BY partition_expression ORDER BY order_expression)
示例
假设有一个sales表,包含year、product和sales_amount列,记录了不同年份各产品的销售金额,以下是一些使用LEAD函数的示例:
- 获取下一年的销售金额
SELECT
year,
product,
sales_amount,
LEAD(sales_amount) OVER (PARTITION BY product ORDER BY year) AS next_year_sales
FROM
sales;
在这个查询中,LEAD(sales_amount)用于获取每个产品下一年的销售金额。通过PARTITION BY product按照产品进行分区,确保在每个产品内进行偏移操作。ORDER BY year按照年份对每个分区内的数据进行排序,以便正确获取下一年的数据。
- 获取间隔两行后的销售金额
SELECT
year,
product,
sales_amount,
LEAD(sales_amount, 2) OVER (PARTITION BY product ORDER BY year) AS next_two_years_sales
FROM
sales;
这里使用LEAD(sales_amount, 2)获取每个产品间隔两行后的销售金额,即间隔两年后的销售数据。
- 指定默认值
SELECT
year,
product,
sales_amount,
LEAD(sales_amount, 2, 0) OVER (PARTITION BY product ORDER BY year) AS next_two_years_sales
FROM
sales;
当偏移超出结果集范围时,将返回指定的默认值0,而不是NULL。
通过使用LEAD函数,可以方便地在查询结果中获取当前行之后的数据,进行数据比较、分析等操作,为数据分析和处理提供了很大的便利。
1.15 DENSE_RANK RANK ROW_NUMBER
三种函数的区别总结
| 函数 | 相同值的处理 | 是否跳过后续序号 | 示例结果(输入:4, 4, 3.85, 3.65, 3.65, 3.5) |
|---|---|---|---|
ROW_NUMBER() | 分配不同的序号 | 否 | 1, 2, 3, 4, 5, 6 |
RANK() | 分配相同的排名 | 是 | 1, 1, 3, 4, 4, 6 |
DENSE_RANK() | 分配相同的排名 | 否 | 1, 1, 2, 3, 3, 4 |
如何选择?
- 如果需要为每一行分配唯一的序号,使用
ROW_NUMBER()。 - 如果需要为相同的值分配相同的排名,并且**允许跳过后续序号,**使用
RANK()。 - 如果需要为相同的值分配相同的排名,并且不允许跳过后续序号,使用
DENSE_RANK()。
# Write your MySQL query statement below
# https://leetcode.cn/problems/rank-scores/
select score,
DENSE_RANK() OVER (ORDER BY score desc) AS 'rank'
from Scores
1.16 尽量使用union all而 不使用union
尽量使用union all而 不使用union,
union 相当于在union all基础上distinct(含有排序)
1.17 not in 遇到null的问题,使用exists改写
EXISTS 并不一定需要关联嵌套查询。EXISTS 的核心作用是检查子查询是否返回至少一行结果,而子查询可以是关联的(Correlated Subquery)或非关联的(Non-Correlated Subquery)

2.算术函数
(1)count (注意加不加group by )
https://leetcode.cn/problems/customer-who-visited-but-did-not-make-any-transactions/?envType=study-plan-v2&envId=sql-free-50

这里 customer_id 只显示筛选结果集中的某一个 customer_id 值(通常是第一条记录的 customer_id),而 count_no_trans 是所有满足条件的记录总数,无法体现每个顾客的具体情况。
综上所述,GROUP BY customer_id 对于实现按顾客统计未交易访问次数的功能是必不可少的。
count sum
-
概念:
COUNT用于统计行数,SUM用于计算数值列的总和-
COUNT:
COUNT函数主要用于统计满足特定条件的行数。 -
SUM:
SUM函数的作用是对某列中的数值进行求和操作,它只适用于数值类型的列。
-
-
举例:
-
**count(age > 20 or null)这个语句,里面or null必须加,否则就等价于count(*)了,要么就是写作sum(age > 20)**也可以。
-
我猜测是因为**age > 20返回的是0或者1,而count对于不管是0还是1,都是会计数一次的,只有Null不会被计数。**sum对于1会计数,对于0,加上0 等于没加。所以这个age > 20 or null表达的是不大于20就转换为null,这样就不会被count计数
-
-
COUNT:
COUNT(*)会统计所有行,包含空值;而COUNT(column_name)只统计该列中非空值的数量。 -
SUM:
SUM函数只能用于数值类型的列,如果列中包含非数值类型的值,这些值会被忽略。
# Write your MySQL query statement below
SELECT p.product_id,p.product_name
FROM Product p,Sales s
WHERE p.product_id = s.product_id
GROUP BY product_id
HAVING COUNT(sale_date BETWEEN '2019-01-01' AND '2019-03-31' OR null) =COUNT(*)
# Write your MySQL query statement below
SELECT p.product_id,p.product_name
FROM Product p,Sales s
WHERE p.product_id = s.product_id
GROUP BY product_id
HAVING SUM(sale_date BETWEEN '2019-01-01' AND '2019-03-31') =COUNT(*)
上述代码等价
-
注意
-
count(某一列)返回指定列中,不为NULL的数量。不会计算NULL值。
-
count(*)count(0)count(1)返回表格的数据量。会计算NULL值。
-
(2)date MySQL 中的日期和时间函数
1. 获取当前日期和时间
CURDATE()或CURRENT_DATE(): 返回当前日期(YYYY-MM-DD)。CURTIME()或CURRENT_TIME(): 返回当前时间(HH:MM:SS)。
2. 提取日期和时间的部分
-
YEAR(date): 返回日期的年份。 -
MONTH(date): 返回日期的月份(1-12)。 -
DAY(date)或DAYOFMONTH(date): 返回日期的天数(1-31)。 -
HOUR(time): 返回时间的小时部分(0-23)。 -
MINUTE(time): 返回时间的分钟部分(0-59)。 -
SECOND(time): 返回时间的秒数部分(0-59)。 -
DAYOFWEEK(date): 返回日期对应的星期几(1=周日,2=周一,…,7=周六)。-
SELECT DAYOFWEEK('2025-02-23'); 返回结果为`7`,因为 2025 年 2 月 23 日是星期六。
-
-
DAYNAME(date)
-
SELECT DAYNAME('2025-02-23'); 返回结果为Saturday。
-
3. 日期和时间的计算
DATE_ADD(date, INTERVAL expr unit): 在日期上添加一个时间间隔。- 例如:
DATE_ADD('2023-10-01', INTERVAL 1 DAY)返回2023-10-02。
- 例如:
DATE_SUB(date, INTERVAL expr unit): 从日期中减去一个时间间隔。- 例如:
DATE_SUB('2023-10-01', INTERVAL 1 MONTH)返回2023-09-01。
- 例如:
DATEDIFF(date1, date2): 返回两个日期之间的天数差**(date1 - date2)。**TIMEDIFF(time1, time2): 返回两个时间之间的差值**(time1 - time2)。**(重要)TIMESTAMPDIFF(unit, datetime1, datetime2): 返回两个日期时间之间的差值,单位可以是YEAR,MONTH,DAY,HOUR,MINUTE,SECOND等。 datetime2 - datetime1
TIMESTAMPDIFF(unit, start_date, end_date) start_date起始日期 end_date:结束日期或时间,
- 日期和时间的格式化
DATE_FORMAT(date, format): 格式化日期为指定的字符串格式。- 例如:
DATE_FORMAT('2023-10-01', '%Y/%m/%d')返回2023/10/01。 必须是大写的Y
- 例如:
TIME_FORMAT(time, format): 格式化时间为指定的字符串格式。- 例如:
TIME_FORMAT('14:30:00', '%H:%i')返回14:30。
- 例如:
日期在…中间 使用between and
select w2.id
from Weather w1, Weather w2
where datediff(w2.recordDate, w1.recordDate) = 1 and w2.Temperature > w1.Temperature
(3)ROUND AVG
在 SQL 中,ROUND 函数用于对数值进行四舍五入。它可以对小数或整数进行四舍五入,并允许指定保留的小数位数。以下是 ROUND 函数的详细说明和用法:
函数的基本语法
ROUND(numeric_expression, [decimal_places])
- numeric_expression: 需要进行四舍五入的数值(可以是列名、常量或表达式)。
- decimal_places: (可选)指定保留的小数位数。如果省略,则默认为
0,即四舍五入到整数。
注意下面这里select要写明 t1.machine_id 不能只写 machine_id
select t1.machine_id , round(avg(t2.timestamp - t1.timestamp),3) processing_time
from Activity t1,Activity t2
where t1.machine_id =t2.machine_id
and t1.process_id =t2.process_id
and t1.activity_type ='start'
and t2.activity_type ='end'
group by machine_id
(4)mod
int计算奇数偶数
mod(id, 2) = 1
(5) 除法
mysql中 5/2得出的是2.5 不是2
求中位数出现的位置:AVG( floor((cnt+1)/2) , ceil((cnt+1)/2) )
5+1 /2=3 floor 3 ceil 3
4+1 /2 =2.5 floor 2 ceil 3
3.字符串
SUBSTRING
- 在 MySQL 中,
SUBSTRING的起始位置是从 1 开始,而不是 0。
SUBSTRING_INDEX

https://www.nowcoder.com/practice/f04189f92f8d4f6fa0f383d413af7cb8?tpId=199&tqId=1975679&sourceUrl=%2Fexam%2Foj%3FquestionJobId%3D10%26subTabName%3Donline_coding_page
SELECT SUBSTRING_INDEX(profile,",",-1) as gender,
COUNT(*) as number
FROM user_submit
GROUP BY gender;
4、表操作
4.1 insert into ,replace into
自增id可以忽略,他会自动加https://www.nowcoder.com/practice/978bcee6530a430fb0be716423d84082?tpId=240&tqId=2223554&sourceUrl=%2Fexam%2Foj%3Fpage%3D1%26tab%3DSQL%25E7%25AF%2587%26topicId%3D298 不管该ID试卷是否存在,都要插入成功,请尝试插入它。

insert into examination_info(exam_id,tag,difficulty,duration,release_time)
values (9003,'SQL','hard',90,'2021-01-01 00:00:00');
replace into examination_info(exam_id,tag,difficulty,duration,release_time)
values (9003,'SQL','hard',90,'2021-01-01 00:00:00');
replace into 跟 insert into功能类似,不同点在于:replace into 首先尝试插入数据到表中,
- 如果发现表中已经有此行数据(根据主键或者唯一索引判断)则先删除此行数据,然后插入新的数据;
- 否则,直接插入新数据。
要注意的是:插入数据的表必须有主键或者是唯一索引!否则的话,replace into 会直接插入数据,这将导致表中出现重复的数据。
4.2 update
请把examination_info表中tag为PYTHON的tag字段全部修改为Python。

update examination_info
set tag='Python'
where tag='PYTHON'
- 修改多个元素 使用逗号
- ]

4.3 delete 删除表中的行(的数据)
delete from
exam_record
where
TIMESTAMPDIFF (MINUTE, start_time, submit_time) < 5
and score < 60; --注意加分号
https://www.nowcoder.com/practice/964c9f7fffbb4ab18b507cfed4111b4a?tpId=240&tqId=2223554&sourceUrl=%2Fexam%2Foj%3Fpage%3D1%26tab%3DSQL%25E7%25AF%2587%26topicId%3D298
delete from
exam_record
where
submit_time is null
or timestampdiff (minute, start_time, submit_time) < 5
order by
start_time asc
limit
3
4.4 truncate table 删除表中数据
请删除nowcoder_exam_record表中所有记录,并重置自增主键。
delete from nowcoder_exam_record;
alter table nowcoder_exam_record drop id;
等价于
truncate table nowcoder_exam_record
- 高效性
TRUNCATE直接释放数据页,而不是逐行删除,因此速度更快,尤其是在大表上。
- 自动重置自增列
- 如果表有自增列(如
AUTO_INCREMENT),TRUNCATE会将其重置为初始值(通常是 1)。
- 如果表有自增列(如
- 不可回滚
TRUNCATE是一个 DDL(数据定义语言)操作,执行后无法通过ROLLBACK回滚。如果需要回滚,应使用DELETE。
- 不触发触发器
TRUNCATE不会触发与表相关的DELETE触发器。
- 需要权限
- 执行
TRUNCATE需要对该表有DROP权限。
- 执行
4.5 create table
create table if not exists
user_info_vip (
id int (11) not null primary key auto_increment comment '自增ID',
uid int (11) not null unique comment '用户ID',
nick_name varchar(64) comment '昵称',
achievement int (11) default 0 comment '成就值',
level int(11) comment '用户等级',
job varchar(32) comment '职业方向',
register_time datetime default CURRENT_TIMESTAMP comment '注册时间' #这里没有逗号
) default charset utf8;

4.6 alter table
alter table user_info add school varchar(15) after level;
# 增加列在某列之后
# alter table 增加的表格 add 增加列的名称 数据类型 位置(after level 在level 之后)
alter table user_info change job profession varchar(10);
# 更换列的名称及数据类型
# alter table user_info change 原列名 修改列名 修改数据类型
alter table user_info modify achievement int(11) default 0;
# 更改数据类型
# alter table 表名 modify 修改列名称 数据类型 默认值等
不可缺失 https://www.nowcoder.com/practice/d08209df6f464cebafda5dfd5de03fce?tpId=240&tqId=2223567&sourceUrl=%2Fexam%2Foj%3Fpage%3D1%26tab%3DSQL%25E7%25AF%2587%26topicId%3D240

4.7 DROP table
-- 删除流水账单表(先删子表)
DROP TABLE IF EXISTS bill;
-- 删除商品表(外键关联已被删除)
DROP TABLE IF EXISTS commodity;
-- 删除销售员表(外键关联已被删除)
DROP TABLE IF EXISTS saler;
-- 删除店铺表(最后删父表)
DROP TABLE IF EXISTS store;
DROP TABLE store;
DROP TABLE IF EXISTS exam_record_2011, exam_record_2012, exam_record_2013, exam_record_2014;
4.8 创建索引
create [unique|fulltext] index 索引名 on 表名 (列名);
alter table examination_info add [索引类型] index 索引名(列名);
#举例
create index idx_duration on examination_info (duration);
create unique index uniq_idx_exam_id on examination_info (exam_id);
create full index full_idx_tag on examination_info (tag);
空格

4.9 删除索引
drop index uniq_idx_exam_id on examination_info;
drop index full_idx_tag on examination_info;

5、细节
尽量写具体
下面的student不能写* ,可能会超时;
COUNT(*):它会统计查询结果集中的所有行数,不管列值是否为NULL,也就是统计分组后的记录总数。在本题的语境下,它会统计每个课程对应的所有记录数量,不管student列的值是不是null。

二、leetcode 、牛客题目
1280. 学生们参加各科测试的次数
https://leetcode.cn/problems/students-and-examinations/description/?envType=study-plan-v2&envId=sql-free-50
- 注意使用笛卡尔积
- 左连接**(因为有些人在左表中 不在右表中,但是最后要统计这个人的参加测试的次数为0)**

- 结果表中 主码是(student_id, subject_name ) ,group by和 order by 后面都是两个属性
# Write your MySQL query statement below
select student_id,
student_name,
subject_name,
count(Examinations.subject_name) as attended_exams
from Students cross join Subjects
left join Examinations USING (student_id, subject_name)
GROUP BY
student_id,
subject_name
ORDER BY
student_id ASC,
subject_name ASC;
1934. 确认率
https://leetcode.cn/problems/confirmation-rate/description/?envType=study-plan-v2&envId=sql-free-50
ROUND(IFNULL(AVG(c.action='confirmed'), 0), 2) AS confirmation_rate 作用是计算名为 confirmation_rate 的字段,表示确认率。
1. AVG(c.action='confirmed')
-
作用:计算
c.action='confirmed'的平均值。 -
解释:
AVG函数会对所有行的c.action='confirmed'的结果(即1或0)求平均值。- 由于
1表示确认,0表示未确认,因此AVG的结果就是确认的比例(即确认率)。
-
示例:
- 如果有 4 行数据,其中 3 行的
c.action是'confirmed',1 行不是,则:AVG(c.action='confirmed') = (1 + 1 + 1 + 0) / 4 = 0.75
- 如果有 4 行数据,其中 3 行的
2. IFNULL(AVG(c.action='confirmed'), 0)
-
作用:处理
AVG结果为NULL的情况。 -
解释:
- 如果
AVG(c.action='confirmed')的结果是NULL(例如,表中没有数据),则IFNULL会将其替换为0。 - 这样可以避免在计算确认率时出现
NULL值。
- 如果
-
示例:
- 如果表中没有数据,
AVG(c.action='confirmed')返回NULL,则:IFNULL(NULL, 0) = 0
- 如果表中没有数据,
- 判断
c.action是否等于'confirmed',返回1或0。 - 对所有行的结果求平均值,得到确认率。
- 如果确认率为
NULL,则替换为0。 - 将确认率四舍五入到小数点后两位。
- 将结果命名为
confirmation_rate。
SELECT
s.user_id,
ROUND(IFNULL(AVG(c.action='confirmed'), 0), 2) AS confirmation_rate
FROM
Signups AS s
LEFT JOIN
Confirmations AS c using (user_id)
GROUP BY
user_id
1251. 平均售价


- 只有ON的情况,会筛选出两条记录

- (1) 再WHERE就没有记录了

--- 错误
SELECT p.product_id,
ROUND(IFNULL(SUM(p.price * u.units) / SUM(u.units), 0), 2) AS average_price
FROM Prices as p
LEFT JOIN UnitsSold as u
ON p.product_id = u.product_id
WHERE u.purchase_date BETWEEN p.start_date AND p.end_date
GROUP BY p.product_id
- (2) AND会得出两条记录

--- 正确
SELECT p.product_id,
ROUND(IFNULL(SUM(p.price * u.units) / SUM(u.units), 0), 2) AS average_price
FROM Prices as p
LEFT JOIN UnitsSold as u
ON p.product_id = u.product_id
AND u.purchase_date BETWEEN p.start_date AND p.end_date
GROUP BY p.product_id
- 结果集差异:第一个查询可能会排除
Prices表中没有在UnitsSold表找到匹配且满足日期范围条件的记录,结果集可能会比第二个查询少;第二个查询会保留Prices表的所有记录,更符合LEFT JOIN的语义。

注意:
左侧: on 先左连接(注意这里会有多的null值,左侧表右悬浮元组),之后where挑选,
右侧:on 和and作为左连接的条件
-
left join时候,
-
右 边表某属性的筛选条件建议可以在ON AND 中给出,例如本题中的u.purchase_date
-
左 边表某属性的筛选条件建议可以在 WHERE 中给出
-
1633. 各赛事的用户注册率
SELECT
contest_id,
ROUND( COUNT( r.user_id )/ ( SELECT COUNT(*) FROM Users )* 100, 2 ) AS percentage
FROM
Users u
RIGHT JOIN Register r ON u.user_id = r.user_id
GROUP BY
contest_id
ORDER BY
percentage DESC,
contest_id ASC
1211. 查询结果的质量和占比
AVG 和 SUM/COUNT
# Write your MySQL query statement below
SELECT query_name ,
ROUND(AVG(rating/position),2) as quality ,
ROUND(SUM(rating<3)/COUNT(rating)*100,2) as poor_query_percentage
FROM Queries
GROUP BY query_name
# Write your MySQL query statement below
SELECT query_name ,
ROUND(SUM(rating/position)/COUNT(rating),2) as quality ,
ROUND(SUM(rating<3)/COUNT(rating)*100,2) as poor_query_percentage
FROM Queries
GROUP BY query_name
1193. 每月交易 I
错误:

- 在 MySQL 中,
SUBSTRING的起始位置是从 1 开始,而不是 0。 SUM函数不能直接嵌套子查询。
SELECT
SUBSTRING(trans_date, 1, 7) AS month, -- 截取年月
country, -- 国家
COUNT(*) AS trans_count, -- 总交易数
SUM(state = 'approved') AS approved_count, -- 已批准交易数
SUM(amount) AS trans_total_amount, -- 总交易金额
SUM(IF(state = 'approved', amount, 0)) AS approved_total_amount -- 已批准交易金额
FROM
Transactions
GROUP BY
month, country; -- 按年月和国家分组
1174. 即时食物配送 II
-
找出每个用户的首次订单:使用
MIN()函数结合GROUP BY子句找出每个用户的最早订单日期。(customer_id, order_date) IN ( -- 子查询找出每个用户的首次订单 SELECT customer_id, MIN(order_date) FROM Delivery GROUP BY customer_id ); -
筛选出首次订单中的即时订单:根据首次订单日期筛选出那些期望配送日期和订单日期相同的订单。
ROUND(SUM(IF(order_date = customer_pref_delivery_date,1,0)) -
计算比例:用即时订单的数量除以首次订单的总数量,得到比例。
ROUND(SUM(IF(order_date = customer_pref_delivery_date,1,0)) / COUNT(*) * 100, 2)
这里的COUNT(*) 是WHERE筛选之后的“每个用户的首次订单”的总和
-- 计算即时订单在所有用户首次订单中的比例
SELECT
-- 保留两位小数
ROUND(SUM(IF(order_date = customer_pref_delivery_date,1,0)) / COUNT(*) * 100, 2) AS immediate_percentage
FROM
Delivery
WHERE
(customer_id, order_date) IN (
-- 子查询找出每个用户的首次订单
SELECT
customer_id, MIN(order_date)
FROM
Delivery
GROUP BY
customer_id
);
1141. 查询近30天活跃用户数
# 正确代码
SELECT
activity_date AS day,
COUNT(DISTINCT user_id) AS active_users
FROM
Activity
WHERE
activity_date BETWEEN '2019-06-28' AND '2019-07-27'
GROUP BY
activity_date;
# 错误代码
# Write your MySQL query statement below
SELECT activity_date BETWEEN 2019-06-27 AND 2019-07-27 as day, COUNT(DISTINCT user_id) as active_users
FROM Activity
GROUP BY activity_date
- 日期需要用单引号括起来,像
'2019-06-27'和'2019-07-27'这种形式 BETWEEN通常要和WHERE子句一起使用,以此筛选出符合日期范围的记录。- 直接把
BETWEEN放在SELECT语句中,这不符合 SQL 语法规则。
619. 只出现一次的最大数字
- 注意子查询必须有别名
- MAX(num) 如果num为null 会自动返回null ,不需要额外判断
MAX函数用于返回一组值中的最大值
# Write your MySQL query statement below
SELECT MAX(num) as num
FROM (
SELECT num
FROM MyNumbers
GROUP BY num
HAVING COUNT(*) =1
) as p
1045. 买下所有产品的客户
- 不用连接
- 查询只与第二个表的product_key 的数量有关系
- 对每个分组进行筛选,选出哪个组的COUNT 满足组的“COUNT()=COUNT()”的组
# Write your MySQL query statement below
SELECT c.customer_id
FROM Customer c
GROUP BY c.customer_id
HAVING COUNT(DISTINCT product_key )=(SELECT COUNT(DISTINCT product_key) product_key FROM Product )
1731. 每位经理的下属员工数量
- ROUND(number, decimal_places) 当不加第二个参数的时候,第二个参数默认为0,相当于取整(向下)
- 注意每个列名字是e1 还是 e2
# Write your MySQL query statement below
SELECT
e2.employee_id,
e2.NAME,
COUNT( e1.employee_id ) AS reports_count,
ROUND( AVG( e1.age ) ) AS average_age
FROM
Employees e1
join Employees e2 on e1.reports_to = e2.employee_id
GROUP BY
e2.employee_id
ORDER BY
e2.employee_id
610. 判断三角形
case when
CASE expression
WHEN value1 THEN result1
WHEN value2 THEN result2
...
ELSE default_result
END
SELECT
x,
y,
z,
CASE
WHEN x + y > z AND x + z > y AND y + z > x THEN 'Yes'
ELSE 'No'
END AS 'triangle'
FROM
triangle
;
if else
IF(condition, value_if_true, value_if_false)
# Write your MySQL query statement below
# 使用IF函数嵌套and条件,判断任意两边之和大于第三边:IF( expr1 AND expr2 AND expr3, trueResult, falseResult)
SELECT
x,
y,
z,
IF
( x + y > z AND x + z > y AND y + z > x, 'Yes', 'No' ) AS triangle
FROM
Triangle;
180. 连续出现的数字
三表连接 自身连接 连续出现
# Write your MySQL query statement below
SELECT DISTINCT l1.num as ConsecutiveNums
FROM Logs l1
join Logs l2 on l1.num=l2.num and l1.id=l2.id-1
join Logs l3 on l2.num=l3.num and l2.id=l3.id-1
1164. (重要)指定日期的产品价格
https://leetcode.cn/problems/product-price-at-a-given-date/solutions/178481/zhi-ding-ri-qi-de-chan-pin-jie-ge-by-leetcode-solu/?envType=study-plan-v2&envId=sql-free-50
- 找出所有的产品:
- 找到
2019-08-16前所有有改动的产品的最新价格。 - 上面两步已经找到了所有的产品和已经修改过价格的产品。使用
left join得到所有产品的最新价格,如果没有设置为10。
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 -- 这里GROUP必须在WHERE之后
)
) as p2 -- 在 2019-08-16 之前有过修改的产品和最新的价格
on p1.product_id = p2.product_id
取出某个表的最后一行的元素 ?先找最后一行对应的某个属性的值 ,然后按照这个属性连接
1204. 最后一个能进入巴士的人
# Write your MySQL query statement below
select person_name
from(
select person_name,sum(weight) over(order by turn) as totalweight
from Queue
order by turn
) as t1
where totalweight<=1000
order by totalweight desc
limit 1 -- 返回第一行
-
在这段 SQL 代码中,
ORDER BY turn出现了两次,但它们的作用和含义是不同的, -
SUM(weight) OVER(ORDER BY turn) AS total_weight中的ORDER BY turn- 这里的
ORDER BY turn是窗口函数SUM()的窗口规范的一部分,它用于定义窗口函数的计算顺序。窗口函数SUM(weight) OVER(ORDER BY turn)会根据turn列的值对结果集进行排序,然后逐行计算weight列的累计总和。
- 这里的
-
ORDER BY turn在子查询末尾- 普通的
ORDER BY子句,用于对整个子查询的结果集进行排序。 - 它的作用是确保子查询返回的结果按照
turn列的值从小到大排序。
- 普通的
1907. 按分类统计薪水
错误解法
SELECT
CASE
WHEN income< 20000 THEN 'Low Salary'
WHEN income between 20000 and 50000 THEN 'Average Salary'
ELSE 'High Salary'
END AS 'category' ,
count(account_id ) as accounts_count
FROM
Accounts
group by category

问题在于:如果某个类别在表中没有对应的记录,GROUP BY会忽略该类别,导致结果中缺少该行。
解决方案需要确保**:即使某个类别没有数据,也会显示该类别并计数为零**。
正确code
实现方法:可以使用一个包含所有三个类别的临时表或CTE,然后左连接原表的数据,这样即使没有对应的记录,也会显示类别并填充零。
- 建立一张含有所有类的表
- 建立一张统计“accounts_count不为0的类” 的表
- 左连接

WITH Categories AS ( -- 表名
SELECT 'Low Salary' AS category
UNION ALL
SELECT 'Average Salary'
UNION ALL
SELECT 'High Salary'
)
SELECT
c.category, -- 全部的类别
IFNULL(a.accounts_count, 0) AS accounts_count -- 哪些类别在左连接之后accounts_count为null 就赋值为0
FROM Categories c
LEFT JOIN (
SELECT
CASE
WHEN income < 20000 THEN 'Low Salary'
WHEN income BETWEEN 20000 AND 50000 THEN 'Average Salary'
ELSE 'High Salary'
END AS category,
COUNT(account_id) AS accounts_count
FROM Accounts
GROUP BY category
) a ON c.category = a.category;
LEFT JOIN后面的一个子查询就是"错误解法"中的代码
leetcode官方解法
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
或者
select 'Low Salary' as category ,sum(income < 20000) as accounts_count
from Accounts
union
select 'Average Salary' as category ,sum(income between 20000 and 50000) as accounts_count
from Accounts
union
select 'High Salary' as category ,sum(income > 50000) as accounts_count
from Accounts
1978. 上级经理已离职的公司员工

# 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 ememployee_id
- 这里的employee_id就是先选出行,再选出这行当中的一个属性employee_id
626. 换座位
select (
case
when id%2=1 and id = (select count(distinct id) from Seat) then id
when id%2=0 then id-1
else id+1
end
) as id ,
student
from Seat
order by id
修改某个属性的值 = 实际上是在原有的基础上错位重新赋值一下 使用case when
1341. 电影评分
- 注意使用UNION all 合并的时候 用()括起来
- UNION 的每个部分必须是一个完整的 SELECT 语句,且不能在 UNION 的某个部分中使用 ORDER BY 和 LIMIT,除非将整个 UNION 查询包装在子查询中。
-- 第一个查询:找出评论电影数量最多的用户
(
select name as results
from
(-- 所有用户评论电影的数量
select user_id ,count(movie_id) as cnt
from MovieRating
group by user_id
) as t1
join Users u
on u.user_id=t1.user_id
order by t1.cnt desc,name -- 以 所有用户评论电影的数量 排序
limit 1 -- 取第一行的数据
)
UNION all
-- 第二个查询:找出 2020 年 2 月平均评分最高的电影
(
select title as results
from
( -- 所有电影的平均得分
select movie_id, AVG(rating) as ratingAvg
from MovieRating
where created_at between '2020-02-01' and '2020-02-29'
group by movie_id
) as t1
join Movies m
on m.movie_id = t1.movie_id
order by t1.ratingAvg desc,title -- 按照 所有电影的平均得分 排序
limit 1
);
不要使用union

1667. 修复表中的名字
SELECT user_id, CONCAT(UPPER(SUBSTRING(name, 1, 1)), LOWER(SUBSTRING(name, 2))) AS name
FROM Users
ORDER BY user_id;
这段 SQL 查询的作用是从 Users 表中提取 user_id 和格式化后的 name,并按 user_id 排序。以下是逐行解释:
1. SELECT user_id, CONCAT(UPPER(SUBSTRING(name, 1, 1)), LOWER(SUBSTRING(name, 2))) AS name
CONCAT(...) AS name: 对name列进行格式化处理,并将结果命名为name。UPPER(SUBSTRING(name, 1, 1)):SUBSTRING(name, 1, 1): 从name列的第一个字符开始,提取 1 个字符(即首字母)。UPPER(...): 将提取的首字母转换为大写。
LOWER(SUBSTRING(name, 2)):SUBSTRING(name, 2): 从name列的第二个字符开始,提取剩余部分。LOWER(...): 将剩余部分转换为小写。
CONCAT(...): 将大写首字母和小写剩余部分拼接起来,形成格式化后的name。
1527. 患某种疾病的患者
SELECT patient_id, patient_name, conditions
FROM Patients
WHERE conditions LIKE 'DIAB1%' OR conditions LIKE '% DIAB1%';
1321. 餐馆营业额变化增长
-
在 ORDER BY 子句中不明确指定 ASC(升序)或 DESC(降序)时,默认按照升序排列结果
-
ROWS 6 PRECEDING :于行的偏移来定义窗口范围的。它表示窗口从当前行开始,往前取 6 行数据,再加上当前行本身,一共包含 7 行数据参与窗口函数的聚合计算。(共7行)
-
RANGE INTERVAL 6 DAY PRECEDING : 是基于值的范围来定义窗口范围的,通常用于日期或时间类型的列。从当前行的 visited_date 往前推 6 天(共7天)
select distinct visited_on,
amount,
round(amount/7,2) as average_amount
from(
select visited_on,
sum(amount) over(order by visited_on range interval 6 day preceding ) as amount
from Customer
) as t
where datediff(visited_on,(select min(visited_on) from Customer))>=6
585. 2016年的投资
- 选出某列的独一无二的行。使用
GROUP BY行名 和COUNT=1( lat, lon ) IN ( SELECT lat, lon FROM Insurance GROUP BY lat, lon HAVING COUNT(*) = 1 );
- 选出某列的行,此行至少跟其他一个行相同。使用
GROUP BY行名 和COUNT > 1tiv_2015 IN ( SELECT tiv_2015 FROM Insurance GROUP BY tiv_2015 HAVING COUNT(*) > 1 )
SELECT
ROUND( SUM( tiv_2016 ), 2 ) AS tiv_2016
FROM
Insurance
WHERE-- 筛选出在 2015 年有其他投保人也有相同总投资价值的记录
tiv_2015 IN ( SELECT tiv_2015 FROM Insurance GROUP BY tiv_2015 HAVING COUNT(*) > 1 )
AND -- 筛选出所在地点是独一无二的记录
( lat, lon ) IN ( SELECT lat, lon FROM Insurance GROUP BY lat, lon HAVING COUNT(*) = 1 );
176. 第二高的薪水
SELECT DISTINCT Salary
FROM Employee
ORDER BY Salary DESC
LIMIT 1 OFFSET 1
LIMIT OFFSET在无数据时会返回空集,但题目要求无结果时必须返回null
空结果集 不是 null
SELECT (
SELECT DISTINCT Salary
FROM Employee
ORDER BY Salary DESC
LIMIT 1 OFFSET 1
) AS SecondHighestSalary;
184. 部门工资最高的员工(每个部门)
+--------------+---------+
| 列名 | 类型 |
+--------------+---------+
| id | int |
| name | varchar |
| salary | int |
| departmentId | int |
+--------------+---------+
在 SQL 中,id是此表的主键。
departmentId 是 Department 表中 id 的外键(在 Pandas 中称为 join key)。
此表的每一行都表示员工的 id、姓名和工资。它还包含他们所在部门的 id。
错误写法

# Write your MySQL query statement below
select d.name as Department ,
e.name as Employee ,
e.Salary
from Employee e
join Department d
on e.departmentId =d.id
group by e.departmentId
order by salary desc
正确写法
注意:新生成的列名Department 由于和表名相同,所以使用单引号
# Write your MySQL query statement below
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 # 找出最大薪资 和 最大薪资对应的部门id,再由此推出 e.Salary d.name
(
select departmentId, max(salary) # 可能返回两个最大的薪资 如果相同
from Employee
group by departmentId
)
185. 部门工资前三高的所有员工
-- 从 Department 表和子查询结果中选取所需列
SELECT
d.name AS 'Department',
e.name AS 'Employee',
e.Salary
FROM (
-- 子查询,对 Employee 表进行处理
SELECT
name,
salary,
departmentId,
-- 使用 DENSE_RANK() 函数对每个部门内的员工按薪资降序排名
DENSE_RANK() OVER (PARTITION BY departmentId ORDER BY salary DESC) AS ranked
FROM
Employee
) AS e
-- 通过部门 ID 关联 Employee 表和 Department 表
JOIN Department d ON e.departmentId = d.id
-- 筛选出每个部门中薪资排名前三的员工
WHERE ranked <= 3
注意下面的错误:

在 SQL 中,**WHERE 子句不能直接使用 SELECT 子句中定义的别名(如 ranked),**因为 WHERE 子句是在 SELECT 子句之前执行的。因此,ranked 这个别名在 WHERE 子句中是不可见的。
类似好题目:
https://www.nowcoder.com/practice/255aa1863fe14aa88694c09ebbc1dbca?tpId=240&tqId=2183404&sourceUrl=%2Fexam%2Foj%3FquestionJobId%3D10%26subTabName%3Donline_coding_page
每类试卷得分前3名

SELECT tag,uid,ranking
FROM (
SELECT b.tag,a.uid,
ROW_NUMBER()OVER(PARTITION BY tag ORDER BY max(a.score) DESC,min(a.score) DESC,a.uid DESC) ranking
/*先按照最高分,再按照最低分排,最后按照uid排序 */
FROM exam_record a
LEFT JOIN examination_info b ON a.exam_id=b.exam_id
GROUP BY b.tag,a.uid)t1
WHERE ranking<=3
SQL125 得分不小于平均分的最低分
错误代码

在 WHERE 子句中不能使用聚合函数 AVG,
这是因为 SQL 语句的执行顺序是先执行 FROM、JOIN、WHERE 等子句来筛选和连接数据,之后才执行聚合函数(如 AVG、SUM、MIN 等)进行分组计算。
**而 WHERE 子句在聚合操作之前执行,**所以不能直接在 WHERE 子句中使用聚合函数。
正确代码
select min(e1.score) as min_score_over_avg
from exam_record e1
join examination_info e2
on e1.exam_id=e2.exam_id and e2.tag='SQL'
where e1.score>=(
SELECT AVG(score)
from exam_record e1
join examination_info e2
on e1.exam_id=e2.exam_id and e2.tag='SQL'
)
SQL124 统计作答次数
https://www.nowcoder.com/practice/45a87639110841b6950ef6a12d20175f?tpId=240&tqId=2180960&sourceUrl=%2Fexam%2Foj%3Fpage%3D1%26tab%3DSQL%25E7%25AF%2587%26topicId%3D240
select
count(id) as total_pv,
count(submit_time) as complete_pv,
count(distinct exam_id and score is not null) as complete_exam_cnt
from exam_record
SQL126 平均活跃天数和月活人数
https://www.nowcoder.com/practice/9e2fb674b58b4f60ac765b7a37dde1b9?tpId=240&tags=&title=&difficulty=0&judgeStatus=0&rp=0&sourceUrl=%2Fexam%2Foj%3Fpage%3D1%26tab%3DSQL%25E7%25AF%2587%26topicId%3D240
202107活跃用户数
count(distinct uid)
202107活跃天数(按照用户和时间两个约束确定天数)
count(distinct uid,date_format(submit_time,'%y%m%d')
select date_format(submit_time, '%Y%m') as month,
round((count(distinct uid, date_format(submit_time, '%y%m%d'))) / count(distinct uid), 2) as avg_active_days,
count(distinct uid) as mau
from exam_record
where submit_time is not null and year(submit_time) = 2021
group by month
SQL127 月总刷题数和日均刷题数
https://www.nowcoder.com/practice/f6b4770f453d4163acc419e3d19e6746?tpId=240&tags=&title=&difficulty=0&judgeStatus=0&rp=0&sourceUrl=%2Fexam%2Foj%3Fpage%3D1%26tab%3DSQL%25E7%25AF%2587%26topicId%3D240
求每月的最大天数
先用last_day()求出当月最后一天日期,再用day()取天数
- 因为day(last_day(submit_time)运算结果还是跟submit_time同样的一串数列,只有加上avg(),min()或max()运算才变成了一个数值作为分母使用
- 否则会导致错误 SQL_ERROR_INFO: “Expression #3 of SELECT list is not in GROUP BY clause and contains nonaggregated column ‘practice_record.submit_time’ which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by”
- SQL 错误信息:“SELECT 列表中的表达式 #3 不在 GROUP BY 子句中,并且包含了非聚合列‘practice_record.submit_time’,该列在功能上并不依赖于 GROUP BY 子句中的列;这与 sql_mode=only_full_group_by 不兼容。”
select
date_format(submit_time,'%Y%m') as submit_month,
count(question_id) as month_q_cnt,
round(count(question_id)/avg(day(last_day(submit_time))),3) as avg_day_q_cnt
from practice_record
where date_format(submit_time,'%Y')=2021
group by submit_month
union all
select '2021汇总' as submit_month,
count(question_id) as month_q_cnt,
round(count(question_id)/31,3) as avg_day_q_cnt
from practice_record
where date_format(submit_time,'%Y')=2021
order by submit_month;

SQL128 (重要)未完成试卷数大于1的有效用户
- 统计为空的用户 sum(if(submit_time is null, 1, 0) 不要使用count
- 统计不为空的用户 sum(if(submit_time is null, 0, 1))
select uid,
sum(if(submit_time is null, 1, 0)) as incomplete_cnt,
sum(if(submit_time is null, 0, 1)) as complete_cnt,
group_concat(distinct CONCAT(DATE_FORMAT(start_time, '%Y-%m-%d'),':',tag) separator ';') as detail
from exam_record er join examination_info ei on er.exam_id = ei.exam_id
where YEAR(start_time) = 2021
group by uid
having incomplete_cnt>1 and incomplete_cnt<5 and complete_cnt >= 1
order by incomplete_cnt desc

或者 https://blog.nowcoder.net/n/295c2f9b67cd4bff87e636a855698a30?f=comment
SELECT uid, count(incomplete) as incomplete_cnt,
count(complete) as complete_cnt,
group_concat(distinct concat_ws(':', date(start_time), tag) SEPARATOR ';') as detail
from (
SELECT uid, tag, start_time,
if(submit_time is null, 1, null) as incomplete,
if(submit_time is null, null, 1) as complete
from exam_record
left join examination_info using(exam_id)
where year(start_time)=2021
) as exam_complete_rec
group by uid
having complete_cnt >= 1 and incomplete_cnt BETWEEN 2 and 4
order by incomplete_cnt DESC
607. 销售员
对
# Write your MySQL query statement below
select name
from SalesPerson
where sales_id not in(
select o.sales_id
from Orders o
join Company c on o.com_id =c.com_id and c.name='RED'
)
对
select name
from SalesPerson
where not exists (
-- 子查询找出与RED公司有订单的销售员的ID
select o.sales_id
from Orders o
join Company c on o.com_id = c.com_id and c.name = 'RED'
-- 关联条件,判断当前SalesPerson表中的销售员ID是否在子查询结果中
and SalesPerson.sales_id = o.sales_id
);
错 (这里要关联才对)
注意:exists筛选的是行
where not exists ( 对
where sales_id not exists( 错
# Write your MySQL query statement below
select name
from SalesPerson
where sales_id not exists(
select o.sales_id
from Orders o
join Company c on o.com_id =c.com_id and c.name='RED'
)
608. 树节点
select t1.id,'Root' as type
from Tree t1
where t1.p_id is null # t1的父节点 为 空
union
select t1.id,'Inner' as type
from Tree t1
where t1.p_id is not null and t1.id in # t1的父节点不空 且 是父节点(id在非空的p_id里)
(select p_id from Tree where p_id is not null)
union
select t1.id,'Leaf' as type
from Tree t1
where t1.p_id is not null and t1.id not in # t1的父节点不空 且 不是父节点(id不在非空的p_id里)
(select p_id from Tree where p_id is not null);
627. 变更性别
- update 没有on
- case后面加sex
# Write your MySQL query statement below
update Salary
set sex = (case sex
when 'm' then 'f'
else 'm'
end)
1070. 产品销售分析 III
# Write your MySQL query statement below
select product_id , year as first_year , quantity , price
from Sales
where (product_id,year) in (
select product_id,min(year)
from Sales
group by product_id
)

[坑] 原因是 group by 作用的只有分组列和聚合函数列,其他列不管用,返回的其他列只有对应的第一行
Sales 表:
+---------+------------+------+----------+-------+
| sale_id | product_id | year | quantity | price |
+---------+------------+------+----------+-------+
| 1 | 100 | 2008 | 10 | 5000 |
| 2 | 100 | 2009 | 12 | 5000 |
| 7 | 200 | 2011 | 15 | 9000 |
+---------+------------+------+----------+-------+
select 之后
+---------+------------+------+----------+-------+
| sale_id | product_id | year | quantity | price |
+---------+------------+------+----------+-------+
| 1 | 100 | 2008 | 10 | 5000 |
| 2 | 100 | 2008 | 12 | 5000 |
| 7 | 200 | 2011 | 15 | 9000 |
+---------+------------+------+----------+-------+
1179. 重新格式化部门表
https://leetcode.cn/problems/reformat-department-table/solutions/535179/group-byben-zhi-lun-by-loverxp-7mgy/
在 sum(case month when 'Dec' then revenue end) 这个表达式中,当 month 不等于 'Dec' 时,CASE 表达式会返回 NULL。
这是因为在 CASE 语句中,如果没有匹配到 WHEN 条件,且没有 ELSE 子句时,默认返回值就是 NULL。SUM 函数在计算时会忽略 NULL 值,不会将其纳入求和计算。
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
输出的 表格中如果没有填充的数据自动为null

SQL211 获取每个部门中薪水最高的员工相关信息
https://www.nowcoder.com/practice/4a052e3e1df5435880d4353eb18a91c6?tpId=82&tags=&title=&difficulty=0&judgeStatus=0&rp=1&sourceUrl=%2Fexam%2Foj%3Fpage%3D1%26tab%3DSQL%25E7%25AF%2587%26topicId%3D240
rank() over(partition by de.dept_no order by s.salary desc) 是在每个分组内部排序,t.rk=1每个分组内部的第一名
select t.dept_no,t.emp_no,t.salary from
(
select
de.dept_no,de.emp_no,s.salary,
rank() over(partition by de.dept_no order by s.salary desc) as rk
from (
select * from dept_emp where to_date='9999-01-01') de
join
(select * from salaries where to_date='9999-01-01') s on de.emp_no=s.emp_no
)t
where t.rk=1;
SQL232 创建一个actor表,包含如下列信息
关于创建数据表的小提示
创建数据表时,表名和字段名不需要用引号括起来。因此,下面的代码是错误的:
CREATE TABLE 'actor'(
'actor_id' smallint(5) primary key,
'first_name' varchar(45) not null,
'last_name' varchar(45) not null,
'last_update' date not null);
正确示例:
CREATE TABLE actor(
actor_id smallint(5) primary key,
first_name varchar(45) not null,
last_name varchar(45) not null,
last_update date not null);
在表上创建索引
# alter table actor add unique index uniq_idx_firstname(first_name);
# alter table actor add index idx_lastname(last_name);
或者 (注意两个索引分别创建)
CREATE UNIQUE INDEX uniq_idx_firstname on actor (first_name);
CREATE INDEX idx_lastname ON actor (last_name);
SQL266 牛客每个人最近的登录日期(二)
select u.name as u_n, c.name as c_n, t.date
from login l
join user u on l.user_id=u.id
join client c on l.client_id=c.id
join (
select user_id,max(date) as date
from login
group by user_id
) as t on l.user_id=t.user_id and l.date=t.date
order by u_n

SQL267 牛客每个人最近的登录日期(三)
select round(count(l2.user_id)/count( l1.user_id),3) as p
from (select user_id,min(date) first_date from login group by user_id) l1
left join login l2 on l1.user_id=l2.user_id and DATEDIFF(l2.date,l1.first_date)=1
SQL268 牛客每个人最近的登录日期(四)
SELECT
a.date,
count( b.user_id ) new
FROM
( SELECT DISTINCT date FROM login ) a
LEFT JOIN ( SELECT user_id, min( date ) first_date FROM login GROUP BY user_id ) b ON a.date = b.first_date
GROUP BY
a.date
ORDER BY
a.date
SQL269 牛客每个人最近的登录日期(五)
SELECT
t0.date,
ifnull( round( count( DISTINCT t2.user_id )/( count( t1.user_id )), 3 ), 0 )
FROM
( SELECT date FROM login GROUP BY date ) t0
LEFT JOIN ( SELECT user_id, min( date ) AS date FROM login GROUP BY user_id ) t1 ON t0.date = t1.date
LEFT JOIN login AS t2 ON t1.user_id = t2.user_id AND datediff( t2.date, t1.date )= 1
GROUP BY
t0.date

此处的group by可以避免最外侧使用distinct l.
SQL241 删除emp_no重复的记录,只保留最小的id对应的
DELETE FROM
titles_test
WHERE
id NOT IN (
SELECT
*
FROM
(
SELECT
MIN(id)
FROM
titles_test
GROUP BY
emp_no
) a
);
-- 把得出的表重命名那就不是原表了(机智.jpg// MySQL中不允许在子查询的同时删除表数据(不能一边查一边把查的表删了)
1393. 股票的资本损益

select
stock_name,
sum(
case
when operation = 'Buy' then -price
when operation = 'Sell' then price
end
) as capital_gain_loss
from stocks
group by stock_name
行转列 union all 1795. 每个产品在不同商店的价格
表:Products
+-------------+---------+
| Column Name | Type |
+-------------+---------+
| product_id | int |
| store1 | int |
| store2 | int |
| store3 | int |
+-------------+---------+
在 SQL 中,这张表的主键是 product_id(产品Id)。
每行存储了这一产品在不同商店 store1, store2, store3 的价格。
如果这一产品在商店里没有出售,则值将为 null。
请你重构 Products 表,查询每个产品在不同商店的价格,使得输出的格式变为(product_id, store, price) 。如果这一产品在商店里没有出售,则不输出这一行。
输出结果表中的 顺序不作要求
code
SELECT product_id, 'store1' AS store, store1 AS price
FROM Products
WHERE store1 IS NOT NULL
UNION all
SELECT product_id, 'store2' AS store, store2 AS price
FROM Products
WHERE store2 IS NOT NULL
UNION all
SELECT product_id, 'store3' AS store, store3 AS price
FROM Products
WHERE store3 IS NOT NULL
1965. 丢失信息的雇员
ORDER BY employee_id是对整个UNION操作的结果进行排序。
# Write your MySQL query statement below
# mysql不支持满外联结,所以我们考虑用union分别查询之后拼在一起
#上面这个是缺薪水的
select e.employee_id
from employees as e left outer join salaries as s
on e.employee_id = s.employee_id
where s.salary is null
union #Union可以去重
#下面这个是缺名字的
select s.employee_id
from employees as e right outer join salaries as s
on e.employee_id = s.employee_id
where e.name is null
order by employee_id;
正则表达式
3465. 查找具有有效序列号的产品
SELECT product_id, product_name, description
FROM products
WHERE description REGEXP 'SN[0-9]{4}-[0-9]{4}([^0-9]|$)'
ORDER BY product_id ASC;
^ 表示以后面的字符为开头
[] 表示括号内任意字符
- 表示连续
* 表示重复前面任意字符任意次数
\ 用来转义后面的特殊字符,以表示字符原本的样子,而不是将其作为特殊字符使用
$ 表示以前面的字符为结尾
三、数据库课程笔记
1、 王珊老师数据库
是3NF不是BCNF 的举例

求最小函数依赖集
(1)
https://www.bilibili.com/video/BV1sv411H7Xp/?spm_id_from=…search-card.all.click&vd_source=134104876aaac3662b320f1ea1286a16

https://www.bilibili.com/video/BV1pE411P7ab/?spm_id_from=…search-card.all.click&vd_source=134104876aaac3662b320f1ea1286a16
1、注意:右侧有冗余的也要删除,
比如:
-
ABD->AC,只保留ABD->C,因为ADB->A 是废话,
-
C->BE 拆分 C->B C->E ,若删除C->E 那么由C->B B->E 可以直接推出C->E
2、试着删除某个函数依赖,
比如试着删除BC->E 看是否能从其他的依赖集中推出这个关系
3、接着试着看看 BC->E 是否能分解成B->E 或者 C->E
(左边属性不为单属性的都试着拆一下)
4、好像最后还要合并试试

(2)

求候选码的方法
(1)L R LR N
求候选码的方法有很多,我用的是:
1.L类属性:如果属性A只在F中各个函数依赖的左端出现,则A一定是候选码中的属性。
2.N类属性:如果属性A在F的各个函数依赖中都不出现,则A一定是候选码中的属性。
3.R类属性:如果属性A只在F中各个函数依赖的右端出现,则A一定不是候选码中的属性。
4.LR类属性:如果属性A在F中各个函数依赖的左右端都出现,则A不确定是不是候选码中的属性。
先根据 函数依赖集F 和 全集U 把所有属性归为以上四类,然后先验证 L类和N类 中的单个属性,若单个属性无法决定R。再验证 L类和N类 中的组合属性,若还是无法决定R。再用L类或N类中的 属性LR类中的属性组合,直至能决定R为止。
https://blog.csdn.net/naozibuok/article/details/139326837



(2)画有向图

分解到3NF
将每个函数依赖写成每个R集合,之后检查结合当中是否含有候选码,如果没有,那么单独加上一个R (里面是候选码的属性)R6
- 注意:左侧相同的要合并 (A->B A->C 那么R(A,B,C))
- 注意:如果集合里面没有公刚刚求出的候选码,那么加上由候选码组成的 集合

例题:


分解到BCNF
BCNF:如果BC->D ,那么箭头左边的一定包含码
https://www.bilibili.com/video/BV1eE411a79r/?spm_id_from=…search-card.all.click&vd_source=134104876aaac3662b320f1ea1286a16

https://www.bilibili.com/video/BV1bq4y147uo/?spm_id_from=333.788.recommend_more_video.1&vd_source=134104876aaac3662b320f1ea1286a16

无损连接:子集的至少一个集合R中含有候选码

文章目录
- 一、SQL语法、日期函数等
- 1、语法
- 1.1. join
- left join
- 注意:left join 的其他作用
- 1.2 as
- 1.3 using
- 1. **`USING` 的语法**(注意使用括号)
- 2. **`USING` 的作用**
- 1.4 where VS HAVING
- 1.5 CASE WHEN
- 1.6 if else
- 1.7 IFNULL
- 1.8 OVER 窗口函数
- 1.9 limit 常常与order by一起使用
- 1.10 CONCAT
- 使用示例
- 1.11 `LIKE` 操作符
- 功能
- 语法
- 使用示例 [1527. 患某种疾病的患者](https://leetcode.cn/problems/patients-with-a-condition/)
- 1.12 RECURSIVE
- 举例:生成 **2017 年所有日期**,并统计每周几(星期一到星期日)出现的次数
- **关键部分解析**
- **1. `WITH RECURSIVE` 递归 CTE**
- **2. `SELECT` 主查询**
- **执行过程**
- **1. 递归生成日期**
- **2. 统计星期几**
- **3. 排序**
- **示例结果**
- **注意事项**
- **总结**
- 1.13 ORDER BY
- **排序逻辑总结**
- 1.14 LEAD
- 语法
- 示例
- 1.15 DENSE_RANK RANK ROW_NUMBER
- **三种函数的区别总结**
- **如何选择?**
- 1.16 尽量使用union all而 不使用union
- 1.17 not in 遇到null的问题,使用exists改写
- 2.算术函数
- (1)count (注意加不加group by )
- count sum
- (2)date MySQL 中的日期和时间函数
- 1. 获取当前日期和时间
- 2. 提取日期和时间的部分
- DAYNAME(date)
- 3. 日期和时间的计算
- (3)ROUND AVG
- (4)mod
- (5) 除法
- 3.字符串
- SUBSTRING
- SUBSTRING_INDEX
- 4、表操作
- 4.1 insert into ,replace into
- 4.2 update
- 4.3 delete 删除表中的行(的数据)
- 4.4 truncate table 删除表中数据
- 4.5 create table
- 4.6 alter table
- 4.7 DROP table
- 4.8 创建索引
- 4.9 删除索引
- 5、细节
- 尽量写具体
- 二、leetcode 、牛客题目
- [1280. 学生们参加各科测试的次数](https://leetcode.cn/problems/students-and-examinations/)
- [1934. 确认率](https://leetcode.cn/problems/confirmation-rate/)
- 1. **`AVG(c.action='confirmed')`**
- 2. **`IFNULL(AVG(c.action='confirmed'), 0)`**
- [1251. 平均售价](https://leetcode.cn/problems/average-selling-price/)
- [1633. 各赛事的用户注册率](https://leetcode.cn/problems/percentage-of-users-attended-a-contest/)
- [1211. 查询结果的质量和占比](https://leetcode.cn/problems/queries-quality-and-percentage/)
- AVG 和 SUM/COUNT
- [1193. 每月交易 I](https://leetcode.cn/problems/monthly-transactions-i/)
- [1174. 即时食物配送 II](https://leetcode.cn/problems/immediate-food-delivery-ii/)
- [1141. 查询近30天活跃用户数](https://leetcode.cn/problems/user-activity-for-the-past-30-days-i/)
- [619. 只出现一次的最大数字](https://leetcode.cn/problems/biggest-single-number/)
- [1045. 买下所有产品的客户](https://leetcode.cn/problems/customers-who-bought-all-products/)
- [1731. 每位经理的下属员工数量](https://leetcode.cn/problems/the-number-of-employees-which-report-to-each-employee/description/)
- [610. 判断三角形](https://leetcode.cn/problems/triangle-judgement/)
- case when
- if else
- [180. 连续出现的数字](https://leetcode.cn/problems/consecutive-numbers/)
- 三表连接 自身连接 连续出现
- [1164. (重要)指定日期的产品价格](https://leetcode.cn/problems/product-price-at-a-given-date/)
- [1204. 最后一个能进入巴士的人](https://leetcode.cn/problems/last-person-to-fit-in-the-bus/)
- `SUM(weight) OVER(ORDER BY turn) AS total_weight` 中的 `ORDER BY turn`
- `ORDER BY turn` 在子查询末尾
- [1907. 按分类统计薪水](https://leetcode.cn/problems/count-salary-categories/)
- 错误解法
- 正确code
- leetcode官方解法
- [1978. 上级经理已离职的公司员工](https://leetcode.cn/problems/employees-whose-manager-left-the-company/)
- [626. 换座位](https://leetcode.cn/problems/exchange-seats/)
- [1341. 电影评分](https://leetcode.cn/problems/movie-rating/)
- [1667. 修复表中的名字](https://leetcode.cn/problems/fix-names-in-a-table/)
- [1527. 患某种疾病的患者](https://leetcode.cn/problems/patients-with-a-condition/)
- [1321. 餐馆营业额变化增长](https://leetcode.cn/problems/restaurant-growth/)
- [585. 2016年的投资](https://leetcode.cn/problems/investments-in-2016/)
- [176. 第二高的薪水](https://leetcode.cn/problems/second-highest-salary/)
- [184. 部门工资最高的员工](https://leetcode.cn/problems/department-highest-salary/)(每个部门)
- 错误写法
- 正确写法
- [185. 部门工资前三高的所有员工](https://leetcode.cn/problems/department-top-three-salaries/)
- 注意下面的错误:
- **SQL125** **得分不小于平均分的最低分**
- 错误代码
- 正确代码
- **SQL124** **统计作答次数**
- **SQL126** **平均活跃天数和月活人数**
- **SQL127** **月总刷题数和日均刷题数**
- 求每月的最大天数
- **SQL128** (重要)**未完成试卷数大于1的有效用户**
- [607. 销售员](https://leetcode.cn/problems/sales-person/)
- [608. 树节点](https://leetcode.cn/problems/tree-node/)
- [627. 变更性别](https://leetcode.cn/problems/swap-salary/)
- [1070. 产品销售分析 III](https://leetcode.cn/problems/product-sales-analysis-iii/)
- [1179. 重新格式化部门表](https://leetcode.cn/problems/reformat-department-table/)
- **SQL211** **获取每个部门中薪水最高的员工相关信息**
- **SQL232** **创建一个actor表,包含如下列信息**
- 关于创建数据表的小提示
- 在表上创建索引
- **SQL266** **牛客每个人最近的登录日期(二)**
- **SQL267** **牛客每个人最近的登录日期(三)**
- **SQL268** **牛客每个人最近的登录日期(四)**
- **SQL269** **牛客每个人最近的登录日期(五)**
- **SQL241** **删除emp_no重复的记录,只保留最小的id对应的**
- [1393. 股票的资本损益](https://leetcode.cn/problems/capital-gainloss/)
- 行转列 union all [1795. 每个产品在不同商店的价格](https://leetcode.cn/problems/rearrange-products-table/)
- code
- [1965. 丢失信息的雇员](https://leetcode.cn/problems/employees-with-missing-information/)
- 正则表达式
- [3465. 查找具有有效序列号的产品](https://leetcode.cn/problems/find-products-with-valid-serial-numbers/)
- 三、数据库课程笔记
- 1、 王珊老师数据库
- 是3NF不是BCNF 的举例
- 求最小函数依赖集
- (1)
- (2)
- 求候选码的方法
- (1)L R LR N
- (2)画有向图
- 分解到3NF
- 例题:
- 分解到BCNF
