一、概述

(一)数据管理技术发展过程

1.数据管理

(1)定义:对数据进行收集、分类、组织、编码、存储、检索和维护一系列活动的总和;

(2)发展过程:人工管理阶段 -> 文件系统阶段 -> 数据库系统阶段

2.人工管理阶段

  • 数据的管理者: 应用程序,数据不保存
  • 数据面向的对象: 某一应用程序
  • 数据的共享程度: 无共享、冗余度极大
  • 数据的独立性: 不独立,完全依赖于程序
  • 数据的结构化: 无结构
  • 数据控制能力: 应用程序自己控制

3.文件系统阶段

  • 数据的管理者: 文件系统,数据可长期保存
  • 数据面向的对象: 某一应用程序
  • 数据的共享程度: 共享性差、冗余度大
  • 数据的结构化: 记录内有结构,整体无结构
  • 数据的独立性: 独立性差,数据逻辑结构改变必须修改应用程序
  • 数据控制能力: 应用程序自己控制

4.数据库系统阶段

(DBMS,独立的软件,位于操作系统之上)

  • 数据结构化
  • 数据的共享性高,冗余度低且易扩充
  • 数据独立性高(应用程序和数据库分离)
  • 数据由 DBMS 统一管理和控制

(二)数据库

1.数据(Data)

  • 数据(Data)是数据库中存储的基本对象
  • 数据的定义:描述事物的符号记录
  • 数据的种类:文本、图形、图像、音频、视频、学生的档案记录、货物的运输情况等
  • 数据的特点:数据与其语义是不可分的

2.数据库(Database)

  • 数据库(Database,简称DB)是长期储存在计算机内有组织的、可共享大量数据的集合

3.数据库管理系统(DBMS)

  • 定义:位于用户和操作系统之间的一层数据管理软件,是数据库和用户之间的一个接口
  • 属性:数据库管理系统和操作系统一样都属于计算机的基础软件,也是一个大型复杂的软件系统。
  • 作用:在数据库建立、运用和维护时对数据库进行统一控制,以保证数据的完整性、安全性,并在多用户同时使用数据库时进行并发控制,在发生故障后对数据库进行恢复。

4.数据库系统(DBS)

​ 数据库系统的构成

  • 数据库
  • 数据库管理系统(及其应用开发工具)
  • 应用程序
  • 数据库管理员

数据库系统

(三)MySQL 安装与配置

1.安装

(1)macOS

(2)Windows

2.连接

(1)终端

1
2
mysql -u root -p
Enter password: 123456

(2)Navicat

(3)DataGrip

(4)VSCode

二、 MySQL 基础

(一)概念

DBMS 数据库管理系统

  • 关系型数据库 SQL
  • 非关系型数据库 noSQL

SQL: Structured Query Language 结构化查询语言

  1. 设定为 主键 的属性可以唯一地表示每一条记录,即用 主键 可以区分不同的记录
  2. 可以通过 外键 将两张表格的记录关联起来,
    • 两张表间关联:表 A 中的 外键 对应到表 B 的 主键
    • 一张表内关联:表A 中的 外键 也可以对应到自己的 主键 ,e.g 主管 id
  3. 主键可以有多个,无论几个,其目的都是通过 一个/组 主键 ,区分不同的记录
  4. 可以同时将某一个属性设置为 主键 和 外键,表示从 A 可以查到 B 的某一条记录,从 B 也可以反过来找到 A 中的对应记录

(二)MySQL 数据类型

  1. 数值

    (1)整数

    数据类型字节数
    TINYINT1
    SMALLINT2
    MEDIUMINT3
    INT4
    BIGINT8

    (2)浮点数

    数据类型字节数
    FLOAT4
    DOUBLE8

    注:float / double(m, n) 带有小数点的数

    m 表示该数字总共有几位数 n 表示小数部分所占的位数

    e.g. float(3, 2) -> 2.33 就是总共有 3 位数,其中小数占了 2 位

    (3)定点数 DECIMAL

    注:DECIMAL(m, n) 带有小数点的数

    m 表示该数字总共有几位数 n 表示小数部分所占的位数

    e.g. DECIMAL(3, 2) -> 2.33

    注:浮点数和定点数的区别

    • float / double 在 db 中存储的是近似值,而 decimal 则是以字符串形式进行保存的。因此,浮点数可能会存在精度丢失的问题,而定点数则不会丢失精度。
    • decimal(m,n) 的规则和 float / double 相同,但区别在 float / double 在不指定 m/n 时默认按照实际精度来处理,而 decimal 在不指定时默认为 decimal(10, 0)。
  2. 日期时间

    数据类型字节数格式备注
    date3yyyy-MM-dd存储日期值
    time3HH:mm:ss存储时分秒
    year1yyyy存储年
    datetime8yyyy-MM-dd HH:mm:ss存储日期+时间
    timestamp4yyyy-MM-dd HH:mm:ss存储日期+时间,时间戳

    注 1 :datetime 和 timestamp 的区别:

    • datetime 占8个字节,timestamp 占4个字节;
    • 由于字节数区别,datetime 与 timestamp 能存储的时间范围也不同,datetime 的存储范围为 1000-01-01 00:00:00 — 9999-12-31 23:59:59,timestamp 存储的时间范围为 19700101080001—20380119111407;
    • datetime 默认值为空,当插入的值为 null 时,该列的值就是 null;timestamp 默认值不为空,当插入的值为 null 的时候,mysql 会取当前时间;
    • datetime 存储的时间与时区无关,timestamp 存储的时间及显示的时间都依赖于当前时区;

    注 2 :通常表格中记录“创建时间”和“修改时间”字段时,用 timestamp

  3. 字符串

    (1)CHAR(n) 定长字符串

    (2)VARCHAR(n) 变长字符串

    (3)TEXT

    (4)BLOB 二进制对象(图片、视频、档案等)

    注:

    • MySql 单行最大数据量为 64K,为了存储大数据,因此创建了 TEXT 和 BLOB 两种类型;

    • TEXT 和 VARCHAR 比较类似,当 varchar(M) 的 M 大于某些数值时,varchar 会自动转为 text:

      • M>255 时转为 tinytext

      • M>500 时转为 text

      • M>20000 时转为 mediumtext

    • varchar(M) 和 text 的区别:

      • 单行 64K 即 65535 字节的空间,varchar 只能用 63352 / 65533 个字节,text 可以用 65535 个字节;

      • text 可以指定 text(M) ,且 M 无限制

      • text 不允许有默认值,varchar 允许有默认值

    • text 和 blob 的区别:text 存储的是字符串而 blob 存储的是二进制字符串。

  4. JSON

  5. 空间

(三)SQL 语句分类

  1. DDL (Data Definition Language) 数据定义语言

    CREATE / DROP / ALTER / TRUNCATE

  2. DML (Data Manipulation Language) 数据操作语言

    INSERT / UPDATE / DELETE / CALL

  3. DQL (Data Query Language) 数据查询语言

    SELECT

  4. DCL (Data Control Language) 数据控制语言

    GRANT / REVOKE

三、常用 SQL 语句

(一)数据库和表操作

  1. 新建数据库

    1
    CREATE DATABASE `sql_tutorial`;
  2. 查看所有数据库

    1
    SHOW DATABASES;
  3. 删除数据库

    1
    DROP DATABASE `sql_tutorial`;
    1
    2
    mysql> drop database test;
    ERROR 1010 (HY000): Error dropping database (can't rmdir './test/', errno: 17)

    注:如果遇到上述无法删除数据库的情况,是因为 test 目录下存在着MySQL 不知道的文件,即 MySQL 中没有该文件的数据字典信息,需要手动删除。

    1
    2
    3
    cd /usr/local/mysql/data/test	# 进入 test 所在位置
    ls # 查看未知文件
    rm -rf xxx # 手动删除

    删除后,再次 DROP 即可。

  4. 选择数据库

    1
    USE `sql_tutorial`;
  5. 创建表格

    1
    2
    3
    4
    5
    6
    CREATE TABLE `student` ( 
    `student_id` INT PRIMARY KEY, # `属性名` 类型 主键,
    `name` VARCHAR ( 20 ),
    `major` VARCHAR ( 20 )
    # PRIMARY KEY(`student_id`) 主键也可以不在属性后注明,也可以在下面单独写
    );
  6. 查看表格

    1
    2
    DESCRIBE `student`;
    DESC `student`;
  7. 删除表格

    1
    DROP TABLE `student`;
  8. 新增表 字段

    1
    ALTER TABLE `student` ADD COLUMN gpa DECIMAL(3,2);
  9. 删除表 字段

    1
    ALTER TABLE `student` DROP COLUMN gpa;
  10. 修改表 字段属性

    1
    ALTER TABLE `student` MODIFY COLUMN gpa FLOAT(3,1);
  11. 修改表 字段名

    1
    ALTER TABLE `student` RENAME COLUMN gpa to Stu_GPA;
  12. 导出数据库

    1
    2
    # 在 Shell 中使用
    mysqldump -u root -p DB (able) > DB.sql
  13. 导入数据库

    1
    2
    # 在 Shell 中使用
    mysql -u root -p DB < DB.sql

(二)数据操作

  1. 查询所有记录

    1
    SELECT * FROM `student`;
  2. 添加记录

    1. 不指定属性

      1
      2
      # 这种方式添加记录,属性的顺序和数量必须全部匹配
      INSERT INTO `student` VALUES(1, '小白', '历史');
    2. 指定属性

      1
      2
      3
      4
      5
      # 可以在表格后添加属性序列,指定输入的属性,如后续值省略,则自动填入默认值
      INSERT INTO `student`(`name`, `major`, `student_id`) VALUES('小蓝', '英语', 4);

      # 插入多条数据
      INSERT INTO `student`(`name`, `student_id`) VALUES('小红', 5),('小绿', 6),('小黄', 7);
  3. 常用约束

    1
    2
    3
    4
    5
    6
    7
    CREATE TABLE `student`
    (
    `student_id` INT PRIMARY KEY AUTO_INCREMENT,
    `name` VARCHAR(20) NOT NULL, # 非空
    `major` VARCHAR(20) UNIQUE, # 唯一
    `sex` VARCHAR(10) DEFAULT '男' # 默认值,通常应用在指定输入属性时
    );
  4. 更新记录

    1
    2
    3
    UPDATE `student`
    SET `major` = 'English', `score` = 98
    WHERE `major` = 'Math' OR `major` = 'Chinese';
  5. 删除记录

    1
    2
    DELETE FROM `student`
    WHERE `student_id` = 4; # 不加条件,则删除所有记录
  6. 查询

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    SELECT 属性1, 属性2
    FROM
    WHERE 属性1 = '特定值' AND / OR 属性2 <> '特定值'
    # 链接符优先级 NOT > AND > OR,可以用括号改变优先级顺序
    # 增加筛选条件( <> 不等于 )
    # 同一属性 a 判断多个条件并使用 OR 连接时,可以用: 属性 IN ('', '', '')
    # 属性在某个区间内取值时,可以用: BETWEEN a AND b
    GROUP BY 分组依据
    HAVING 分组后过滤的依据
    ORDER BY 排序依据1, 排序依据2 DESC
    # 默认升序 ASC ,添加 DESC 变为降序
    # 先按照依据1排序,有相同的再按照依据2排序
    LIMIT n / a,b
    # 如果只写一个数字 n 则返回前 n 条记录
    # 如果写 a,b 则返回 从 a+1 条记录开始的 b 条记录

注:

  1. 判断是否为空用 IS NULL / IS NOT NULL
  2. 计算字符串中字符数的最佳函数是 CHAR_LENGTH(str),它返回字符串 str 的长度;而函数 LENGTH(str) 返回字符串 str 的字节数,某些字符包含多于 1 个字节,可能导致返回结果错误。
  3. DISTINCT + 属性 可用于去重;

(三)进阶操作

  1. 聚合函数

    聚合函数用于对某些列进行一些计算,包括求和、计数、求平均等。

    • COUNT( ) 返回集合中的项目数;

    • AVG( ) 返回集合的平均数;

    • SUM( ) 求和;

    • ROUND( 对象, 小数位数 ) 四舍五入保留小数;

    • MIN( ) 最小值;

    • MAX( ) 最大值;

  2. 通配符

    1
    2
    3
    属性 (NOT) LIKE '通配符';
    # % 表示任意长度的字符串
    # _ 表示任意单个字符
  3. 正则表达式

    1
    2
    3
    4
    5
    6
    属性 REGEXP '正则表达式';
    # ^ 开头 $ 结尾
    # . 任意一个字符
    # [abc] 其中任意一个字符
    # [a-z] 范围内任意一个字符
    # A|B A 或 B
  4. 集合运算

    • UNION 并集

      将两个结果合并为一个结果返回,后者直接接在前者最后;

      1
      2
      3
      SELECT 属性1 FROM1
      UNION
      SELECT 属性2 FROM2;

      注:

      UNION 默认会去除两条 select 中得到的重复记录;如果要求不去重,则可以使用 UNION ALL。

    • INTERSECT 交集

      将两个结果取交集后返回;

      1
      2
      3
      SELECT 属性1 FROM1
      INTERSECT
      SELECT 属性2 FROM2;
    • EXCEPT 差集

      将两个结果取差集后返回,查找满足第一条语句但不满足第二条语句的记录;

      1
      2
      3
      SELECT 属性1 FROM1
      EXCEPT
      SELECT 属性2 FROM2;
  5. 连接查询 JOIN

    连接查询,将后者拼接在前者表的右边

    (1)外连接

    • LEFT JOIN(左连接)以左表为主,右表中没有的数据用 NULL 填充;
    • RIGHT JOIN(右连接)以右表为主,左表中没有的数据用 NULL填充;
    • FULL (OUTER) JOIN(全连接)两边连接,没有的都用 FULL 填充 并集

    (2)内连接

    • INNER JOIN (内连接)只返回两个表中都有的数据 交集
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    SELECT 属性1, 属性2, 属性3 FROM1 
    INNER JOIN2
    ON1.属性a =2.属性b;

    # INNER JOIN 内连接 也可以用 WHERE 的方式实现
    # INNER JOIN
    SELECT * FROM player
    INNER join equip
    on player.id = equip.player_id;

    # WHERE
    select * from player, equip
    where player.id = equip.player_id;

    (3)交叉连接

    • (CROSS) JOIN (交叉连接)相当于取两个表的***笛卡尔积***

      注:在 MySQL 中,如果不指定 ON 条件,则 CROSS JOIN 与 INNER JOIN 的结果是一样的,都是两张表的笛卡尔积。

    1
    2
    3
    SELECT 属性1, 属性2, 属性3 FROM1 
    (CROSS) JOIN2
    ON1.属性a =2.属性b;
  6. 子查询 / 嵌套查询

    (1)一个查询嵌套在另一个查询中

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    # 查询对一位客户销售金额超过 50000 的员工姓名
    SELECT Employee.name, Works_with.total_sales
    FROM Employee LEFT JOIN Works_with
    ON Employee.emp_id = Works_with.emp_id
    WHERE Employee.emp_id IN
    (
    SELECT Works_with.emp_id
    FROM Works_with
    WHERE Works_with.total_sales >= 50000
    );

    # 注: 这里内层的查询结果不止一个,所以不能用 = 连接内外查询,要用 IN 连接内外查询

    (2)用子查询结果创建新表格

    1
    2
    3
    4
    5
    # 查询所有等级小于 5 级的玩家,并将结果保存到新的表格中
    CREATE TABLE new_player SELECT * FROM player WHERE level < 5;

    # 查询所有等级在 6-10 级的玩家,并将结果插入到 new_player 中
    INSERT INTO new_player SELECT * FROM player WHERE level BETWEEN 6 AND 10;
  7. 索引 INDEX

    通常对一张表格的主键字段或常用字段创建索引,从而提高后续的查询效率。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    # 在指定表(的某些字段)上创建 唯一/全文/空间 索引
    CREATE [UNIQUE|FULLTEXT|SPATIAL] INDEX 索引名称
    ON 表名 (字段名称, ...);

    # 查看索引
    SHOW INDEX FROM 表名;

    # 删除索引
    DROP INDEX 索引名称
    ON 表;

(四)窗口函数

  1. 概念

    窗口函数是一种特殊类型的 SQL 函数,它在查询结果集中的一定窗口(或称为窗口框架)上执行计算操作。

    这个窗口是通过使用 OVER 子句定义的,它指定了在进行聚合、排序或分析等操作时应考虑的行集合。

    窗口函数通常与聚合函数一起使用,但与普通的聚合函数不同,它不会将整个结果集作为输入,而是基于定义的窗口框架对子集进行计算。这使得在不引入子查询或自连接的情况下,能够在每一行上执行对整个结果集的聚合或分析操作。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    SELECT
    column1,
    column2,
    SUM(column3) OVER (PARTITION BY column1 # 按 column1 分组
    ORDER BY column2 # 按 column2 排序
    ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING # 当前行及其前后各一行
    ) AS running_total
    FROM
    your_table;

    # SUM 是一个聚合函数,但它通过OVER子句指定了一个窗口框架。
    # 在这个例子中,窗口由 PARTITION BY column1 定义,然后使用 ORDER BY column2 排序。
    # ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING指定了窗口的范围,表示当前行及其前后各一行。
    # 这样,SUM函数将在每个窗口内计算列column3的累计和。
  2. 聚合函数

    示例数据

    ![截屏2023-12-20 21.15.22](/Users/angfff/Library/Application Support/typora-user-images/截屏2023-12-20 21.15.22.png)

    • SUM

      1
      2
      3
      4
      5
      6
      SELECT *,
      SUM(Salary) OVER(PARTITION BY GroupName) AS 部门工资总和, # 按照部门分组,计算部门工资总和
      SUM(Salary) OVER(PARTITION BY GroupName ORDER BY ID) AS 累加_部门工资总和, # 按照部门分组,再按组内 ID 依次累加部门工资总和
      SUM(Salary) OVER(ORDER BY ID) AS 累加_全体工资总和, # 按所有人的 ID 依次累加工资总和
      SUM(Salary) OVER() AS 全体工资总和 # 窗口为空,直接计算所有员工工资总和 等同于 SUM()
      FROM Emp;

      ![截屏2023-12-20 21.13.50](/Users/angfff/Library/Application Support/typora-user-images/截屏2023-12-20 21.13.50.png)

    • MAX

    • MIN

    • COUNT

      1
      2
      3
      4
      5
      6
      SELECT *,
      COUNT(*) OVER(PARTITION BY GroupName) AS 部门人数,
      COUNT(*) OVER(PARTITION BY GroupName ORDER BY ID) AS 累加_部门人数,
      COUNT(*) OVER(ORDER BY ID) AS 累加_全体人数,
      COUNT(*) OVER() AS 总人数
      FROM Emp;

      ![截屏2023-12-20 21.14.46](/Users/angfff/Library/Application Support/typora-user-images/截屏2023-12-20 21.14.46.png)

    • AVG

  3. 排序函数

    示例数据

    ![截屏2023-12-20 21.16.26](/Users/angfff/Library/Application Support/typora-user-images/截屏2023-12-20 21.16.26.png)

    • ROW_NUMBER 将 SELECT 得到的数据进行排序,必须与 ORDER BY 连用,常用于分页查询;

      1
      2
      3
      4
      SELECT *,
      ROW_NUMBER() over (PARTITION BY ClassName ORDER BY Score DESC) AS 班内排名, # 按照班级分组,再按照分数降序排列,给每行一个排名
      ROW_NUMBER() over (ORDER BY Score DESC) AS 全体排名 # 按照分数降序排列,给每行一个排名
      FROM SCO;

      ![截屏2023-12-20 21.16.59](/Users/angfff/Library/Application Support/typora-user-images/截屏2023-12-20 21.16.59.png)

    • RANK 与 ROW_NUMBER 类似,但是允许并列排名,即分数相同时,排名也相同,后续跳过被占用的名次;

      1
      2
      3
      # ROW_NUMBER
      SELECT *, ROW_NUMBER() OVER (ORDER BY SCO.Score DESC) AS 总排名
      FROM SCO; # 不能并列排名

      ![截屏2023-12-20 21.18.28](/Users/angfff/Library/Application Support/typora-user-images/截屏2023-12-20 21.18.28.png)

      1
      2
      3
      # RANK
      SELECT *, RANK() over (ORDER BY SCO.Score DESC) AS 总排名
      FROM SCO; # 可以并列,且后续跳过被占用的排名

      ![截屏2023-12-20 21.18.40](/Users/angfff/Library/Application Support/typora-user-images/截屏2023-12-20 21.18.40.png)

    • DENSE_RANK 与 RANK 类似,允许并列排名,但后续不会跳过被占用的名次。

      1
      2
      3
      # DENSE_RANK
      SELECT *, DENSE_RANK() over (order by SCO.Score DESC ) AS 总排名
      FROM SCO; # 可以并列,且后续不跳过被占用的排名

      ![截屏2023-12-20 21.19.55](/Users/angfff/Library/Application Support/typora-user-images/截屏2023-12-20 21.19.55.png)

  4. 取值函数

    (1)向前向后取数

    • lag( ) 返回窗口内当前行之前的第 N 行数据
    • lead( ) 返回窗口内当前行之后的第 N 行数据

    (2)First_value & Last_value

    • First_value 返回窗口内第一行数据,可以用 Min 聚合函数替代
    • Last_value 返回窗口内最后一行数据,可以用 Max 聚合函数替代

(五)函数

  1. substr( 属性 a, begin, n ) 截取属性 a 从 begin 位置开始的 n 个字符,e.g. 查找姓氏 substr(name, 1, 1);
  2. datediff( 日期 1, 日期 2 ) 前减后,得到的结果是日期 1 与日期 2 相差的天数。如果日期 1 比日期 2 大,结果为正;如果日期 1 比日期 2 小,结果为负。
  3. timestampdiff(时间类型, 日期1, 日期2) 后减前在“时间类型”的参数位置,通过添加“day”, “hour”, “second”等关键词,来规定计算天数差、小时数差、还是分钟数差。日期 1 大于日期 2 ,结果为负,日期 1 小于日期 2 ,结果为正。
  4. IFNULL( 判断对象 A, 替换值 N ) 如果判断对象 A 的值为 NULL,则返回替换值 N。
  5. mod( 操作对象 A, 取模数 n ) = x 判断操作对象 A 对 n 取模的结果是否为 x,等价于 A % n = x。

(六)视图

  1. 创建视图的目的是将查询结果保存为一张虚拟的表,当后续需要使用该查询结果时,就可以直接从视图中调用,做进一步的查询。
  2. 视图是动态变化的,随着原表的修改而修改。
1
2
3
4
5
6
7
8
9
10
11
12
# 创建视图
CREATE VIEW 视图名称
AS
SELECT * FROM 表名;

# 修改视图
ALTER VIEW 视图名称
AS
SELECT * FROM 表名;

# 删除视图
DROP VIEW 视图名称;