数据库 SQL 语句的一些复习笔记。
最近看 leetcode 上的数据库题时,感觉又拾回了上数据库课写那种脑筋急转弯题目一样的感觉。
连接问题
我电脑 Windows 下 IDEA、datagrip、JDBC 使用默认设置会连接不上本地 MySQL,jdbc 参数后加 ?characterEncoding=UTF-8&serverTimezone=GMT%2B8
即可。
第一个参数是指定编码为 UTF-8,第二个指定时区为 GMT+8。
时区错误可能是之前我为了解决双系统时间不同步,使用了偏方,修改了 Windows 默认计时方式。
本人测试在 Ubuntu 和 mscOS 下可以不添加这些参数。
创建
CREATE TABLE test_table
(
id INT NOT NULL AUTO_INCREMENT,
col1 INT NOT NULL DEFAULT 1,
col2 VARCHAR(45) NULL,
col3 DATE NULL,
# 主键
PRIMARY KEY (`id`)
);
创建好的表如下所示:
修改
添加列
ALTER TABLE test_table
ADD new_col VARCHAR(20);
删除 col3 这一列:
ALTER TABLE test_table
DROP COLUMN col3;
删除 test_table
表:
DROP TABLE test_table;
插入
向 test_table 插入 col1 值为 114514 的一条记录:
INSERT INTO test_table(col1)
VALUES (114514);
将查询到的结果插入表:
INSERT INTO test_table(col1, col2)
SELECT col1, col2
FROM test_table;
拷贝表(只会拷贝数据和类型,不会拷贝表结构,比如主键等):
CREATE TABLE new_table AS
SELECT *
FROM test_table
更新
将 id 为 8 的 col2 记录改为 col2_val:
UPDATE new_table
SET col2='col2_val'
WHERE id = 8;
删除
删除 id 为 10 的记录:
DELETE
FROM test_table
WHERE id = 10
清空 test_table 表:
TRUNCATE TABLE test_table;
查询
DISTINCT
相同值只出现一次,列值完全相同才算做相同:
SELECT DISTINCT col1, col2
FROM new_table;
LIMIT
限制返回的行数。
接受两个参数,第一个表示偏移量,起始为 0,可省略,第二个参数表示返回记录的最大数。
显示查询的前三行:
SELECT *
FROM new_table
LIMIT 3;
上面的查询语句等价为 LIMIT 0, 3
。
偏移 2,最多返回五条记录,即返回 3 - 7 行,从第三行开始只有四条记录,所以只返回四条:
SELECT *
FROM new_table
LIMIT 2, 5;
排序
- ASC 升序(默认)
- DESC 降序
e.g.
查询 test_table 中所有数据,优先按 col1 降序排列,col1 相等时按 col2 升序排列:
SELECT *
FROM test_table
ORDER BY col1 DESC, col2 ASC;
查询结果如图所示:
过滤
操作符 | 说明 |
---|---|
= | 等于 |
< | 小于 |
> | 大于 |
!= <> | 不等于 |
<= !> | 小于等于 |
>= !< | 大于等于 |
BETWEEN | 在两个值之间 |
IS NULL | 为 null 值 |
AND 和 OR 用于连接多个过滤条件。优先处理 AND,当一个过滤表达式涉及到多个 AND 和 OR 时,可以使用 () 来决定优先级,使得优先级关系更清晰。
IN 操作符用于匹配一组值,其后也可以接一个 SELECT 子句,从而匹配子查询得到的一组值。
NOT 操作符用于否定一个条件。
e.g.
SELECT *
FROM test_table
WHERE col1 != 114514;
计算字段
使用 AS
来取别名,比如:
SELECT col1 * id AS temp
FROM test_table;
否则列名会变成 col1 * id
。
CONCAT
可将字段进行连接,比如:
SELECT CONCAT(id, ', ', col1, '(', col2, ')') AS result
FROM test_table;
某些 数据库会用空格把某个值进行填充,使用 TRIM()
可以去除首尾空格,e.g.
SELECT CONCAT(id, ', ', TRIM(col1), '(', TRIM(col2), ')') AS concat_col
FROM test_table;
函数
汇总
函 数 | 说 明 |
---|---|
AVG() | 返回某列的平均值 |
COUNT() | 返回某列的行数 |
MAX() | 返回某列的最大值 |
MIN() | 返回某列的最小值 |
SUM() | 返回某列值之和 |
计算 col1
所有列值的平均值:
SELECT AVG(col1)
FROM test_table;
结果为 467347.6000
。
计算 col1
所有列值的平均值(不包含重复列值):
SELECT AVG(DISTINCT col1)
FROM test_table;
结果为 408542.0000
。
字符串
函数 | 说明 |
---|---|
LEFT() | 左边的字符 |
RIGHT() | 右边的字符 |
LOWER() | 转换为小写字符 |
UPPER() | 转换为大写字符 |
LTRIM() | 去除左边的空格 |
RTRIM() | 去除右边的空格 |
LENGTH() | 长度 |
SOUNDEX() | 转换为语音值 |
e.g.
在表中新添加以下记录:
SELECT *
FROM test_table
WHERE SOUNDEX(col2) = SOUNDEX('apple')
日期和时间
- 日期格式: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() | 返回一个日期的年份部分 |
SELECT now();
# 结果:
2019-06-05 21:56:04
数值
函数 | 说明 |
---|---|
SIN() | 正弦 |
COS() | 余弦 |
TAN() | 正切 |
ABS() | 绝对值 |
SQRT() | 平方根 |
MOD() | 余数 |
EXP() | 指数 |
PI() | 圆周率 |
RAND() | 随机数 |
分组
把具有相同数值的某查询记录放在同一组中,可以方便进行统计、求值等操作。
e.g.
表中数据如下:
SELECT col1, COUNT(*) AS `count`
FROM test_table
GROUP BY col1;
查询结果:
WHERE 过滤行,HAVING 过滤分组,行过滤应当先于分组过滤。
e.g.
SELECT col1, COUNT(*) AS `count`
FROM test_table
WHERE col1 > 1
GROUP BY col1
HAVING count > 1;
查询结果只有以下一条:
子查询
子查询中只能返回一个字段的数据。
可以将子查询的结果作为 WHRER 语句的过滤条件:
SELECT *
FROM test_table
WHERE col1 IN (SELECT col2 FROM test_table)
连接
连接可以替换子查询,一般效率比子查询快。
内连接
内连接又称等值连接,使用 INNER JOIN
关键字。
SELECT A.value, B.value
FROM tablea AS A INNER JOIN tableb AS B
ON A.key = B.key;
可以不明确使用 INNER JOIN
,而使用普通查询并在 WHERE
中将两个表中要连接的列用等值方法连接起来。
SELECT A.value, B.value
FROM tablea AS A, tableb AS B
WHERE A.key = B.key;
自连接
可看成是内连接,只是连接对象变成了自己。
e.g.
一张员工表,包含员工姓名和员工所属部门,要找出与 Jim 处在同一部门的所有员工姓名。
子查询:
SELECT name
FROM employee
WHERE department = (
SELECT department
FROM employee
WHERE name = "Jim");
自连接:
SELECT e1.name
FROM employee AS e1 INNER JOIN employee AS e2
ON e1.department = e2.department
AND e2.name = "Jim";
自然连接
自然连接是把同名列通过等值测试连接起来的,同名列可以有多个。
内连接和自然连接的区别:内连接提供连接的列,而自然连接自动连接所有同名列。
外连接
外连接保留了没有关联的那些行。分为左外连接,右外连接以及全外连接,左外连接就是保留左表没有关联的行。
组合查询
使用 UNION
来组合两个查询,如果第一个查询返回 M 行,第二个查询返回 N 行,那么组合查询的结果一般为 M+N 行。
每个查询必须包含相同的列、表达式和聚集函数。
默认会去除相同行,如果需要保留相同行,使用 UNION ALL
。
只能包含一个 ORDER BY
子句,并且必须位于语句的最后。
*e.g. *
SELECT col1
FROM test_table
WHERE col1 = 1
UNION
SELECT col1
FROM test_table
WHERE col1 = 114514;
视图
视图是虚拟的表,本身不包含数据,也就不能对其进行索引操作。
对视图的操作和对普通表的操作一样。
视图具有如下好处:
- 简化复杂的 SQL 操作,比如复杂的连接;
- 只使用实际表的一部分数据;
- 通过只给用户访问视图的权限,保证数据的安全性;
- 更改数据格式和表示。
e.g.
创建一个 test_view 视图,其结果和上一个例子一致。
CREATE VIEW test_view AS
SELECT col1
FROM test_table
WHERE col1 = 1
UNION
SELECT col1
FROM test_table
WHERE col1 = 114514;
事务
如果没有设置保留点,ROLLBACK 会回退到 START TRANSACTION 语句处;如果设置了保留点,并且在 ROLLBACK 中指定该保留点,则会回退到该保留点。
START TRANSACTION
// ...
SAVEPOINT delete1
// ...
ROLLBACK TO delete1
// ...
COMMIT
参考: CyC2018/CS-Notes