【后端SQL训练营】高频 SQL 50 题(基础版·上篇)

文章目录

查询

可回收且低脂的产品(字符串与运算符)

题目链接:1757. 可回收且低脂的产品

注意点:

  • windows系统中大小写不敏感,linux中大小写敏感
  • 一般字符串、日期使用单引号,别名使用双引号
  • 条件中可以使用的运算符

解题代码:

sql 复制代码
SELECT
    product_id
FROM
    Products
WHERE
    low_fats = 'Y' AND recyclable = 'Y'
;

寻找用户推荐人(NULL操作)

题目链接:584. 寻找用户推荐人

注意点:

用 IS NULL / IS NOT NULL 处理 NULL!!!

MySQL 引入 UNKNOWN 作为第三种逻辑值,本质是为了处理 "缺失 / 不确定的数据"(即 NULL)

在传统的二值逻辑(TRUE/FALSE)中,"未知" 状态无法被描述 ------ 比如 "查询年龄大于 30 的用户",如果某条记录的 "年龄" 是 NULL(既不是 "大于 30",也不是 "不大于 30",而是 "不知道年龄是多少"),此时用 TRUE 或 FALSE 都无法准确表达这种 "不确定",因此引入 UNKNOWN 来专门代表这种状态。

无论比较运算符是 =、>、<、!= 还是 IN,只要其中一方是 NULL,结果都是 UNKNOWN

注意:NULL = NULL 的结果不是 TRUE,而是 UNKNOWN!

解题代码:

sql 复制代码
SELECT
    name
FROM
    Customer 
WHERE
    referee_id <> 2 || referee_id IS NULL
; 

大的国家

题目链接:595. 大的国家

解题代码:

sql 复制代码
SELECT
    name,population,area
FROM
    World
WHERE
    area >= 3000000 OR population >= 25000000
;

文章浏览 I(别名)

题目链接:1148. 文章浏览 I

涉及知识点:

列的别名

三种定义方式:

  • ①直接空格
    例:SELECT employee_id emp_id FROM employees;
  • ②使用AS,全称:alias(别名),可以省略(省略之后就是方式一)
    例:SELECT last_name AS lname FROM employees;
  • ③使用双引号
    例:SELECT last_name "Name" FROM employees;

解题代码:

sql 复制代码
SELECT DISTINCT
    viewer_id AS id
FROM
    Views
WHERE
    author_id = viewer_id
ORDER BY
    id
;

无效的推文(单行函数)

题目链接:1683. 无效的推文

涉及知识点:

在MySQL中,单行函数 是指对数据表中的每一行记录单独进行运算,并返回一个对应结果的函数(即输入一行、输出一行),是数据查询、转换和处理的核心工具。根据功能可分为字符串函数、数值函数、日期时间函数、转换函数、条件函数等类别,以下是各类别中最常用的单行函数总结:

单行函数的核心作用是 "对值进行处理并返回新值",因此只要 SQL 语句中需要一个 "值" 的位置(如字段、条件、排序依据等),几乎都可以使用单行函数。

一、字符串函数(处理字符/字符串类型数据)

用于字符串的拼接、截取、替换、大小写转换等操作,核心函数如下:

函数语法 功能说明 示例 结果
CONCAT(s1, s2, ...sn) 拼接多个字符串(若有一个参数为NULL,结果整体为NULL CONCAT('My', 'SQL', '5.7') MySQL5.7
CONCAT_WS(sep, s1,...) 用指定分隔符sep拼接字符串(自动忽略NULL CONCAT_WS('-', '2024', '10', '12') 2024-10-12
LENGTH(str) 计算字符串的字节数(注意:UTF-8编码中,中文占3字节,英文占1字节) LENGTH(' MySQL数据库 ') 11(空格+英文5+中文2×3)
CHAR_LENGTH(str) 计算字符串的字符数(无论中英文,每个字符计1) CHAR_LENGTH(' MySQL数据库 ') 7(空格1+英文5+中文1)
UPPER(str) / LOWER(str) 将字符串转为全大写/全小写 UPPER('mysql') / LOWER('MYSQL') MYSQL / mysql
SUBSTRING(str, pos, len) 截取字符串:pos为起始位置(1开始),len为截取长度(可选,默认到末尾) SUBSTRING('HelloWorld', 3, 4) lloW
TRIM([opt] str) 去除字符串首尾的指定字符(默认去除空格;opt可指定LEADING/TRAILING TRIM(' abc ') / TRIM(LEADING 'x' FROM 'xxabcxx') abc / abcxx
REPLACE(str, old, new) 将字符串中所有old子串替换为new REPLACE('Hello', 'l', 'x') Hexxo
LEFT(str, n) / RIGHT(str, n) 从字符串左侧 /右侧截取n个字符 LEFT('Database', 4) Data

二、数值函数(处理数值类型数据)

用于数值的计算、取整、进制转换等,核心函数如下:

函数语法 功能说明 示例 结果
ABS(n) 计算数值n的绝对值 ABS(-10.5) 10.5
ROUND(n, d) n进行四舍五入,d为保留小数位数(默认d=0,即整数;d<0向个位左舍入) ROUND(3.1415, 2) / ROUND(123, -1) 3.14 / 120
CEIL(n) / FLOOR(n) CEIL(n):向上取整(取大于等于n的最小整数);FLOOR(n):向下取整 CEIL(2.1) / FLOOR(2.9) 3 / 2
MOD(n, m) 计算n除以m的余数(同n % m,结果符号与n一致) MOD(10, 3) / MOD(-10, 3) 1 / -1
POWER(n, p) 计算np次方(n^p POWER(2, 3) 8
SQRT(n) 计算n的平方根(n必须非负) SQRT(16) 4

三、日期时间函数(处理DATE/DATETIME/TIME类型数据)

用于日期时间的获取、计算、格式化,是业务中(如订单时间、日志时间)最常用的函数类别之一:

函数语法 功能说明 示例 结果(示例)
NOW() / CURRENT_TIMESTAMP() 获取当前日期+时间 (格式:YYYY-MM-DD HH:MM:SS NOW() 2024-10-12 15:30:45
CURDATE() / DATE(NOW()) 获取当前日期 (格式:YYYY-MM-DD CURDATE() 2024-10-12
CURTIME() / TIME(NOW()) 获取当前时间 (格式:HH:MM:SS CURTIME() 15:30:45
DATE_FORMAT(dt, fmt) 将日期时间dt按指定格式fmt转换为字符串(fmt为格式符,如%Y=4位年、%m=2位月) DATE_FORMAT(NOW(), '%Y年%m月%d日 %H:%i') 2024年10月12日 15:30
STR_TO_DATE(str, fmt) 将字符串str按格式fmt转换为日期时间类型(与DATE_FORMAT反向) STR_TO_DATE('2024-10-12', '%Y-%m-%d') 2024-10-12
DATEDIFF(d1, d2) 计算两个日期的差值(d1 - d2,结果为天数,仅关注日期部分) DATEDIFF('2024-10-12', '2024-10-01') 11
TIMESTAMPDIFF(unit, d1, d2) 按指定单位unit计算d2 - d1的差值(unitY=年、M=月、D=日、H=时) TIMESTAMPDIFF(MONTH, '2024-01-01', '2024-10-12') 9
DATE_ADD(dt, INTERVAL expr unit) 给日期dt增加指定时间间隔(unit同上) DATE_ADD(CURDATE(), INTERVAL 7 DAY) 2024-10-19
YEAR(dt) / MONTH(dt) / DAY(dt) 从日期dt中提取 /月/日(同理:HOUR()/MINUTE()/SECOND()提取时间) YEAR('2024-10-12') 2024

四、类型转换函数(转换数据类型)

用于在不同数据类型(如字符串、数值、日期)之间转换,核心函数为CAST()CONVERT()

函数语法 功能说明 示例 结果
CAST(expr AS type) 将表达式expr转换为指定类型type(支持类型:CHARINTDATEDATETIME等) CAST('2024' AS INT) / CAST(123 AS CHAR) 2024 / '123'
CONVERT(expr, type) CAST()功能完全一致,仅语法不同 CONVERT('3.14' AS DECIMAL(5,2)) 3.14

注意:转换需符合逻辑(如CAST('abc' AS INT)会返回0,而非报错)。

五、条件函数(按条件返回结果)

类似编程语言中的"分支逻辑",根据条件判断返回不同值,最常用的是IF()CASE()

  1. IF()函数(简单二分支)
  • 语法:IF(condition, val1, val2)
  • 逻辑:若条件conditionTRUE,返回val1;否则返回val2(支持嵌套)。
  • 示例:IF(score >= 60, '及格', '不及格')(分数≥60返回"及格",否则"不及格")。

解题代码:

sql 复制代码
SELECT
    tweet_id       
FROM
    Tweets
WHERE
    LENGTH(content) > 15
;

连接

使用唯一标识码替换员工ID(表连接)

题目链接:1378. 使用唯一标识码替换员工ID

注意点:

MySQL数据库的表连接方式

  1. 内连接(INNER JOIN)

    • 语法:SELECT ... FROM 表A INNER JOIN 表B ON 表A.字段 = 表B.字段;
    • 通俗理解:只保留两个表中关联条件匹配成功的记录,相当于"取交集"。
    • 例子:表A(学生)有id=1,2,表B(成绩)有student_id=1,3,内连接后只保留id=1的匹配记录。
  2. 左连接(LEFT JOIN / LEFT OUTER JOIN)

    • 语法:SELECT ... FROM 表A LEFT JOIN 表B ON 表A.字段 = 表B.字段;
    • 通俗理解:保留左表(表A)的所有记录,右表(表B)只保留匹配的记录,不匹配的部分用NULL填充。
    • 例子:表A(学生)有id=1,2,表B(成绩)有student_id=1,左连接后保留id=1(匹配成绩)和id=2(成绩为NULL)。
  3. 右连接(RIGHT JOIN / RIGHT OUTER JOIN)

    • 语法:SELECT ... FROM 表A RIGHT JOIN 表B ON 表A.字段 = 表B.字段;
    • 通俗理解:与左连接相反,保留右表(表B)的所有记录,左表(表A)不匹配的部分用NULL填充。
    • 例子:表A(学生)有id=1,表B(成绩)有student_id=1,3,右连接后保留student_id=1(匹配学生)和student_id=3(学生信息为NULL)。
  4. 全连接(FULL JOIN)

    • 说明:MySQL不直接支持FULL JOIN,但可通过UNION组合左连接和右连接实现。
    • 效果:保留两个表的所有记录,互相不匹配的部分用NULL填充,相当于"取并集"。
    • 替代语法:
      SELECT ... FROM 表A LEFT JOIN 表B ON ... UNION SELECT ... FROM 表A RIGHT JOIN 表B ON ...;
  5. 交叉连接(CROSS JOIN)

    • 语法:SELECT ... FROM 表A CROSS JOIN 表B;(省略ON条件)
    • 通俗理解:两个表的所有记录两两组合,即"笛卡尔积",结果行数 = 表A行数 × 表B行数。
    • 注意:通常需配合WHERE条件过滤,否则结果可能非常庞大。

解题代码:

sql 复制代码
SELECT
    eu.unique_id,e.name
FROM
    Employees AS e LEFT JOIN EmployeeUNI AS eu
ON
    e.id = eu.id
;

产品销售分析 I

题目链接:1068. 产品销售分析 I

和上一题类似:

sql 复制代码
SELECT
    p.product_name,s.year,s.price
FROM
    Sales AS s LEFT JOIN Product AS p
ON 
    s.product_id = p.product_id
;

进店却未进行过交易的顾客(分组与聚合)

题目链接:1581. 进店却未进行过交易的顾客

本题涉及知识点:

WHERE和HAVING的区别是什么?

在SQL中,WHEREHAVING都用于筛选数据,但它们的使用场景和作用对象有本质区别,主要区别如下:

  1. 作用对象不同
  • WHERE :用于筛选原始表中的行 ,作用于单个记录 (即未经过分组的数据)。 它在数据分组(GROUP BY)之前执行,因此不能直接使用聚合函数(如SUMCOUNT等)。

  • HAVING :用于筛选分组后的结果 (即GROUP BY之后的组),作用于整个组。 它在数据分组之后执行,因此可以使用聚合函数来过滤符合条件的组。

  1. 使用场景不同
  • WHERE:适用于在分组前过滤不需要的记录,减少参与分组的数据量。

  • HAVING :适用于在分组后过滤不符合条件的组(不是以行为筛选单位,而是组!!!)

  1. 是否可与聚合函数搭配
  • WHERE不能直接使用聚合函数(因为它作用于单条记录,聚合函数需要多记录计算)。

  • HAVING可以直接使用聚合函数(因为它作用于分组后的结果,聚合函数已计算完成)。

  1. 可筛选的字段
  • WHERE:WHERE 用于在分组(GROUP BY)之前筛选原始数据行,因此它只能基于表中已有的原始字段(非聚合结果)设置条件

  • HAVING:HAVING 用于在分组(GROUP BY)之后筛选组,因此它可以基于以下两类条件:

    • 聚合函数的结果(如SUM(amount)、COUNT(visit_id)等,这些是分组后计算出的结果)。
    • GROUP BY中指定的分组字段(因为分组字段是每个组的 "标识",属于组的属性)。

②MySQL中常用的聚合函数

在MySQL中,聚合函数用于对一组数据进行计算并返回单个结果,通常与GROUP BY配合使用(也可用于整表聚合)。以下是常用的聚合函数及其说明:

  1. COUNT()
  • 功能:统计记录(行)的数量。

  • 常见用法

    • COUNT(*):统计所有行(包括NULL值和空行)。
    • COUNT(1):等效于COUNT(*),通过常量1统计行存在性。
    • COUNT(字段名):统计指定字段中非NULL值的行数。
  • 示例

    sql 复制代码
    SELECT COUNT(*) AS 总记录数, COUNT(email) AS 非空邮箱数 FROM users;
  1. SUM()
  • 功能 :计算指定数值字段的总和(忽略NULL值)。

  • 注意 :仅对数值类型有效,非数值字段会返回0NULL

  • 示例

    sql 复制代码
    SELECT SUM(amount) AS 总销售额 FROM orders;
  1. AVG()
  • 功能 :计算指定数值字段的平均值(忽略NULL值,公式:SUM(字段)/COUNT(非NULL值))。

  • 示例

    sql 复制代码
    SELECT AVG(score) AS 平均分 FROM students;
  1. MAX()
  • 功能 :返回指定字段的最大值(适用于数值、字符串、日期等类型,忽略NULL值)。

  • 示例

    sql 复制代码
    SELECT MAX(price) AS 最高价格, MAX(create_time) AS 最近订单时间 FROM products;
  1. MIN()
  • 功能 :返回指定字段的最小值(与MAX()类似,忽略NULL值)。

  • 示例

    sql 复制代码
    SELECT MIN(age) AS 最小年龄 FROM employees;
  1. GROUP_CONCAT()
  • 功能:将分组后同一组的指定字段值拼接成一个字符串(默认用逗号分隔)。

  • 常用参数

    • SEPARATOR '分隔符':自定义分隔符(如空格、分号)。
    • DISTINCT:去重后拼接。
  • 示例

    sql 复制代码
    -- 按班级分组,拼接学生姓名(用逗号分隔)
    SELECT class_id, GROUP_CONCAT(student_name) AS 学生名单 FROM students GROUP BY class_id;
    
    -- 用分号分隔并去重
    SELECT class_id, GROUP_CONCAT(DISTINCT subject SEPARATOR ';') AS 课程列表 FROM courses GROUP BY class_id;
  1. STDDEV() / STD()
  • 功能 :计算指定数值字段的标准差(衡量数据离散程度,忽略NULL值)。

  • 示例

    sql 复制代码
    SELECT STDDEV(score) AS 成绩标准差 FROM exam_results;
  1. VARIANCE() / VAR()
  • 功能 :计算指定数值字段的方差(标准差的平方,忽略NULL值)。

  • 示例

    sql 复制代码
    SELECT VARIANCE(weight) AS 体重方差 FROM athletes;

聚合函数的共性特点

  1. 忽略NULL值(COUNT(*)COUNT(1)除外,它们统计所有行)。
  2. 通常与GROUP BY结合,对分组后的数据进行聚合;若不使用GROUP BY,则对全表数据聚合。
  3. 可与DISTINCT配合去重后计算(如SUM(DISTINCT 字段)COUNT(DISTINCT 字段))。

例如,统计不同类别商品的去重供应商数量:

sql 复制代码
SELECT category, COUNT(DISTINCT supplier) AS 供应商数 FROM products GROUP BY category;

解题答案:

sql 复制代码
SELECT
    v.customer_id,COUNT(1) AS count_no_trans 
FROM
    Visits AS v LEFT JOIN Transactions AS t
ON 
    v.visit_id = t.visit_id
WHERE
    t.transaction_id IS NULL
GROUP BY
    v.customer_id
;

上升的温度(自连接)

题目链接:197. 上升的温度

解题思路:

自连接

本题考查的是交叉连接,也就是笛卡尔积。当然也可以使用INNER JOIN。

解题代码:

sql 复制代码
SELECT
    w1.id
FROM
    Weather AS w1 CROSS JOIN Weather AS w2
WHERE
    DATEDIFF(w1.recordDate,w2.recordDate) = 1 AND w1.Temperature > w2.Temperature
;

每台机器的进程平均运行时间

题目链接:1661. 每台机器的进程平均运行时间

解题思路:

还是使用自连接

解题代码:

sql 复制代码
SELECT
    a1.machine_id AS machine_id,
    ROUND(AVG(a2.timestamp - a1.timestamp),3) AS processing_time 
FROM
    Activity AS a1 INNER JOIN Activity AS a2
ON 
    a1.machine_id = a2.machine_id 
AND 
    a1.process_id = a2.process_id
AND 
    a1.activity_type = 'start'
AND
    a2.activity_type = 'end'
GROUP BY
    a1.machine_id 
;

员工奖金

解题思路:

考察左连接

解题代码:

sql 复制代码
SELECT
    e.name,b.bonus
FROM
    Employee AS e LEFT JOIN Bonus AS b
ON 
    e.empId = b.empId
WHERE
    b.bonus IS NULL OR b.bonus < 1000
;

学生们参加各科测试的次数(多表连接与笛卡尔积)

题目链接:1280. 学生们参加各科测试的次数

考察了多表连接与笛卡尔积

注意:

在 MySQL 中,如果执行 a LEFT JOIN b 时,b 表中有多条记录与 a 表中的一条记录匹配,那么查询结果中会生成多条记录。

具体来说:

  • a 表中的那条记录会被保留
  • 同时会与 b 表中所有匹配的记录分别组合,形成多条结果行
  • 最终结果的数量 = a 表中符合条件的记录数 × 对应 b 表中匹配的记录数

例如:

  • a 表有一条记录:(id=1, name='张三')
  • b 表有两条匹配记录:(id=1, a_id=1, address='北京')(id=2, a_id=1, address='上海')

执行 a LEFT JOIN b ON a.id = b.a_id 后,会得到两条结果:

复制代码
id  name   id   a_id  address
1   张三    1    1     北京
1   张三    2    1     上海

这种现象称为"笛卡尔积效应"的一种特殊情况,当连接条件匹配多条记录时就会产生。如果不需要这种效果,可能需要调整连接条件,或使用 DISTINCT、聚合函数(如 GROUP BY)来处理结果。

本题的解题逻辑:

  • Student表和Subjects表进行笛卡尔积连接(Student JOIN Subjects)
  • 在第一点的基础上拼接Examinations中的每个学生参加每门科目的数量。使用左连接。
  • 然后就可以根据st.student_id,su.subject_name进行分组,然后使用聚合函数了
  • 有一个值得注意点的点就是:使用聚合函数统计个数的时候,不能直接使用COUNT(*)、COUNT(1),因为这会把null也给统计进去。所以我们因该根据Examinations表中的subject_name来统计个数,而不能通过Subjects表中的subject_name来统计(这样的话没有考的科目也会记为1)。

解题代码:

sql 复制代码
SELECT
    st.student_id,st.student_name,su.subject_name,COUNT(e.subject_name) AS attended_exams
FROM
    Students AS st CROSS JOIN Subjects AS su LEFT JOIN Examinations AS e
ON
    e.student_id = st.student_id AND e.subject_name = su.subject_name
GROUP BY
    st.student_id,su.subject_name
ORDER BY
    st.student_id,su.subject_name
;

至少有5名直接下属的经理

题目链接:570. 至少有5名直接下属的经理

解题逻辑:

这一题的注意点在于不能使用左连接,而是应该使用内连接:

如果使用左连接的话,以下两种情况过不了:

sql 复制代码
SELECT
    e2.name 
FROM
    Employee AS e1 LEFT JOIN Employee AS e2
ON 
    e1.managerId = e2.id
GROUP BY
    e1.managerId,e2.name  
HAVING
    count(e1.managerId) >= 5 AND e2.name IS NOT NULL
;

我们需要不仅要保证员工存在,经理也需要存在,所以使用内连接:

解题代码:

sql 复制代码
SELECT
    e2.name 
FROM
    Employee AS e1 INNER JOIN Employee AS e2
ON 
    e1.managerId = e2.id
GROUP BY
    e1.managerId,e2.name  
HAVING
    count(e1.managerId) >= 5 
;

总结:表连接的使用场景

在 MySQL 中,不同的连接方式用于处理表之间的关联关系,选择哪种连接方式取决于你想要获取的数据范围。以下是各种连接的适用场景:

  1. 内连接(INNER JOIN)
    适用场景 :只需要两个表中匹配条件的记录
    内连接是最常用的连接方式,它会返回两个表中满足连接条件的交集部分。
    例如:查询"学生表"和"成绩表"中所有有成绩记录的学生信息(只显示有成绩的学生)。
sql 复制代码
SELECT 学生.姓名, 成绩.分数
FROM 学生
INNER JOIN 成绩 ON 学生.学号 = 成绩.学号;
  1. 左连接(LEFT JOIN / LEFT OUTER JOIN)
    适用场景 :需要左表的所有记录 ,以及右表中匹配条件的记录;右表中无匹配的记录用 NULL 填充。
    左连接以左表为基础,即使右表没有对应的数据,也会保留左表的全部记录。
    例如:查询"所有学生"的信息,以及他们的成绩(包括没有成绩的学生,成绩字段显示 NULL)。
sql 复制代码
SELECT 学生.姓名, 成绩.分数
FROM 学生
LEFT JOIN 成绩 ON 学生.学号 = 成绩.学号;
  1. 右连接(RIGHT JOIN / RIGHT OUTER JOIN)
    适用场景 :需要右表的所有记录 ,以及左表中匹配条件的记录;左表中无匹配的记录用 NULL 填充。
    右连接以右表为基础,功能与左连接对称,只是基准表不同。
    例如:查询"所有成绩记录",以及对应的学生信息(包括成绩记录中可能存在的无效学号,学生信息字段显示 NULL)。
sql 复制代码
SELECT 学生.姓名, 成绩.分数
FROM 学生
RIGHT JOIN 成绩 ON 学生.学号 = 成绩.学号;
  1. 全连接(FULL JOIN / FULL OUTER JOIN)
    适用场景 :需要两个表的所有记录 ,无论是否匹配;不匹配的部分用 NULL 填充。

    全连接是左连接和右连接的结合,返回两个表的并集。

    注意 :MySQL 不直接支持 FULL JOIN ,但可以通过 UNION 组合左连接和右连接实现:

sql 复制代码
-- 实现全连接效果
SELECT 学生.姓名, 成绩.分数
FROM 学生
LEFT JOIN 成绩 ON 学生.学号 = 成绩.学号
UNION
SELECT 学生.姓名, 成绩.分数
FROM 学生
RIGHT JOIN 成绩 ON 学生.学号 = 成绩.学号;

总结选择依据:

  • 交集:用内连接(INNER JOIN)。
  • 左表全部 + 右表匹配:用左连接(LEFT JOIN)。
  • 右表全部 + 左表匹配:用右连接(RIGHT JOIN)。
  • 全部记录(包括不匹配的):用全连接(FULL JOIN,MySQL 需用 UNION 模拟)。

根据业务需求中是否需要保留"不匹配的记录"来决定具体使用哪种连接方式。

确认率(条件判断的使用)

题目链接:1934. 确认率

解题思路:

条件判断可以在哪里使用?

在 MySQL 中,条件判断是非常重要的功能,广泛应用于各种查询场景中。以下是条件判断的主要使用场景和常见用法总结:

  1. WHERE 子句中使用条件判断
    最基础的用法,用于过滤行数据:
sql 复制代码
-- 筛选年龄大于18的用户
SELECT * FROM users WHERE age > 18;

-- 多条件组合
SELECT * FROM products 
WHERE price > 100 
  AND category = 'electronics' 
  OR (promotion = 1 AND stock > 0);
  1. HAVING 子句中使用条件判断
    与 GROUP BY 配合使用,用于过滤聚合后的结果:
sql 复制代码
-- 筛选平均评分大于4.5的商品分类
SELECT category, AVG(rating) as avg_rating
FROM products
GROUP BY category
HAVING avg_rating > 4.5;
  1. CASE 表达式实现复杂条件判断
    可在 SELECT、WHERE、ORDER BY 等多个位置使用,实现多分支判断:
sql 复制代码
-- 在SELECT中使用,将数值转换为文字描述
SELECT 
  id,
  score,
  CASE 
    WHEN score >= 90 THEN '优秀'
    WHEN score >= 60 THEN '及格'
    ELSE '不及格'
  END as score_level
FROM students;

-- 在ORDER BY中使用,自定义排序逻辑
SELECT * FROM products
ORDER BY 
  CASE WHEN promotion = 1 THEN 0 ELSE 1 END,
  price;
  1. 聚合函数中使用条件判断
    通常用于在聚合时只计算满足特定条件的记录:
sql 复制代码
-- 计算男性用户的平均工资(忽略女性)
SELECT AVG(CASE WHEN gender = 'male' THEN salary END) as male_avg_salary
FROM employees;

-- 统计不同状态的订单数量
SELECT 
  COUNT(CASE WHEN status = 'pending' THEN 1 END) as pending_count,
  COUNT(CASE WHEN status = 'completed' THEN 1 END) as completed_count
FROM orders;
  1. JOIN 子句中使用条件判断
    控制表之间的连接条件:
sql 复制代码
-- 只连接同部门的员工和经理
SELECT e.name, m.name as manager_name
FROM employees e
LEFT JOIN employees m 
  ON e.department_id = m.department_id
  AND m.is_manager = 1;
  1. UPDATE/DELETE 语句中使用条件判断
    控制数据修改或删除的范围:
sql 复制代码
-- 更新库存不足的商品状态
UPDATE products
SET status = 'out_of_stock'
WHERE stock <= 0;

-- 删除30天前的过期日志
DELETE FROM logs
WHERE created_at < DATE_SUB(NOW(), INTERVAL 30 DAY);
  1. 函数中的条件判断
    在自定义函数或存储过程中使用条件逻辑:
sql 复制代码
CREATE FUNCTION get_discount(price DECIMAL(10,2), is_vip INT) 
RETURNS DECIMAL(10,2)
BEGIN
  DECLARE discount DECIMAL(10,2);
  IF is_vip = 1 THEN
    SET discount = price * 0.9;
  ELSEIF price > 1000 THEN
    SET discount = price * 0.95;
  ELSE
    SET discount = price;
  END IF;
  RETURN discount;
END;

常用条件判断函数

  • IF(expr, v1, v2):如果 expr 为真返回 v1,否则返回 v2
  • IFNULL(v1, v2):如果 v1 不为 NULL 则返回 v1,否则返回 v2
  • NULLIF(expr1, expr2):如果 expr1 等于 expr2 则返回 NULL,否则返回 expr1
  • CASE:多分支条件判断(最灵活,推荐优先使用)
  1. IF(expr, v1, v2):简单双分支判断
  • 作用 :如果表达式 exprTRUE(非 0、非 NULL),返回 v1;否则返回 v2

  • 语法IF(判断条件, 条件为真时的结果, 条件为假时的结果)

  • 示例

    sql 复制代码
    -- 判断用户是否成年
    SELECT 
      name,
      age,
      IF(age >= 18, '成年', '未成年') AS age_status
    FROM users;
    
    -- 计算订单实际支付金额(有折扣则用折扣价,否则用原价)
    SELECT 
      order_id,
      price,
      discount,
      IF(discount > 0, price * discount, price) AS pay_amount
    FROM orders;
  1. IFNULL(v1, v2):处理 NULL 值
  • 作用 :如果 v1 不是 NULL,返回 v1;否则返回 v2(用于替换 NULL 为指定值)。

  • 语法IFNULL(可能为NULL的值, 替换的默认值)

  • 注意 :仅判断 NULL,不判断 0 或空字符串('')。

  • 示例

    sql 复制代码
    -- 用0替换NULL的库存(避免计算时NULL影响结果)
    SELECT 
      product_name,
      IFNULL(stock, 0) AS actual_stock  -- 若stock为NULL,返回0
    FROM products;
    
    -- 计算平均评分(忽略NULL,用0填充)
    SELECT 
      product_id,
      AVG(IFNULL(rating, 0)) AS avg_rating  -- 评分NULL时按0计算
    FROM reviews
    GROUP BY product_id;
  1. NULLIF(expr1, expr2):值相等时返回 NULL
  • 作用 :如果 expr1 等于 expr2,返回 NULL;否则返回 expr1(常用于避免除以0等场景)。

  • 语法NULLIF(值1, 值2)

  • 示例

    sql 复制代码
    -- 避免除法运算中除数为0(若除数为0,返回NULL)
    SELECT 
      total_sales,
      sales_count,
      total_sales / NULLIF(sales_count, 0) AS avg_sale  -- 若sales_count=0,结果为NULL
    FROM sales;
    
    -- 当名称为空字符串时返回NULL
    SELECT 
      name,
      NULLIF(name, '') AS clean_name  -- 若name是'',返回NULL
    FROM users;
  1. CASE 表达式:多分支复杂判断
    CASE 是最灵活的条件判断方式,支持多分支,可替代多层 IF() 嵌套。有两种语法形式:

形式1:CASE expr WHEN val1 THEN res1 ... ELSE resN END

  • 用于判断 expr 是否等于某个值(单条件匹配)。

  • 示例

    sql 复制代码
    -- 将订单状态码转换为文字描述
    SELECT 
      order_id,
      status,
      CASE status 
        WHEN 1 THEN '待支付'
        WHEN 2 THEN '已支付'
        WHEN 3 THEN '已发货'
        ELSE '已取消'  -- 其他状态默认值
      END AS status_desc
    FROM orders;

形式2:CASE WHEN cond1 THEN res1 ... ELSE resN END

  • 用于判断多个不同条件(多条件匹配,更常用)。

  • 示例

    sql 复制代码
    -- 根据分数划分等级(多条件范围判断)
    SELECT 
      student_id,
      score,
      CASE 
        WHEN score >= 90 THEN 'A'
        WHEN score >= 80 THEN 'B'
        WHEN score >= 60 THEN 'C'
        ELSE 'D'
      END AS grade
    FROM exam_scores;
    
    -- 按用户等级计算折扣(组合条件)
    SELECT 
      user_id,
      level,
      consumption,
      CASE 
        WHEN level = 'VIP' AND consumption > 1000 THEN 0.8
        WHEN level = 'VIP' THEN 0.9
        WHEN consumption > 500 THEN 0.95
        ELSE 1.0
      END AS discount
    FROM users;

总结:函数选择建议

  • 简单双分支判断:用 IF()(简洁)。
  • 处理 NULL 替换:用 IFNULL()
  • 需要"值相等时返回 NULL":用 NULLIF()(如避免除0)。
  • 多分支、范围判断或复杂逻辑:优先用 CASE(可读性更好,支持更多条件)。

这些函数可在 SELECTWHEREORDER BY、聚合函数(如 SUM()AVG())等场景中灵活使用,满足各种条件判断需求。

解题代码:

sql 复制代码
# Write your MySQL query statement below
SELECT
    s.user_id,
    CASE
        WHEN COUNT(c.action) = 0 THEN ROUND(0,2)
        ELSE ROUND(COUNT(CASE WHEN c.action = 'confirmed' THEN 1 END) / COUNT(c.action),2)
    END AS confirmation_rate
FROM
    Signups AS s LEFT JOIN Confirmations AS c
ON
    s.user_id = c.user_id
GROUP BY
    s.user_id
;
相关推荐
迷失的walker4 小时前
【Qt C++ QSerialPort】QSerialPort fQSerialPortInfo::availablePorts() 执行报错问题解决方案
数据库·c++·qt
程序新视界4 小时前
什么是OLTP ,MySQL是如何支持OLTP的?
数据库·后端·mysql
pen-ai4 小时前
【数据工程】14. Stream Data Processing
数据库·oracle
倔强的石头1065 小时前
【金仓数据库】ksql 指南(三) —— 创建与管理表空间和模式
数据库·金仓数据库
金仓拾光集5 小时前
__金仓数据库平替MongoDB:银行存款系统国产化实践__
数据库·mongodb
流烟默5 小时前
MongoDB入门指南基础篇
数据库·mongodb
金仓拾光集5 小时前
_金仓数据库平替MongoDB实战:制造业生产进度管理的国产化升级之路
数据库·mongodb
熊文豪5 小时前
时序数据库选型指南:从大数据视角看高效存储与分析
大数据·数据库·时序数据库
Lisonseekpan5 小时前
为什么要避免使用 `SELECT *`?
java·数据库·后端·sql·mysql·oracle