sql语法
Sql语句执行顺序
- from
- join
- where
- group by
- having
- select
- distinct
- order by
- limit/offset
Case和If
CASE
表达式有两种形式:简单 CASE
表达式和搜索式 CASE
表达式。
- 简单
CASE
表达式:
1 | CASE 表达式 |
它将一个表达式与多个值进行比较,根据匹配情况返回相应的结果。
- 搜索式
CASE
表达式:
1 | CASE |
可以使用更复杂的条件进行判断,依次检查每个 WHEN
子句中的条件,当某个条件为 TRUE
时,返回对应的结果。
IF
函数接受三个参数,当条件为 TRUE
时返回 结果1
,当条件为 FALSE
时返回 结果2
。
1 | IF(条件, 结果1, 结果2) |
单字段分组
示例表 Sales
order_id | product | category | amount |
---|---|---|---|
1 | A | Electronics | 100 |
2 | B | Books | 50 |
3 | A | Electronics | 200 |
4 | C | Books | 30 |
按 category
统计销售额
1 | SELECT category, SUM(amount) AS total_sales |
结果
category | total_sales |
---|---|
Electronics | 300 |
Books | 80 |
多字段分组
按 category
和 product
统计销量
1 | SELECT category, product, COUNT(*) AS sales_count |
结果
category | product | sales_count |
---|---|---|
Electronics | A | 2 |
Books | B | 1 |
Books | C | 1 |
结合聚合函数
常用聚合函数:
SUM()
:求和AVG()
:平均值COUNT()
:计数MAX()/MIN()
:最大/最小值
示例:计算每个产品的平均销售额
1 | SELECT product, AVG(amount) AS avg_amount |
过滤分组结果(HAVING)
筛选总销售额 > 100 的类别
1 | SELECT category, SUM(amount) AS total_sales |
结果
category | total_sales |
---|---|
Electronics | 300 |
常见错误与注意事项
错误1:选择未聚合的字段
1 | -- 错误写法(product 未在 GROUP BY 中) |
报错:Column 'product' is invalid in the select list...
错误2:WHERE 与 HAVING 混淆
WHERE
:在分组前过滤行。HAVING
:在分组后过滤组。
其他注意事项:
- NULL 值处理:
GROUP BY
将NULL
视为同一分组。 - 性能优化:大表分组时,确保分组字段有索引。
- 结果顺序:分组结果默认无序,需用
ORDER BY
明确排序。
综合示例
统计每个类别下销量超过1次的商品
1 | SELECT |
DISTINCT
对单列数据去重:假设有一个 Employees
表,包含 employee_id
、employee_name
和 department
列,现在要查询不同的部门名称。
对多列数据组合去重:若要查询不同的员工姓名和部门的组合。
与聚合函数一起使用:DISTINCT
可以与聚合函数(如 COUNT
、SUM
等)一起使用,用于统计不同值的数量或对不同值进行求和。
UNION
注意:使用
union
组合查询时,只能使用一条order by
字句,他必须位于最后一条select
语句之后
UNION
运算符将两个或更多查询的结果组合起来,并生成一个结果集,其中包含来自 UNION
中参与查询的提取行。
UNION
基本规则:
- 所有查询的列数和列顺序必须相同。
- 每个查询中涉及表的列的数据类型必须相同或兼容。
- 通常返回的列名取自第一个查询。
默认地,UNION
操作符选取不同的值。如果允许重复的值,请使用 UNION ALL
。
1 | SELECT column_name(s) FROM table1 |
UNION
结果集中的列名总是等于 UNION
中第一个 SELECT
语句中的列名。
JOIN
vs UNION
:
JOIN
中连接表的列可能不同,但在UNION
中,所有查询的列数和列顺序必须相同。UNION
将查询之后的行放在一起(垂直放置),但JOIN
将查询之后的列放在一起(水平放置),即它构成一个笛卡尔积。
UNION
运算符将两个或更多查询的结果组合起来,并生成一个结果集,其中包含来自 UNION
中参与查询的提取行。
UNION
基本规则:
- 所有查询的列数和列顺序必须相同。
- 每个查询中涉及表的列的数据类型必须相同或兼容。
- 通常返回的列名取自第一个查询。
默认地,UNION
操作符选取不同的值。如果允许重复的值,请使用 UNION ALL
。
1 | SELECT column_name(s) FROM table1 |
UNION
结果集中的列名总是等于 UNION
中第一个 SELECT
语句中的列名。
JOIN
vs UNION
:
JOIN
中连接表的列可能不同,但在UNION
中,所有查询的列数和列顺序必须相同。UNION
将查询之后的行放在一起(垂直放置),但JOIN
将查询之后的列放在一起(水平放置),即它构成一个笛卡尔积。
Replace
- Replace 函数
REPLACE
函数用于在字符串中替换指定的子字符串。它会将字符串中所有出现的指定旧子字符串替换为新的子字符串。
1 | REPLACE(str, old_substr, new_substr) |
str
:要进行替换操作的原始字符串。old_substr
:需要被替换的旧子字符串。new_substr
:用于替换旧子字符串的新子字符串。
- Replace into
REPLACE INTO
语句用于向表中插入数据,如果插入的数据对应的主键或唯一索引已经存在,则先删除原有的记录,然后再插入新的记录;如果不存在,则直接插入新记录。
- 示例
1 | -- 创建一个示例表 |
在上述示例中,由于 user_id
为 1 的记录已经存在,REPLACE INTO
语句会先删除原有的记录,然后插入新的记录,将 user_name
更新为 'Bob'
。
1 | UPDATE examination_info |
把examination_info表中tag
为PYTHON
的tag
字段全部修改为Python
。
USING
USING
关键字主要用于简化表连接操作,特别是在进行 JOIN
操作时,它可以让代码更加简洁。
假设有两个表:Orders
表和 Customers
表,它们都有一个名为 customer_id
的列,用于关联订单和客户信息。
1 | -- 创建 Customers 表 |
在这个示例中,USING (customer_id)
表示根据 Orders
表和 Customers
表中的 customer_id
列进行连接。与使用传统的 ON
子句(ON Orders.customer_id = Customers.customer_id
)相比,USING
语法更加简洁。
USING
也可以用于多列连接,只需在括号中列出多个列名,用逗号分隔即可。
AFTER
在 SQL 中,当使用 ALTER TABLE
语句向表中添加新列时,AFTER
关键字可以指定新增列在表中的位置,即让新增列位于指定列之后。如果不使用 AFTER
关键字,新增列通常会被添加到表的最后一列。
1 | ALTER TABLE user_info |
EXIST
EXISTS
运算符不关注子查询返回的具体数据内容,仅在乎子查询是否能返回至少一行结果。若子查询返回了至少一行,EXISTS
就会返回布尔值 TRUE
,此时主查询会包含满足该条件的记录;若子查询没有返回任何行,EXISTS
则返回 FALSE
,主查询会排除满足该条件的记录。
1 | SELECT customer_name |
这里,对于 Customers
表中的每一行记录,子查询会检查 Orders
表中是否存在 customer_id
与之匹配的订单记录。若存在,EXISTS
返回 TRUE
,该行客户记录会被包含在主查询结果中;若不存在,EXISTS
返回 FALSE
,该行客户记录会被排除。
函数
文本处理
函数 | 说明 |
---|---|
LEFT() 、RIGHT() |
左边或者右边的字符 |
LOWER() 、UPPER() |
转换为小写或者大写 |
LTRIM() 、RTRIM() |
去除左边或者右边的空格 |
LENGTH() |
长度,以字节为单位 |
SOUNDEX() |
转换为语音值 |
其中, SOUNDEX()
可以将一个字符串转换为描述其语音表示的字母数字模式。
1 | SELECT * |
日期和时间处理
日期格式:
YYYY-MM-DD
时间格式:
HH:MM:SS
函 数 | 说 明 |
---|---|
AddDate() |
增加一个日期(天、周等) |
AddTime() |
增加一个时间(时、分等) |
CurDate() |
返回当前日期 |
CurTime() |
返回当前时间 |
Date() |
返回日期时间的日期部分 |
DateDiff() |
计算两个日期之差 |
Date_Add() |
高度灵活的日期运算函数 |
Date_Format() |
返回一个格式化的日期或时间串 |
Day() |
返回一个日期的天数部分 |
DayOfWeek() |
对于一个日期,返回对应的星期几 |
Hour() |
返回一个时间的小时部分 |
Minute() |
返回一个时间的分钟部分 |
Month() |
返回一个日期的月份部分 |
Now() |
返回当前日期和时间 |
Second() |
返回一个时间的秒部分 |
Time() |
返回一个日期时间的时间部分 |
Year() |
返回一个日期的年份部分 |
数值处理
函数 | 说明 |
---|---|
SIN() | 正弦 |
COS() | 余弦 |
TAN() | 正切 |
ABS() | 绝对值 |
SQRT() | 平方根 |
MOD() | 余数 |
EXP() | 指数 |
PI() | 圆周率 |
RAND() | 随机数 |
汇总
函 数 | 说 明 |
---|---|
AVG() |
返回某列的平均值 |
COUNT() |
返回某列的行数 |
MAX() |
返回某列的最大值 |
MIN() |
返回某列的最小值 |
SUM() |
返回某列值之和 |
AVG()
会忽略 NULL 行。
使用 DISTINCT
可以让汇总函数值汇总不同的值。
1 | SELECT AVG(DISTINCT col1) AS avg_col |
COUNT的用法
- COUNT(*): 统计表中所有行的数量,包括NULL值。
1 | SELECT COUNT(*) FROM table_name; |
- COUNT(column): 统计指定列中非NULL值的数量。
1 | SELECT COUNT(column_name) FROM table_name; |
- COUNT(DISTINCT column): 统计指定列中不重复的非NULL值的数量。
1 | SELECT COUNT(DISTINCT column_name) FROM table_name; |
- COUNT(CASE WHEN action = ‘confirmed’ THEN 1 END):统计列名是confirmed的行数量,count里面的参数是NULL不会+1,不是NULL就+1.
AVG的用法
功能是计算一组数值的平均值。它在数据统计和分析方面非常实用,能够帮助我们快速获取数据的平均水平。需要注意的是,AVG
函数会自动忽略 NULL
值,仅对非 NULL
的数值进行计算。
1 | AVG([DISTINCT] 列名) |
参数解释
DISTINCT
(可选):使用该关键字时,AVG
函数会先去除重复值,再计算平均值。若省略DISTINCT
,则会对所有非NULL
值进行计算。- 列名:指定要计算平均值的列,该列的数据类型通常为数值类型,如
INT
、DECIMAL
等。
(一)计算单列的平均值
假设存在一个 Employees
表,记录了员工的薪资信息,表结构和示例数据如下:
1 | -- 创建 Employees 表 |
要计算所有员工的平均薪资,可以使用以下查询:
1 | SELECT AVG(salary) AS average_salary |
结果解释:此查询会计算 Employees
表中 salary
列的平均值,并将结果命名为 average_salary
。
(二)使用 DISTINCT
计算不同值的平均值
假设 Employees
表中有一些重复的薪资值,现在要计算不同薪资值的平均值。
1 | SELECT AVG(DISTINCT salary) AS distinct_average_salary |
结果解释:DISTINCT
关键字会先去除 salary
列中的重复值,然后 AVG
函数对这些不同的值计算平均值。
(三)结合 GROUP BY
子句按分组计算平均值
假设 Employees
表中还有一个 department_id
列,用于表示员工所属的部门,现在要计算每个部门的平均薪资。
1 | -- 修改 Employees 表,添加 department_id 列 |
结果解释:GROUP BY
子句会将员工按 department_id
分组,然后 AVG
函数分别计算每个组内 salary
列的平均值。
(四)结合 WHERE
子句计算满足条件的平均值
若要计算薪资大于 5500 的员工的平均薪资,可以使用 WHERE
子句进行筛选。
1 | SELECT AVG(salary) AS high_salary_average |
结果解释:WHERE
子句会筛选出 salary
大于 5500 的员工记录,然后 AVG
函数对这些记录的 salary
列计算平均值。
注意事项
NULL
值处理:AVG
函数会自动忽略NULL
值。若要计算包含NULL
值的列的平均值,需要先对NULL
值进行处理,例如使用COALESCE
函数将NULL
值替换为合适的默认值。
1 | SELECT AVG(COALESCE(salary, 0)) AS average_salary |
- 数据类型:
AVG
函数只能用于数值类型的列。若对非数值类型的列使用AVG
函数,会导致错误。 - 性能考虑:在处理大量数据时,频繁使用
AVG
函数可能会影响性能。可以考虑在合适的列上创建索引,以提高查询效率。
问题: avg(case when rating < 3 then 1 else 0 end)
和 avg(case when rating < 3 then 1 end)
有什么区别?
AVG(case when rating < 3 then 1 else 0 end)
- 这里的
CASE
表达式是一个完整的条件判断,对于每一行记录,当rating
小于 3 时,表达式返回 1;当rating
大于等于 3 时,表达式返回 0。 AVG
函数会对这些 1 和 0 进行求平均值的操作。实际上,这个平均值代表的是rating
小于 3 的记录在所有记录中所占的比例。
AVG(case when rating < 3 then 1 end)
此
CASE
表达式没有ELSE
部分,当rating
小于 3 时,表达式返回 1;当rating
大于等于 3 时,表达式返回NULL
。AVG
函数会忽略NULL
值,只对返回 1 的记录进行计数,然后计算平均值。最终结果是rating
小于 3 的记录数量除以rating
小于 3 的记录数量,结果始终为 1。
Date_format
基本语法:
1 | DATE_FORMAT(date, format) |
date
:这是需要进行格式化的日期或日期时间表达式,可以是日期类型的列名,也可以是具体的日期值,例如'2024-10-01'
。format
:用于指定输出格式的字符串,其中包含各种格式说明符,这些说明符决定了日期和时间的显示方式。
常用格式说明符
说明符 | 描述 | 示例 |
---|---|---|
%Y |
四位数的年份 | 2024 |
%y |
两位数的年份 | 24 |
%m |
两位数的月份(01 - 12) | 03 |
%c |
月份(1 - 12) | 3 |
%d |
两位数的日(01 - 31) | 05 |
%e |
日(1 - 31) | 5 |
%H |
24 小时制的小时数(00 - 23) | 15 |
%h |
12 小时制的小时数(01 - 12) | 03 |
%i |
分钟数(00 - 59) | 30 |
%s |
秒数(00 - 59) | 45 |
%p |
AM 或 PM | PM |
约束
约束可以在创建表时规定(通过 CREATE TABLE 语句),或者在表创建之后规定(通过 ALTER TABLE 语句)。
约束类型:
NOT NULL
- 指示某列不能存储 NULL 值。UNIQUE
- 保证某列的每行必须有唯一的值。PRIMARY KEY
- NOT NULL 和 UNIQUE 的结合。确保某列(或两个列多个列的结合)有唯一标识,有助于更容易更快速地找到表中的一个特定的记录。FOREIGN KEY
- 保证一个表中的数据匹配另一个表中的值的参照完整性。CHECK
- 保证列中的值符合指定的条件。DEFAULT
- 规定没有给列赋值时的默认值。
事务处理
不能回退 SELECT
语句,回退 SELECT
语句也没意义;也不能回退 CREATE
和 DROP
语句。
MySQL 默认是隐式提交,每执行一条语句就把这条语句当成一个事务然后进行提交。当出现 START TRANSACTION
语句时,会关闭隐式提交;当 COMMIT
或 ROLLBACK
语句执行后,事务会自动关闭,重新恢复隐式提交。
通过 set autocommit=0
可以取消自动提交,直到 set autocommit=1
才会提交;autocommit
标记是针对每个连接而不是针对服务器的。
指令:
START TRANSACTION
- 指令用于标记事务的起始点。SAVEPOINT
- 指令用于创建保留点。ROLLBACK TO
- 指令用于回滚到指定的保留点;如果没有设置保留点,则回退到START TRANSACTION
语句处。COMMIT
- 提交事务。