【数据库】MySQL知识点总结(一):基础操作

本系列包含:

  • MySQL知识点总结(一):基础操作
  • MySQL知识点总结(二):窗口函数

MySQL知识点总结(一):基础操作

  • 1.检索数据
    • 1.1 检索单个列
    • 1.2 检索多个列
    • 1.3 检索所有列
    • 1.4 检索不同的值
    • 1.5 限制结果
    • 1.6 注释
  • 2.排序
    • 2.1 排序数据
    • 2.2 按多个列排序
    • 2.3 按列位置排序
    • 2.4 指定排序方向
  • 3.过滤数据
    • 3.1 使用 WHERE 子句
    • 3.2 WHERE 子句操作符
  • 4.高级数据过滤
    • 4.1 组合数据过滤
    • 4.2 IN 操作符
    • 4.3 NOT 操作符
  • 5.通配符过滤
    • 5.1 百分号 % 通配符
    • 5.2 下划线 _ 通配符
    • 5.3 方括号 [] 通配符
  • 6.创建计算字段
    • 6.1 拼接字段
    • 6.2 算术计算
  • 7.函数
    • 7.1 DBMS 函数的差异
    • 7.2 常用的文本处理函数
    • 7.3 日期和时间处理函数
    • 7.4 数值处理函数
  • 8.数据汇总
  • 9.分组数据
  • 10.子查询
  • 11.联结表
    • 11.1 创建联结
    • 11.2 内联结
    • 11.3 自联结
    • 11.4 自然联结
    • 11.5 外连接
  • 12.组合查询
  • 13.插入数据
    • 13.1 数据插入
    • 13.2 从一个表复制到另一个表
  • 14.更新和删除数据
    • 14.1 更新数据
    • 14.2 删除数据
  • 15.创建和操纵表
    • 15.1 创建表
    • 15.2 更新表
    • 15.3 删除表
  • 16.视图
  • 17.存储过程
  • 18.高级SQL特性
    • 18.1 约束
      • 18.1.1 主键
      • 18.1.2 外键
      • 18.1.3 唯一约束
      • 18.1.4 检查约束
      • 18.2 索引
    • 18.3 触发器
  • 文献参考

在学习 MySQL 的基础操作之前,我们应该首先了解一下 MySQL 中的语句执行顺序。

(8) SELECT (9) DISTINCT column,# (8)选择字段 、(9)去重
(6) AGG_FUNC(column or expression),# (6)聚合函数
(1) FROM [left_table]                  # (1)选择表
(3) <join_type> JOIN <right_table>     # (3)链接
(2) ON <join_condition>                # (2)链接条件
(4) WHERE <where_condition>            # (4)条件过滤
(5) GROUP BY <group_by_list>           # (5)分组
(7) HAVING <having_condition>          # (7)分组过滤
(10) ORDER BY <order_by_list>          # (10)排序
(11) LIMIT count OFFSET count;         # (11)分页

1.检索数据

1.1 检索单个列

select name from student

1.2 检索多个列

select name, age, class from student

1.3 检索所有列

select * from student

1.4 检索不同的值

即利用 distinct 去重。

select distinct class from student

1.5 限制结果

使用 LIMIT 子句。

select * from student limit 10 offset 1
  • offset 基数为 0,所以 offset 1 代表从第 2 行开始
  • 第一个数字是检索的行数,第二个数字是指从哪儿开始。

若使用下面这个语法,逗号之前的值对应 OFFSET,逗号之后的值对应 LIMIT

select * from student limit 1, 10

1.6 注释

# 注释 1
-- 注释 2
/* 注释 3
   注释 3
   注释 3 */

2.排序

2.1 排序数据

在指定一条 ORDER BY 子句时,应该保证它是 SELECT 语句中最后一条子句。

select prod_name
from Products
order by prod_name

2.2 按多个列排序

select prod_id, prod_price, prod_name
from Products 
order by prod_price, prod_name

2.3 按列位置排序

select prod_id, prod_price, prod_name
from Products 
order by 2, 3

2.4 指定排序方向

默认排序是 ASC,所以一般升序的时候不需指定,降序的关键字是 DESC。如果想在多个列上进行降序排序,必须对每一列指定 DESC 关键字。

select prod_id, prod_price, prod_name
from Products 
order by prod_price desc
select prod_id, prod_price, prod_name
from Products 
order by prod_price desc, prod_name

3.过滤数据

3.1 使用 WHERE 子句

# 找到学号为 1 的学生。
select * from student 
where number = 1

3.2 WHERE 子句操作符

说明 操作符
等于 =
不等于 <>
不等于 !=
小于 <
小于等于 <=
不小于 !<
大于 >
大于等于 >=
不大于 !>
在指定的两个值之间 BETWEEN
为 NULL 值 IS NULL
# 找到学号为在 [1, 10] 的学生(闭区间)
select * from student 
where number between 1 and 10
# 找到未设置电子邮箱的学生,注意不能使用 =
select * from student 
where email is null

4.高级数据过滤

4.1 组合数据过滤

# 找到一班中大于 23 岁的学生
select * from student 
where class_id = 1 and age > 23
# 找到一班或者大于 23 岁的学生
select * from student 
where class_id = 1 or age > 22

4.2 IN 操作符

# 找到一班与二班的学生
select * from student 
where class_id in (1, 2)

4.3 NOT 操作符

WHERE 子句中的 NOT 操作符有且只有一个功能,那就是否定其后所跟的任何条件。

# 找到不是一班二班的学生
select * from student 
where class_id not in (1, 2)

5.通配符过滤

5.1 百分号 % 通配符

# 所有以词 Fish 起头的产品
select prod_id, prod_name
from Products 
where prod_name like 'Fish%'

5.2 下划线 _ 通配符

下划线的用途与 % 一样,但它只匹配单个字符,而不是多个字符。

select prod_id, prod_name
from Products
where prod_name like '__ inch teddy bear'

5.3 方括号 [] 通配符

用来指定一个字符集,它必须匹配指定位置(通配符的位置)的一个字符,此通配符可以用前缀字符 ^(脱字号)来否定。

# 找出所有名字以 J 或 M 起头的联系人
select cust_contact
from Customers 
where cust_contact LIKE '[JM]%' 
order by cust_contact

6.创建计算字段

6.1 拼接字段

select concat(name, '(', age, ')') as nameWithAge 
from student;
select concat('hello', 'world') as helloworld

6.2 算术计算

select age - 18 as relativeAge from student;
SELECT prod_id, quantity, item_price, quantity * item_price AS expanded_price
FROM OrderItems
WHERE order_num = 20008:

7.函数

7.1 DBMS 函数的差异

与 SQL 语句不一样,SQL 函数不是可移植的。

函数 语法
提取字符串的组成部分 DB2、Oracle、PostgreSQL 和 SQLite 使用 SUBSTR();MariaDB、MySQL 和 SQL Server 使用 SUBSTRING()
数据类型转换 Oracle 使用多个函数,每种类型的转换有一个函数;DB2 和 PostgreSQL 使用 CAST()。MariaDB、MySQL 和 SQL Server 使用CONVERT()
提取当前日期 DB2 和 PostgreSQL 使用 CURRENT_DATE;MariaDB 和 MySQL 使用 CURDATE()。Oracle 使用 SYSDATE;SQL Server 使用 GETDATE();SQLite 使用 DATE()

SQL 中,关键字与函数名不区分字母的大小写。

7.2 常用的文本处理函数

函数 说明
LEFT(str, length) 从左边开始截取 strlength 是截取的长度
RIGHT(str, length) 从右边开始截取 strlength 是截取的长度
LENGTH(str) 计算字符串 str 的长度
LOCATE(substr, str) 返回子串 substr 在字符串 str 中第一次出现的位置,如果字符 substr 在字符串 str 中不存在,则返回 0
POSITION(substr IN str) 返回子串 substr 在字符串 str 中第一次出现的位置,如果字符 substr 在字符串 str 中不存在,与 LOCATE 函数作用相同
SUBSTRING(str, n, m) 返回字符串 str 从 第 n 个字符截取,共截取 m 个字符
SUBSTRING_INDEX(str, substr, n) 返回字符 substrstr 中第 n 次出现位置之前的字符串
REPLACE(str, n, m) 将字符串 str 中的 n 字符替换成 m 字符
TRIM() 去掉字符串左右两边的空格
RTRIM() 去掉字符串右边的空格
LTRIM() 去掉字符串左边的空格
LOWER() 将字符串转换为小写
UPPER() 将字符串转换为大写

7.3 日期和时间处理函数

函数 说明
YEAR() 从日期中提取年份
MONTH() 从日期中提取月份
DAY() 从日期中提取天
TIMESTAMPDIFF(interval,datetime1,datetime2) datetime2 - datetime1 的时间差, 比较的单位 interval 可以为:FRAC_SECONDSECONDMINUTEHOURDAYWEEKMONTHQUARTERYEAR
DATEDIFF(datepart,startdate,enddate) department 可为:yearquartermonth weekdayhourminutesecondmillisecond
UNIX_TIMESTAMP() 返回时间戳
select order_num
from Orders 
where YEAR(order_date) = 2020
# 获取月
MONTH(date);
# 或者
date_format(date,'%m');
# 获取天
DAY(date);
# 或者
date_format(date,'%d');
# 获取两个时间之间相差多少分钟
(UNIX_TIMESTAMP(submit_time) - UNIX_TIMESTAMP(start_time)) / 60

7.4 数值处理函数

函数 说明
ABS() 返回一个数的绝对值
EXP() 返回一个数的指数值
SQRT() 返回一个数的平方根

8.数据汇总

聚集函数,一些对数据进行汇总的函数,常见有 COUNTMINMAXAVGSUM 五种。

# 统计1班人数
select count(*) from student 
where class_id = 1;

9.分组数据

使用 group by 进行数据分组,可以使用聚合函数对分组数据进行汇总,使用 having 对分组数据进行筛选。group by 子句必须出现在 where 子句之后,order by 子句之前。

# 按照班级进行分组并统计各班人数
select class_id, count(*) 
from student 
group by class_id
# 列出大于三个学生的班级
select class_id, count(*) as cnt 
from student
group by class_id 
having cnt > 3

10.子查询

作为子查询的 SELECT 语句只能查询单个列。

# 列出软件工程班级中的学生
select * from student
where class_id in (
    select id 
    from class 
    where class_id = '软件工程'
);

作为计算字段使用子查询

SELECT cust_name, cust_state, (
  SELECT COUNT(*)
  FROM Orders
  WHERE Orders.cust_id = Customers.cust_id) AS orders
FROM Customers
ORDER BY cust_name;

11.联结表

11.1 创建联结

要保证所有联结都有 WHERE 子句,否则 DBMS 将返回比想要的数据多得多的数据。同理,要保证 WHERE 子句的正确性。

# 列出软件工程班级中的学生
select * 
from student, class
where student.class_id = class.id and class.name = '软件工程';

虽然两个表拥有公共字段便可以创建联结,但是使用外键可以更好地保证数据完整性。比如当对一个学生插入一条不存在的班级的时候,便会插入失败。一般来说,联结比子查询拥有更好的性能。

11.2 内联结

目前为止使用的联结称为等值联结,它基于两个表之间的相等测试。这种联结也称为内联结 inner join。其实,可以对这种联结使用稍微不同的语法,明确指定联结的类型。下面的 SELECT 语句返回与前面例子完全相同的数据。

# 列出软件工程班级中的学生
select * 
from student
inner join class on student.class_id = class.id
where class.name = '软件工程';

11.3 自联结

假如要给与 Jim Jones 同一公司的所有顾客发送一封信件。这个查询要求首先找出 Jim Jones 工作的公司,然后找出在该公司工作的顾客。

# 列出与张三同一班级的学生
SELECT cust_id, cust_name, cust_contact
FROM Customers
WHERE cust_name = (
  SELECT cust_name
  FROM Customers
  WHERE cust_contact ='Jim Jones'
  )

使用自联结

SELECT cl.cust_id, cl.cust_name, cl.cust_contact
FROM Customers AS cl, Customers AS c2
WHERE cl.cust_name = c2.cust_name AND c2.cust_contact = 'Jim Jones'

此查询中需要的两个表实际上是相同的表,因此 Customers 表在 FROM 子句中出现了两次。虽然这是完全合法的,但对 Customers 的引用具有歧义性,因为 DBMS 不知道你引用的是哪个 Customers 表。

11.4 自然联结

无论何时对表进行联结,应该至少有一列不止出现在一个表中(被联结的列)。标准的联结(前一课中介绍的内联结)返回所有数据,相同的列甚至多次出现。自然联结排除多次出现,使每一列只返回一次。

怎样完成这项工作呢?答案是,系统不完成这项工作,由你自己完成它。自然联结要求你只能选择那些唯一的列,一般通过对一个表使用通配符 SELECT *,而对其他表的列使用明确的子集来完成。

SELECT C. *, O.order_num, O.order_date, OI.prod_id, OI. quantity, OI.item_price
FROM Customers AS C, Orders AS O, OrderItems AS OI
WHERE C.cust_id = O.cust_id AND OI.order_num = 0.order_num AND prod_id = 'RGANO1'

11.5 外连接

许多联结将一个表中的行与另一个表中的行相关联,但有时候需要包含没有关联行的那些行。例如,可能需要使用联结完成以下工作:

  • 对每个顾客下的订单进行计数,包括那些至今尚未下订单的顾客;
  • 列出所有产品以及订购数量,包括没有人订购的产品;
  • 计算平均销售规模,包括那些至今尚未下订单的顾客。

在上述例子中,联结包含了那些在相关表中没有关联行的行。这种联结称为外联结。外连接只限制一张表中的数据必须满足连接条件,而另一张表中数据可以不满足连接条件。语法格式为:

SELECT ... 
FROM1 
LEFT | RIGHT [OUTER] JOIN2 
ON <连接条件>
  • left join:包含左表的所有行,对应的右表行可能为空
  • right join:包含右表的所有行,对应的左表行可能为空
  • full join:左右表格的行全部都有,左右表格判断一致的在同一行,不一致的单独一行
-- 列出每个学生的班级,若没有班级则为 null
select name, class.name 
from student
left join class on student.class_id = class.id;

12.组合查询

假如需要 Illinois、Indiana 和 Michigan 等美国几个州的所有顾客的报表,还想包括不管位于哪个州的所有的 Fun4All。

SELECT cust_name, cust_contact, cust_email
FROM Customers
WHERE cust_state IN ('IL','IN','MI')
UNION
SELECT cust_name, cust_contact, cust_email
FROM Customers
WHERE cust_name = 'Fun4All';

UNION 从查询结果集中自动去除了重复的行;如果想返回所有的匹配行,可使用 UNION ALL

SELECT 语句的输出用 ORDER BY 子句排序。在用 UNION 组合查询时,只能使用一条 ORDER BY 子句,它必须位于最后一条 SELECT 语句之后。

13.插入数据

13.1 数据插入

插入单条语句。

INSERT INTO table_name ( field1, field2,...fieldN ) VALUES ( value1, value2,...valueN );

可以采用以下方法插入一条数据。

insert into student values (8, '陆小凤', 24, 1, 3);

不过上面的做法严重依赖表中列的顺序关系,推荐指定列名插入数据,并且可以插入部分列。

insert into student(name, age, sex, class_id) values(9, '花无缺', 25, 1, 3);

插入多条语句。

INSERT INTO table_name (a,b,c)  VALUES (1,2,3), (4,5,6), (7,8,9);

带更新的插入

REPLACE INTO table_name VALUES (value1, value2, ...) 

13.2 从一个表复制到另一个表

有一种数据插入不使用 INSERT 语句。要将一个表的内容复制到一个全新的表(运行中创建的表),可以使用 CREATE SELECT 语句。

CREATE TABLE CustCopy AS SELECT * FROM Customers;

14.更新和删除数据

14.1 更新数据

# 修改张三的班级
update student 
set class_id = 2 
where name = '张三'

14.2 删除数据

# 删除张三的数据
delete from student where name = '张三'
# 删除表中所有数据
delete from student
# 更快地删除表中所有数据
truncate table student

15.创建和操纵表

15.1 创建表

# 创建学生表
create table student (
  id int(11) not null auto_increment,
  name varchar(50) not null,
  age smallint default 20,
  sex enum('male', 'famale'),
  score tinyint comment '入学成绩',
  class_id int(11),
  createTime timestamp default current_timestamp,
  primary key (id),
  foreign key (class_id) references class (id)
);

根据旧表创建新表

create table student_copy as select * from student;

15.2 更新表

# 删除 age 列
alter table student 
drop column age;
# 添加 age 列
alter table student 
add column age smallint
# 在某列前插入列 
alter table table_name 
add column column_name 数据类型 AFTER 列名
# 修改某列名字和数据类型
alter table table_name 
change 原来的列名 新列名 数据类型
# 为某列添加默认值
alter table table_name 
modify coulmn 列名 数据类型 default 默认值
# 修改表名
alter table 表名 rename (to) 新表名
# 将某一列放到第一列
alter table 表名 modify column 列名 类型 first

15.3 删除表

drop table if exists student

16.视图

视图是一种虚拟的表,便于更好地在多个表中检索数据,视图也可以作写操作,不过最好作为只读。在需要多个表联结的时候可以使用视图。

  • 重用 SQL 语句。
  • 简化复杂的 SQL 操作。
  • 使用表的一部分而不是整个表。
  • 保护数据。可以授予用户访问表的特定部分的权限,而不是整个表的访问权限。
  • 更改数据格式和表示。
create view v_student_with_classname as
select student.name name, class.name class_name
from student 
left join class
where student.class_id = class.id;
select * 
from v_student_with_classname

因为视图不包含数据,所以每次使用视图时,都必须处理查询执行时需要的所有检索。如果你用多个联结和过滤创建了复杂的视图或者嵌套了视图,性能可能会下降得很厉害。因此,在部署使用了大量视图的应用前,应该进行测试。

17.存储过程

存储过程可以视为一个函数,根据输入执行一系列的 sql 语句。存储过程也可以看做对一系列数据库操作的封装,一定程度上可以提高数据库的安全性。

# 创建存储过程
create procedure create_student(name varchar(50))
begin
  insert into students(name) values (name);
end;

# 调用存储过程
call create_student('shanyue');

18.高级SQL特性

18.1 约束

18.1.1 主键

表中任意列只要满足以下条件,都可以用于主键。

  • 任意两行的主键值都不相同。
  • 每行都具有一个主键值(即列中不允许 NULL 值)。
  • 包含主键值的列从不修改或更新。
  • 主键值不能重用。如果从表中删除某一行,其主键值不分配给新行。

任意两行绝对没有相同的主键,且任一行不会有两个主键且主键绝不为空。使用主键可以加快索引。

# primiry key
alter table student add constraint primary key (id);

18.1.2 外键

外键可以保证数据的完整性,有助防止意外删除。

alter table student add constraint 
foreign key (class_id) references class (id);

18.1.3 唯一约束

唯一约束用来保证一列(或一组列)中的数据是唯一的。它们类似于主键,但存在以下重要区别。

  • 表可包含多个唯一约束,但每个表只允许一个主键。
  • 唯一约束列可包含 NULL 值。
  • 唯一约束列可修改或更新。
  • 唯一约束列的值可重复使用。
  • 与主键不一样,唯一约束不能用来定义外键。
alter table student add constraint unique key (name);

18.1.4 检查约束

检查约束可以使列满足特定的条件,如果学生表中所有的人的年龄都应该大于 0。不过很可惜 mysql 不支持,可以使用触发器代替。

alter table student add constraint check (age > 0);

18.2 索引

索引用来排序数据以加快搜索和排序操作的速度。

  • 索引改善检索操作的性能,但降低了数据插入、修改和删除的性能。在执行这些操作时,DBMS 必须动态地更新索引。
  • 索引数据可能要占用大量的存储空间。
  • 并非所有数据都适合做索引。取值不多的数据(如州)不如具有更多可能值的数据(如姓或名),能通过索引得到那么多的好处。
  • 索引用于数据过滤和数据排序。如果你经常以某种特定的顺序排序数据,则该数据可能适合做索引。
  • 可以在索引中定义多个列(例如,州加上城市)。这样的索引仅在以州加城市的顺序排序时有用。如果想按城市排序,则这种索引没有用处。
create index index_on_student_name on student (name);
alter table student add constraint key(name);

例:现有一张试卷信息表 examination_info,要求如下。

  • duration 列创建普通索引 idx_duration
  • exam_id 列创建唯一性索引 uniq_idx_exam_id
  • tag 列创建全文索引 full_idx_tag
-- 普通索引
ALTER TABLE examination_info
ADD INDEX idx_duration(duration);

-- 唯一索引
ALTER TABLE examination_info
ADD UNIQUE INDEX uniq_idx_exam_id(exam_id);

-- 全文索引
ALTER TABLE examination_info
ADD FULLTEXT INDEX full_idx_tag(tag);

create index idx_duration on examination_info(duration);
create unique index uniq_idx_exam_id on examination_info(exam_id);
create fulltext index full_idx_tag on examination_info(tag);

删除索引

drop index 索引名 on 表名
alter table 表名 drop index 索引名 

18.3 触发器

触发器是特殊的存储过程。可以在插入,更新,删除行的时候触发事件。

与存储过程不一样(存储过程只是简单的存储 SQL 语句),触发器与单个的表相关联。与 Orders 表上的 INSERT 操作相关联的触发器只在 Orders 表中插入行时执行。类似地,Customers 表上的 INSERTUPDATE 操作的触发器只在表上出现这些操作时执行。

触发器内的代码具有以下数据的访问权。

  • INSERT 操作中的所有新数据;
  • UPDATE 操作中的所有新数据和旧数据;
  • DELETE 操作中删除的数据。

下面是触发器的一些常见用途。

  • 保证数据一致。例如,在 INSERTUPDATE 操作中将所有州名转换为大写。
  • 基于某个表的变动在其他表上执行活动。例如,每当更新或删除一行时将审计跟踪记录写入某个日志表。
  • 进行额外的验证并根据需要回退数据。例如,保证某个顾客的可用资金不超限定,如果已经超出,则阻塞插入。
  • 计算计算列的值或更新时间戳。
# 创建触发器
# 比如 mysql 中没有 check 约束,可以使用创建触发器,当插入数据小于 0 时,置为 0。
create trigger reset_age before insert on student for each row
begin
  if NEW.age < 0 then set NEW.age = 0;
  end if;
end;

# 打印触发器列表
show triggers;

一般来说,约束的处理比触发器快,因此在可能的时候,应该尽量使用约束。

文献参考

[1]《SQL必知必会》(第5版) 作者:本·福达
[2] 8000字《SQL必知必会》精华笔记

你可能感兴趣的:(#,数据库,mysql,数据库,sql,数据分析)