【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 子句。
HAVING
vsWHERE
: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 > 1
tiv_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