MySQL学习笔记老杜版


MySQL学习笔记老杜版

MySQL[8.18]

DAY01

数据库概述

  1. 什么是数据库?什么是数据库管理系统?什么是SQL?他们之间的关系是什么?

    • **数据库(DB)**:按照一定格式存储数据的一些文件的组合。存储数据的仓库,实际上就是一堆文件。这些文件中存储了具有特定格式的数据。

    • 数据库管理系统(DBMS):数据库管理系统是专门用来管理数据库中数据的,数据库管理系统可以对数据库当中的数据进行增删查改。

      –> 常见的数据库管理系统:

      MySQL、Oracle、MS、SqlServer、DB2、sybase等。。。

    • SQL(结构化查询语言):程序员需要学习SQL语句,程序员通过编写SQL语句,然后DBMS负责执行SQL语句,最终来完成数据库中数据的增删改查操作。

    • DBAS(数据库应用程序):在数据库管理系统的基础上,使用数据库管理系统的语法,开发的直接面对最终用户的应用程序。

    • DBA(数据库管理员):数据库管理者

    • 数据库系统 = 数据库 + 数据库管理系统 + 数据库应用程序 + 最终用户

  2. 数据库的发展阶段

    • 网状数据库

    • 层次数据库

    • 关系数据库

    • 采用关系【二维表】结构来存储和管理数据。

    • 采用结构化查询语言(sql) 作为客户端数据库服务器间沟通的桥梁。

  3. 三者之间的关系?

    • DBMS –> 执行 –> SQL –> 操作 –> DB*
  4. 查看计算机上的服务,查看MySQL的服务在哪儿?

    • 计算机 –> 右键 –> 服务和应用程序 –> 服务 –> 找mysql服务
    • MySQL的服务,默认是“启动”的状态,只有启动了mysql才能使用。
    • 默认情况下是“自动”启动,自动启动表示下一次重启操作系统的时候自动启动该服务。
    • 可以在服务上右击:启动、重启服务、停止服务…
    • 还可以改变服务的默认配置:服务上点击右键,属性,然后可以选择启动方式:自动(延迟启动)、自动、手动、禁用
  5. 在Windows操作系统中,怎么使用命令来启动和关闭mysql服务呢?

    -

    net stop 服务名称;
    net start 服务名称;

    –> 其它服务的启停都可以采用以上的命令。

  6. mysql安装后,服务启动后,怎么使用客户端登录mysql数据库?

    • 使用bin目录下的mysql.exe命令来连接mysql数据库服务器

    • 本地登录(显示编写密码的形式):

      mysql -uroot -p123456 #-u --> 用户 | -p --> 密码
    • 隐式编写密码:

      mysql -uroot -p
      ******
  7. mysql常用命令:

    exit  #退出mysql
    show databases;  #查看mysql中有哪些数据库。注意:以分号结尾,分号是英文的分号
    use XXX;  #使用某个数据库  --> mysql默认自带四个数据库
    Create database XXX;  #创建某个数据库
    show tables; #查看某个数据库下有哪些表
    select version();  #查看当前数据库的版本号
    select database();  #查看当前使用的是那个数据库
    \c  #用来终止一条命令的输入

    注意:mysql不见”;”不执行,”;”代表结束,可以换行写。

  8. 数据库中最基本的单元是表:table

    • 为什么用表来存储数据呢?

      –> 数据库中是以表格的形式表示数据的,表比较直观。

    • 任何一张表都有行和列:

      • 行(row):被称为数据 / 记录。
      • 列(column):被称为字段。
    • 每一个字段都有:字段名、数据类型、约束等属性。

    • 约束:约束也有很多,其中一个叫做唯一性约束,这种约束添加之后,该字段中的数据不能重复。

  9. 关于SQL语句的分类

    • DQL:数据查询语言(凡是带有select关键字的都是查询语句)

      –> slect…

    • DML:数据操作语言(凡是对表当中的数据进行增删改的都是DML)

      insert  #增
      delete  #删
      update  #改

      这个主要是操作表中的数据data。

    • DDL:数据定义语言(凡是带有create\drop\alter的都是DDL)

    • DDL主要操作的是表的结构,不是表中的数据。

      create  #新建,等同于增
      drop  #删除
      alter  #修改

      这个增删改和DML不同,这个主要是对表结构进行操作。

    • TCL:事务控制语言(包括:事务提交 commit;事物回滚 rollback)

    • DCL:数据控制语言(授权 grant;撤销权限 revoke…)

  10. 导入数据库表

source #绝对路径

注意:路径不要有中文!

  1. 怎么查看表中的数据?

    select * from 表名;  #从XX表中查询所有数据; * --> 表示所有
  2. 不看表中的数据,只看表的结构。

    desc 表名;

    –> varchar:就是java中的String


默认几个库的简单说明

  1. information_schema

    • information_schema数据库是MySQL自带的,它提供了访问数据库元数据的方式。什么是元数据呢?元数据是关于数据的数据,如数据库名或表名,列的数据类型,或访问权限等。有些时候用于表述该信息的其他术语包括“数据词典”和“系统目录”。
    • 在MySQL中,把 information_schema 看作是一个数据库,确切说是信息数据库。其中保存着关于MySQL服务器所维护的所有其他数据库的信息。如数据库名,数据库的表,表栏的数据类型与访问权 限等。在INFORMATION_SCHEMA中,有数个只读表。它们实际上是视图,而不是基本表,因此,你将无法看到与之相关的任何文件
  2. mysql

    • 这个是mysql的核心数据库,主要负责存储数据库的用户、权限设置、关键字等mysql自己需要使用的控制和管理信息。不可以删除,如果对mysql不是很了解,也不要轻易修改这个数据库里面的表信息。
  3. performance_schema

    • mysql 5.5 版本 新增了一个性能优化的引擎: PERFORMANCE_SCHEMA这个功能默认是关闭的:
    • 需要设置参数: performance_schema 才可以启动该功能,这个参数是静态参数,只能写在my.cnf 中 不能动态修改。
    • windows里面叫 my.ini文件 C:\ProgramData\MySQL\MySQL Server 5.7 【默认在这个目录】linux里面叫my.cnf文件
  4. sys

    • 通过这个库可以快速的了解系统的元数据信息

      这个库确实可以方便DBA发现数据库的很多信息,解决性能瓶颈都提供了巨大帮助

  5. :以上几个库都不能删除(重点)


创建数据库

  1. 语法:

    -- 创建数据库有两种方式:
    -- 第一种 语法如下
    -- 注意:<>里面的内容都是可变的
    -- create database <数据库名称>;
    -- 例如
    create database whpowernode;
    -- 第二种 语法如下
    -- if not exists 创建之前会做check操作,如果数据库名称在MySQL数据库管理系统中不存在才创建
    -- charset default 为数据库指定默认编码
    -- create database if not exists <数据库名称>  default charset  <数据库字符集编码>;
    -- 例如:
    create database if not exists whpowernode default charset UTF8;
    create database if not exists whpowernode default charset UTF8MB4;
    -- UTF8和UTF8MB4区别?
    -- 绝大多数情况下使用UTF8编码的字符集一个汉字占据3个字符,但是有极少数情况下一个汉字占据4个字符
    -- 占据4个字符的汉字不能使用UTF8存储,必须使用UTF8MB4存储
    -- UTF8MB4字符集编码表示一个汉字最多占据4个字符  M Most  B Byte 
    UTF8MB4 它可以存储表情符号
    -- 注意:创建数据库指定的字符集编码必须跟MySQL数据库里面的my.ini字符集编码保持一致
  2. 修改数据库密码

    1使用mysql数据库
    use mysql;
    
    2修改密码命令(将root用户的密码修改为123456)
    update mysql.user set authentication_string=PASSWORD('123456') where user='root;
    
    3刷新
    flush privileges;

创建用户

  1. 语法:

    CREATE USER 'username'@'host' IDENTIFIED BY 'password';
    • 说明:

      • username:你将创建的用户名
      • host:指定该用户在哪个主机上可以登陆,如果是本地用户可用localhost,如果想让该用户可以从任意远程主机登陆,可以使用通配符%
      • password:该用户的登陆密码,密码可以为空,如果为空则该用户可以不需要密码登陆服务器
    • eg:

      #创建dog用户密码为123456 并且限制只能在本机上登录访问
      CREATE USER 'dog'@'localhost' IDENTIFIED BY '123456';
      
      #创建pig用户密码为123456 并且限制只能在指定的ip(192.168.1.101)登录访问
      CREATE USER 'pig'@'192.168.1.101_' IDENDIFIED BY '123456';
      
      #创建pig用户密码为123456 并且可以通过任意远程主机登录访问
      CREATE USER 'pig'@'%' IDENTIFIED BY '123456';
      
      #创建pig用户输入任意密码即可登录并且可以通过任意远程主机登录访问
      CREATE USER 'pig'@'%' IDENTIFIED BY '';
      
      #创建pig用户没有密码并且可以通过任意远程主机登录访问
      CREATE USER 'pig'@'%';

给用户授权

  1. 语法:

    GRANT privileges ON databasename.tablename TO 'username'@'host'
    • 说明:

      • privileges:用户的操作权限,如SELECT,INSERT,UPDATE等,如果要授予所的权限则使用ALL
      • databasename:数据库名
      • tablename:表名,如果要授予该用户对所有数据库和表的相应操作权限则可用 * 表示。
    • eg:

      #给pig用户授权 给 test库里面 的user表 查询和插入权限并且可以通过任意主机访问
      GRANT SELECT, INSERT ON test.user TO 'pig'@'%';
      
      #给pig用户授权 给所有库的所有表 增删改查的权限并且可以通过任意主机访问
      GRANT ALL ON *.* TO 'pig'@'%'; 
      
      #给pig用户授权 给 maindataplus库里面 的所有表 增删改查权限并且可以通过任意主机访问
      GRANT ALL ON maindataplus.* TO 'pig'@'%';
  2. 注:用以上命令授权的用户不能给其它用户授权,如果想让该用户可以授权,用以下命令

    GRANT privileges ON databasename.tablename TO 'username'@'host' WITH GRANT OPTION;

DDL数据定义语言

  • 全称Data Definition Language,主要用于创建和删除结构。
    1. 创建结构相关操作:表、视图、索引、数据
    2. 删除结构相关操作:表、视图、索引、数据
    3. 改变结构相关操作:表、视图、索引、数据

DML数据操纵语言

  • DML对表和视图进行插入、删除、更新相关操作,全称Data Manipulation Language。
    1. 插入数据关键字:insert
    2. 删除数据关键字:delete
    3. 修改数据关键字:update

DQL数据查询语言

  • 对表和视图进行操作,全称: Data Query Language
    1. DQL关键字 select

TCL事务控制语言

  • TCL全称Transaction Control Language,使用事务管理DML操作。
    1. 提交事务关键字 commit;
    2. 回滚事务关键字 rollback;
    3. 开启事务关键字 start transaction;

MySQL数据类型

整数类型

数据类型 大小 描述
tinyint 8bit 范围(-128~127)
smallint 16bit 范围(-32768~32767)
mediumint 24bit 范围(-8388608~8388607)
int 32bit 范围(-2147483648~2147483647)==(对应java中的类型int 或 Integer 记住)==
bigint 64bit 范围(+-9.22*10的18次方)==(对应java中的类型long或 Long记住)==

小数类型

数据类型 大小 描述
float(m,d) 32bit 单精度浮点型,m总个数,d小数位
double(m,d) 64bit 双精度浮点型,m总个数,d小数位

定点数

  • 浮点型在数据库中存放的是近似值,而定点类型在数据库中存放的是精确值。 decimal(m,d) 参数m是总个数,d是小数位。

字符串

数据类型 含义
char(n) 固定长度,最多255个字符,索引效率级高,必须在括号里定义长度,可以有默认值
varchar(n) 可变长度,最多65535个字符==(对应java中的类型String或 char记住)==
tinytext 可变长度,最多255个字符
text 可变长度,最多65535个字符
mediumtext 可变长度,最多2的24次方-1个字符
longtext 可变长度,最多2的32次方-1个字符
json mysql5.7新增加的数据类型,用来存放json数据
  • char、varchar、text区别 (理解)
    • char:存储定长数据很方便,CHAR字段上的索引效率级高,必须在括号里定义长度,可以有默认值,比如定义char(10),那么不论你存储的数据是否达到了10个字节,都要占去10个字节的空间(自动用空格填充),且在检索的时候后面的空格会隐藏掉,所以检索出来的数据需要记得用什么trim之类的函数去过滤空格。
    • varchar:存储变长数据,但存储效率没有CHAR高,必须在括号里定义长度,可以有默认值。保存数据的时候,不进行空格自动填充,而且如果数据存在空格时,当值保存和检索时尾部的空格仍会保留。另外,varchar类型的实际长度是它的值的实际长度+1,这一个字节用于保存实际使用了多大的长度。
    • text:存储可变长度的非Unicode数据,最大长度为2^31-1个字符。text列不能有默认值,存储或检索过程中,不存在大小写转换,后面如果指定长度,不会报错误,但是这个长度是不起作用的,意思就是你插入数据的时候,超过你指定的长度还是可以正常插入。
  • 实战结论:
    1. 经常变化的字段用varchar;
    2. 知道固定长度的用char;
    3. 超过255字节的只能用varchar或者text;
    4. 能用varchar的地方不用text;
    5. 能够用数字类型的字段尽量选择数字类型而不用字符串类型,这会降低查询和连接的性能,并会增加存储开销。这是因为引擎在处理查询和连接回逐个比较字符串中每一个字符,而对于数字型而言只需要比较一次就够了;
    6. 同一张表出现多个大字段,能合并时尽量合并,不能合并时考虑分表

时间日期类

数据类型 含义
date 日期 ‘2021-1-1’(对应java中的类Date 记住)
time 时间 ‘12:25:36’
datetime 日期时间 ‘2021-1-1 22:06:44’ (对应java中的类Date 记住)
timestamp 自动存储记录修改时间

逻辑类型

  • (适用于:性别、状态、标记等只有两个值的)
    • Logic类型使用bit表示,它只能存储两个值0或者1

数据类型的属性

关键字 含义
NULL 数据列可包含NULL值
NOT NULL 数据列不允许包含NULL值
DEFAULT 默认值
PRIMARY KEY 主键
AUTO_INCREMENT 自动递增,适用于整数类型
UNSIGNED 无符号
CHARACTER SET name 指定一个字符集

简单查询

  1. 查询一个字段

    select 字段名 from 表名;

    注意:select 和 from都是关键字。

    ​ 字段名和表明都是标识符。

    强调:

    ​ 对于SQL语句来说,是通用的,所有的SQL语句以”;”结尾。

    ​ 另外SQL语句不区分大小写,都可以。

  2. 查询两个字段或多个字段

    • 使用 “,” 隔开
  3. 查询所有字段

    • 第一种方式:可以把每个字段都写上

      select a,b,c...from tablename;
    • 第二种方式:可以使用 *

      select * from tablename;

      这种方式的缺点:

      1. 效率低;
      2. 可读性差;
      3. 在实际开发中不建议,自己想在dos窗口中快速看一看全表数据可以采用这种方式。
  4. 给查询的列起别名

    select deptno,dname as deptname from dept;

    使用as关键字起别名。

    • 注意:只是将显示的查询结果列名显示为deptname,原表列名还是叫:dname

    • 记住:select语句是永远都不会进行修改操作的。(因为只负责查询)

    • as 关键字可以省略:

      select deptno,dname deptname from dept;

    假设起别名的时候,名名里面有空格,怎么办?

    select deptno,dname dept name from dept;
    • DBMS看到这样的语句,进行SQL语句的编译,不符合语法,编译报错。

    • 解决方案:【双引号也可以】

      select deptno,dname 'dept name' from dept;

      注意:在所有的数据库当中,字符串统一使用单引号括起来,单引号是标准,双引号在oracle数据库中用不了。但是在mysql中可以使用。【单引号是标准,双引号不是标准】

  5. 计算员工的年薪 【sal * 12】

    select ename, sal * 12 as '年薪' from emp;
    • 字段可以使用数学表达式;
    • 别名是中文,用单引号括起来。

条件查询

  1. 什么是条件查询?

    • 不是将表中所有数据都查出来,是查询出来符合条件的。

    • 语法格式:

      select 
      	字段,字段...
      		from
      			表名
      		where
      			条件;
  2. 条件举例

    • = 等于:eg –> 查询薪资等于800的员工姓名和编号

      select ename,empno from emp where sal = 800;
    • <> 或 != 不等于:eg –> 查询薪资不等于800的员工姓名和编号

      select ename,empno from emp where sal <> 800;
    • **between … and … 两个值之间,等同于 >= and <=** :eg –> 查询薪资在2450和3000之间的员工信息,包括2450和3000

      select * from emp where sal between 2450 and 3000;  #第一种方式
      select * from emp where sal >= 2450 and sal <= 3000;  #第二种方式

      注意:使用between and 的时候,必须遵循左小右大。between and 是闭区间,包括两端的值。

    • is null 为null(is not null 不为空):eg –> 查询哪些员工的津贴 / 补助为Null?

      select * from emp where comm is null;  #is null
      select * from emp where comm is not null;  #is not null

      注意:在数据库当中null不能使用等号进行衡量。需要使用is null,因为数据库中的null代表什么也没有,它不是一个值,所以不能使用等号衡量。【0 和 null 是两个不同的概念】

    • and 并且:eg –> 查询工作岗位是MANAGER并且工资大于2500的员工信息

      select * from emp where job = 'manager' and sal > 2500;
    • or 或者:查询工作岗位是MANAGER和SALESMAN的员工

      select * from emp where job = 'manager' or job = 'salesman';
    • and 和 or同时出现的话,有优先级问题吗?

      • eg:查询工资大于2500,并且部门编号为10或20部门的员工
      select * from emp where sal > 2500 and deptno = 10 or deptno = 20;

      分析以上语句的问题:

      • and 优先级比 or 高;

      • 以上语句会先执行and,然后执行or;

      • 以上这个语句表示什么含义?

        –> 找出工资大于2500并且部门编号为10的员工,或者20部门所有员工找出来。

      • 正确写法:

      select * from emp where sal > 500 and (deptno = 10 or deptno = 20);

      and 和 or 同时出现,and 优先级较高。如果想让 or 先执行,需要加”小括号”,以后在开发中,如果不确定优先级,就加小括号就行了。

    • in 包含,相当于多个 or (not in 不在这个范围中):eg –> 查询工作岗位是MANAGER和SALESMAN的员工

      select * from emp where job in('manager','salesman');  #in
      
      # 表示不在这几个值当中的数据
      select * from emp where job not in('manager','salesman');  #not in

      注意:in 不是一个区间,in 后面跟的是具体的值。

    • not 可以取非,主要用在 is 或 in 中

      is null
      is not null
      in
      not in
    • like 称为模糊查询,支持 % 或下划线匹配。

      • % 匹配任意个字符。

      • _ 下划线:一个下划线只匹配一个字符。

      • eg:

        #找出名字中含有o的
        select * from emp where ename like '%o%';
        #找出名字以T结尾的
        select * from emp where ename like '%T';
        #找出第二个字母是A的
        select * from emp where ename like '_A%';
        #找出第三个字母是R的
        select * from emp where ename like '__R%';
      • 找出名字中有”_”的?

        # select * from emp where ename like '%\_%'; --> 错误的 _ 默认是匹配单个任意字符
        select * from emp where ename like '%\_%';  # --> 正确
        # \ 转义字符

select语句加条件的作用

  • 加条件只是为了避免笛卡尔积现象,只是为了查询出有效的组合记录。匹配的次数一次都没有少。

排序

  1. sql 语句默认是升序的。

    select ename,sal from emp order by sal;  #默认是升序
  2. desc 降序。

    select ename,sal from emp order by sal desc;  #降序
  3. asc 指定升序

    select ename,sal from emp order by sal asc;  #指定升序
  4. 多个字段排序

    #查询员工名字和薪资,要求按照薪资升序,如果薪资一样的话再按照名字升序排序
     select ename,sal from emp order by sal asc, ename desc;
     # sal 在前,起主导,只有 sal 相等的时候,才会考虑启用 ename 排序
  5. 了解:根据字段的位置也可以排序

    select ename,sal from emp order by 2; # 2 表示第二列,第二列是 sal
    # 按照查询结果的第2列排序。

    了解一下,不建议在开发中这样写,因为不健壮。因为列的顺序很容易发生改变,列顺序修改之后,2 就没用了。

  6. 综合案例:

    • 找出工资在1250到3000之间的员工信息,要求按照薪资降序排序
    select * from emp where sal between 1250 and 3000 order by sal desc;
    • 关键字顺序不能变

      select
      	...
      	from
      		...
      		where
      			...
      			order by  # 排序语句总是在最后执行
      				...
    • 以上语句的执行顺序必须掌握

      1. 第一步:from
      2. 第二步:where
      3. 第三步:select
      4. order by(排序总是在最后执行)

数据处理函数【单行处理函数】

  1. 数据处理函数又被称为单行处理函数

    • 单行处理函数的特点:一个输入对应一个输出。
    • 多行处理函数的特点:多个输入,对应1个输出!(和单行处理函数相对)。
  2. 单行处理函数常见的有哪些?

    • Lower 转换小写:

      select lower(ename) as ename from emp;

      –> eg:14个输入,最后还是14个输出。这就是单行处理函数的特点。

    • upper 转换大写:

      select upper(sname) as name from t_student;
    • substr 取子串【 substr (被截取的字符串, 起始下标, 截取的长度) 】:

      select substr(ename,1,1) as name from emp;

      注意:起始下标从 1 开始,没有 0;

      eg:找出员工名字第一个字母是A的员工信息

      select * from emp where ename like 'A%';  # 第一种方式
      select * from emp where substr(ename,1,1) = 'A';  # 第二种方式
    • concat() 函数进行字符串的拼接

      eg:首字母大写

      select concat(upper(substr(sname,1,1)),substr(sname,2,length(sname)-1)) from t_student;
    • length 取长度:

      select length(ename) enamelength from emp;
    • trim 去空格:

      select * from emp where ename = trim('   KING');
    • str_to_date 将字符串varchar类型转换成date类型:

      –> 语法格式:

      str_to_date('字符日期','日期格式')
      # mysql的日期格式
      
      %Y 年
      %m 月
      %d 日
      %h 时
      %i 分
      %s 秒

      str_to_date函数通常使用在插入insert方面,因为插入的时候需要一个日期类型的数据,需要通过该函数将字符串转换成date。

      # eg:
      insert into t_user(id,name,birth) values(1,'zhangsan',str_to_date('01-10-1990','%d-%m-%Y'));

      好消息:

      ​ 如果提供的日期字符串时这个格式,str_to_date函数就不需要了!!!

      %Y-%m-%d

      insert into t_user(id,name,birth) values(2,'lisi','1990-10-01');
    • date_ format 将date数据类型转换成具有一定格式的varchar字符串类型:

    • format 设置千分位:

    • case…when…then…when…then…else…end

    • md5(str): 为字符串算出一个MD5 128比特校验和,改值以32位十六进制数字的二进制字符串形式返回

      不可逆,对同一明文,加密字符串相同。可以采用加密两次的方式将用户在客户端输入的密码在数据库中找到对应的密文。

      select md5(123456) from dual;
    • round 四舍五入:

      select round(1236.5315,0) as result from emp;  #保留到整数位
      select round(1236.5315,1) as result from emp;  #保留到1个小数
      select round(1236.5315,2) as result from emp;  #保留到2个小数
      select round(1236.5315,-1) as result from emp;  #保留到十位  --> 1240
      #  -2 / -3...以此类推
    • rand() 生成随机数:

      eg:100以内的随机数

      select round(rand()*100,0) as result from emp;

    • ifnull 可以将 null 转换成一个具体值:

      ifnull 是空处理函数。专门处理空的。

      在所有数据库当中,只要有 NULL 参与的数学运算,最终结果就是NULL。

      select ename,sal + comm as salcomm from emp;

      注意:NULL只要参与运算,最终结果一定是NULL。为了避免这个现象,需要使用 ifnull 函数。

      ifnull 函数用法:ifnull(数据, 被当作那个值)

      如果 “数据” 为 NULL 的时候,把这个数据结构当作那个值。

      补助为NULL的时候,将补助当作0:

      select ename,(sal + ifnull(comm,0)) as salcomm from emp;

  3. select 后面直接跟“字面量 / 字面值”会有什么效果?

    select 'abc' num from emp; #数据库会根据emp有多少条记录而产生多少个"abc"字段值
    select 1000 as num from emp;  # 1000也是被当作一个字面量 / 字面值
    
    select abc num from emp; # 报错:因为会把abc当作一个字段的名字,去emp表中找abc字段

    结论:select后面可以跟某个表的字段名(可以等同看做变量名),也可以跟着字面量 / 字面值(数据)。


分组函数(多行处理函数)

  1. 多行处理函数的特点:输入多行,最终输出一行。

  2. 5个:

    • count 计数:eg –> 计算员工数量

      select count(ename);
    • sum 求和:eg –> 计算工资和

      select sum(sal) from emp;
    • avg 平均值:计算平均工资

      select avg(sal) from emp;

      14个工资全都加起来,然后除以14。

    • max 最大值:eg –> 找出最高工资

      select max(sal) from emp;
    • min 最小值:eg –> 求最低工资

      select min(sal) from emp;
  3. 注意:

    • 分组函数在使用的时候必须先进行分组,然后才能用。如果你没有对数据进行分组,整张表默认为一组。
  4. 分组函数在使用的时候需要注意哪些?

    1. 分组函数自动忽略NULL,你不需要提前对NULL进行处理。【五个都忽略】

      select sum(comm) from emp;
    2. 分组函数中 count(*) 和 count(具体字段) 有什么区别?

      select count(*) from emp;  
      select count(comm) from emp;

      count(具体字段):表示统计该字段下所有不为NULL的元素的总数。

      count(*)统计表当中的总行数。(只要有一行数据 count 则 ++ ),因为每一行记录不可能都为NULL,一行数据中有一列不为NULL,则这行数据就是有效的。

    3. 分组函数不能直接使用在 where 子句中。

      # 找出比最低工资高的员工信息
      select ename, sal from emp where sal > min(sal); # 错误
      
      # 表面上看着没问题,其实是错误的
      ERROR 1111 (HY000): Invalid use of group function

      因为分组函数在使用的时候必须先分组之后才能使用。

    4. 所有的分组函数可以组合起来一起用。

      select sum(sal),min(sal),max(sal),avg(sal),count(*) from emp;

分组查询(重要)

  1. 什么是分组查询?

    • 在实际的应用中,可能有这样的需求,需要先进行分组,然后对每一组的数据进行操作。这个时候我们需要使用分组查询,怎么进行分组查询呢?

      select
      	...
      	from
      		...
      		group by
      			...

      eg:计算每个部门的工资和?

      ​ 找出每个工作岗位的平均薪资?

      ​ 找出每个工作岗位的最高薪资?

  2. 这个没有分组,为啥sum()函数可以用呢?

    select sum(sal) from emp;
    # 这个没有分组,为啥sum()函数可以用呢?
    # 因为select在group by 之后执行。
  3. 找出每个工作岗位的工资和

    • 实现思路:按照工作岗位分组,然后对工资求和。

      select job,sum(sal) from emp group by job;
    • 以上这个语句的执行顺序

      1. 先从emp表中查询数据;
      2. 根据job字段进行分组;
      3. 然后对每一组的数据进行sum(sal)。
    • 判断:

      select ename, job, sum(sal) from emp group by job;

      以上语句在Mysql中可以执行,但是毫无意义。

      以上语句在oracle中执行报错,因为oracle的语法比mysql的语法严格。(mysql语法相对松散一些)

    • 重点结论:

      在一条select语句中,如果有group by语句,select后面只能跟:参加分组的字段,以及分组函数。其他的一律不能跟。

  4. eg:找出”每个部门,不同工作岗位”的最高薪资?

    select deptno,job max(sal) from emp group by deptno,job;
  5. 使用having可以对分完组之后的数据进一步过滤。having不能单独使用,having不能代替where,having必须和group by 联合使用。

    eg1:找出每个部门组稿薪资,要求显示最高薪资大于3000的

    select  deptno, ename, max(sal) from emp group by deptno having max(sal) > 3000;

    思考:以上的sql语句执行效率是不是低?

    • 比较低,实际上可以先将大于3000的都找出来,然后再分组。

      select deptno, max(sal) from emp where sal > 3000 group by deptno;

      优化策略:where 和 having ,优先选择 where,where实在完成不了的,再选择having。

  6. where没办法的?

    • 找出每个部门平均薪资,要求显示平均薪资高于2500的。

      select deptno, avg(sal) from emp group by deptno having avg(sal) > 2500; 

      这里无法使用where来进行age(sal)的条件过滤,只能用having


单表查询总结

整体执行顺序:from >> where >> group by … having >> 聚合函数(分组函数) >> select >> order by

  1. from:先确定从哪个表中取数据,所以最先执行from tab。存在多表连接,from tab1,tab2。可以对表加别名,方便后面的引用。

  2. where语句:是对条件加以限定,如果没有需要限定的,那就写成where 1=1,表示总为true,无附加条件。

  3. 分组语句,比如按照员工姓名分组,要就行分组的字段,必须出现在select中,否则就会报错。having是和group by配合使用的,用来作条件限定。

  4. 常用的聚合函数有max,min, count,sum,聚合函数的执行在group by之后,having之前。如果在where中写聚合函数,就会出错

  5. 选出要查找的字段,如果全选可以select *。

  6. 排序语句,默认为升序排列。如果要降序排列,就写成order by [XX] desc。order by语句在最后执行,只有select选出要查找的字段,才能进行排序。

  7. 从某张表中查询数据 –> 先经过where条件筛选出有价值的数据 –> 对这些有价值的数据进行分组 – > 分组之后可以使用having继续筛选 –> select查询出来 –> 最后排序输出。

  8. 综合案例:

    • 找出每个岗位的平均薪资,要求显示平均薪资大于1500的,除manager岗位之外,要求按照平均薪资降序排序。

      select deptno, avg(sal) from emp where job <> 'manager' group by deptno having avg(sal) > 1500 order by avg(sal) desc;

DAY02

把查询结果去除重复记录

注意:原表数据不会被修改,只是查询结果去重。

  1. 去重需要使用一个关键字:distinct只能出现在所有字段的最前方

    select distinct job form emp;
  2. distinct出现在job,deptnmo两个字段之前,表示两个字段联合起来去重。

    select distinct job,deptno from emp;
  3. 统计一下工作岗位的数量

    select count(distinct job) from empl;

    这里的distinct可以用,去除重复值的统计


连接查询

  1. 什么是连接查询?

    • 从一张表中单独查询,称为单表查询。
    • emp表和dept表联合起来查询数据,从emp表中取员工名字,从dept表中取部门名字。这种跨表查询,多张表联合起来查询数据,被称为连接查询。
  2. 连接查询的分类

    • 根据语法的年代分类:

      • SQL92:1992年的时候出现的语法;
      • SQL99:1999年的时候出现的语法;
    • 根据表连接的方式分类:

      • 内连接:

        ​ 等值连接:

        ​ 非等值连接:

        ​ 自连接:

      • 外连接:

        ​ 左外连接(左连接):

        ​ 右外连接(右连接):

      • 全连接:取出两张表的交集以及两张表剩下的记录条数。

  3. 当两张表进行连接查询,没有任何条件限制的时候,【最终查询结果条数,是两张表条数的乘积】,这种现象被称为:笛卡尔积现象。(笛卡尔发现的,这是一个数学现象。)

  4. 怎么避免笛卡尔积现象?

    • 连接时加条件,满足这个条件的记录被筛选出来。

      # 查询每个员工所在部门名称,显示员工名和部门名
      # 表起别名。很重要,效率问题。
      select e.ename,d.dname from emp e, dept d where e.deptno = d.deptno; # SQL92的语法

      思考:最终查询的结果条数是14条,,但是匹配的过程中,匹配的次数减少了吗?

      –> 没有,还是两张表条数的乘积,只不过进行了四选一。次数没有减少。

    • 注意:通过笛卡尔积现象得出,表的连接此参数越多效率越低,尽量避免表的连接次数。


内连接之等值连接

案例:查询每个员工所在部门名称,显示员工名和部门名。

SQL92语法:

select e.name,d.dname from emp e, dept d where e.deptno = d.deptno;

sql92的缺点:结构不清晰,表的连接条件,和后期进一步筛选的条件,都放到了where后面。

SQL99语法:

select e.name,d.dname from emp e join dept d on e.deptno = d.deptno; # 条件是一个等量,所以是一个等值连接

sql99优点:表连接的条件是独立的,连接之后,如果还需要进一步筛选,再往后继续添加where。

# sql99语法
select
	...
	from
		a
	[inner]	join  # 带着inner 可读性好,一眼看出来是内连接
			b
			on
				a和b的连接条件
				where
					筛选条件

内连接之非等值连接

案例:找出每个员工的薪资等级,要求显示员工名、薪资、薪资等级?

select e.ename, d.dname from emp e join salgrade s on e.sal between s.losal and s.hisal;
# 条件不是一个等量关系,称为非等值连接

内连接之自连接

案例:查询员工的上级领导,要求显示员工名和对应的领导名?

select a.ename, b.ename from emp a join emp b on a.empno = b.empno; # 员工的领导编号 = 领导的员工编号。

13条记录,没有KING。【内连接】

以上就是内连接中的:自连接,技巧:一张表看作两张表。

内连接:(A和B连接,AB两张表没有主次关系,平等的。)

select a.ename, b.ename from emp a join emp b on a.empno = b.empno;
# 内连接的特点:完全能够匹配上这个条件的数据查询出来。

外连接

  1. 外连接(右外连接):

    # outer 是可以省略的,带着可读性强
    select a.ename, b.ename from emp a right join emp b on a.empno = b.empno;
    • right 代表什么:表示将 join 关键字右边的这张表看成主表,主要是为了将这张表的数据全部查询出来,捎带着关联查询左边的表。
    • 在外连接当中,两张表连接,产生了主次关系。
  2. 外连接(左外连接):

    select a.ename, b.ename from emp a left join emp b on a.empno = b.empno;
    • 带有right的是右外连接,又叫做右连接。
    • 带有left的是左外连接,又叫做左连接。
    • 任何一个右连接都有左连接的写法。
    • 任何一个左连接都有右连接的写法。
  3. 思考:外连接的查询结果条数一定是 >= 内连接的查询结果条数?

    • 正确。
  4. 案例:查询每个员工的上级领导,要求显示所有员工的名字和领导名?

    select a.ename, b.dname from emp a left join emp b on a.mgr = b.empno;
    # 左边的是员工表,员工表中KING的领导是Null,使用左连接就不会与内连接一样忽略KING,此时输出14条数据。
  5. 三张表,四张表怎么连接?

    • 语法

      select
      	...
      	from
      		a
      		join
      			b
      			on
      			a和b的连接条件
      			join
      				c
      				on
      				 a和c的连接条件
      				 right join
      				 	d
      				 	  on
      				 	  	a和d的连接条件
      				 		
    • 一条SQL中内连接和外连接可以混合。都可以出现!

    • 案例:找出每个员工的部门名称以及工资等级,要求显示员工名、部门名、薪资、薪资等级?

      select e.ename, e.sal, d.dname, s.grade 
      from emp e 
      join dept d on e.deptno = d.deptno 
      join salgrade s on e.sal between s.losal and s.hisal;
    • 案例:找出每个员工的部门名称以及工资等级,还有上级领导,要求显示员工名、领导名、部门名、薪资、薪资等级?

      select e.ename, e.sal, d.dname, s.grade, l.ename '领导' 
      from emp e 
      join dept d on e.deptno = d.deptno 
      join salgrade s on e.sal between s.losal and s.hisal
      left join emp l on e.mgr = l.empno;  # 左连接,这样才能让KING显示出来,结果才会正确

连接查询SQL99

SQL语法概述

  • SQL92的语法规则的缺点:
    1. 语句过滤条件和表连接的条件都放到了where子句中 。
    2. 当条件过多时,联结条件多,过滤条件多时,就容易造成混淆
  • SQL99修正了整个缺点,把联结条件,过滤条件分开来,包括以下新的TABLE JOIN的句法结构:
    1. 交叉连接(Cross join)
    2. 自然连接(Natural join)
    3. 使用Using子句建立连接
    4. 使用On子句建立连接
    5. 连接( Outer join )
      • 内连接INNER JOIN
      • 左连接LEFT JOIN
      • 右连接RIGHT JOIN

SQL99语法

  • 语法

    select 字段列表 from table1 [cross join table2] |     # 1:交叉连接
    [natural join table2] |                              # 2:自然连接
    [join table2 using (字段名)] |                        # 3:using子句
    [join table2 on (table1.column_name = table2.column_name)] |  # 4:on子句
    [(left | right | full outer) join table2
    on (table1.column_name = table2.column_name)]; # 5:左/右/满外连接

交叉连接

  • Cross join 产生了一个笛卡尔集,其效果等同于在两个表进行连接时未使用WHERE子句限定连接条件;

  • 可以使用where条件从笛卡尔集中选出满足条件的记录。

  • 举例:

    select dept.deptno,dname,ename from dept cross join emp; 
    等价于
    select dept.deptno,dname,ename from dept,emp; 

自然连接

  • Natural join基于两个表中的全部同名列建立连接

  • 从两个表中选出同名列的值均对应相等的所有行

  • 如果两个表中同名列的数据类型不同,则出错

  • 不允许在参照列上使用表名或者别名作为前缀

  • 自然连接的结果不保留重复的属性

  • 举例:

    select empno, ename, sal, deptno, dname from emp natural join dept

using子句

  • 如果不希望参照被连接表的所有同名列进行等值连接,自然连接将无法满足要求,可以在连接时使用USING子句来设置用于等值连接的列(参照列)名。

  • using子句引用的列在sql任何地方不能使用表名或者别名做前缀

  • 举例:

    select e.ename,e.sal,deptno,d. dname from emp e join dept d using(deptno) where deptno=10

on子句

  • 自然连接的条件是基于表中所有同名列的等值连接,为了设置任意的连接条件或者指定连接的列,需要使用ON子句

  • 连接条件与其它的查询条件分开书写

  • 使用ON 子句使查询语句更容易理解

  • 举例:

    # 1.
    select ename,dname from emp inner join  dept on emp.deptno=dept.deptno where  emp.deptno=30;
    
    # 2.
    select empno, ename, sal, emp.deptno, dname from emp inner  join dept on (emp.deptno = dept.deptno and sal>5000);
    
    # 3.
    select * from dept, emp where dept.deptno = emp.deptno and sal>5000;
  • 第三种是sql92里面的查询方法,和上面两种等价

  • 等列名

    select emp.empno,emp.ename,dept.dname,dept.loc from emp join dept on emp.deptno = dept.deptno
  • 不等列名

    select  emp.empno,emp.ename, s.grade  from emp join salgrade s on emp.sal > s.losal and emp.sal < s.hisal

外连接

  • 左外连接

    • 两个表在连接过程中除返回满足连接条件的行以外,还返回左表中不满足条件的行,这种连接称为左外联接。

      select deptno, dname,empno,ename from dept left join emp using(deptno);
  • 右外连接

    • 两个表在连接过程中除返回满足连接条件的行以外,还返回右表中不满足条件的行,这种连接称为右外联接。

      select deptno, dname,empno,ename from dept right join emp using(deptno);
  • 满外连接

    • 两个表在连接过程中除返回满足连接条件的行以外,还返回两个表中不满足条件的所有行,这种连接称为满外联接。

      select deptno, dname,empno,ename from dept full join emp using(deptno);
  • 内连接

    • 在SQL99规范中,内连接只返回满足连接条件的数据。

子查询

  1. 什么是子查询?

    • select语句中嵌套select语句,被嵌套的select语句称为子查询。
  2. 子查询都可以出现在哪儿呢?

    select
    	..(select).
    from
    	..(select).
    where
    	..(select).
  3. where子句中的子查询

    • 案例:找出比最低工资高的员工姓名和工资?

      select ename,sal from emp where sal > (select min(sal) from emp);
  4. from子句中的子查询

    • 注意:from后面的子查询,可以将子查询的查询结果当作一张临时表。(技巧)

    • 案例:找出每个岗位的平均工资的薪资等级。

      select t.*, s.grade 
      from (select job, avg(sal) jobavgsal from emp group by job) t 
      join  salgrade s
      on t.jobavgsal between s.losal and s.hisal;
  5. select后面出现的子查询(这个内容不需要掌握,了解即可!!!)

    • 案例:找出每个员工的部门名称,要求显示员工名,部门名

      select e.ename, (select d.dname from dept d where e.deptno = d.deptno) from emp e;

      注意:对于select后面的子查询来说,这个子查询只能一次返回1条结果,多于1条,就报错了!


union合并查询结果集

  1. 案例:查询工作岗位是MANAGER和SALESMAN的员工

    select * from emp where job = 'manager' 
    union 
    select * from emp where job = 'salesman';
    • union的效率要高一些。对于表连接来说,每连接一次新表,则匹配的次数满足笛卡尔积,成倍的翻。。。但是union可以减少匹配的次数。在减少匹配次数的情况下,还可以完成两个结果集的拼接。

      a 连接 b 连接 c
      a 10条记录
      b 10条记录
      c 10条记录
      匹配次数是:1000
      
      a 连接 b 一个结果:10 * 10 --> 100次
      a 连接 c 一个结果:10 * 10 --> 100次
      使用union的话是:100次 + 100次 = 200次。(union把乘法变成了加法运算)
    • 注意事项:union在进行结果集合并的时候,要求两个结果集的列数相同,数据类型也要相同。


limit(非常重要)

  1. limit 作用:将查询结果集的一部分取出来。通常使用在分页查询中。

    • 百度默认:一页显示10条记录。
    • 分页的作用是为了提高用户的体验,因为一次全部都查出来,用户体验差,可以一页一页的看。
  2. limit 怎么用?

    • 完整用法:

      limit startIndex,length  #startIndex 是起始下标【从0开始】,length是长度
      
      # 缺省用法:
      limit 5; #这是取前5
    • eg1:按照薪资降序,取出排名在前5的员工?

      select ename,sal 
      from emp
      order by sal desc
      limit 5;

      注意:mysql 中 limit 在 order by 之后执行!

    • eg2:取出工资排名在[ 3 - 5 ]名的员工

      select * from emp order by sal desc limit 2,3; # 2表示起始位置从下标2开始,就是第三条记录,3表示长度
  3. 分页

    • limit 分页 公式:(查询第几页 - 1) * 每页显示几条数据

表的创建(建表)

  1. 语法(建表属于DDL语句,DDL包括:create / drop / alter):

    create table 表名(字段1 数据类型, 字段2 数据类型...);
    • 表名和字段名都属于标识符。
  2. 关于Mysql中的数据类型

    • varchar(最长255)

      可变长度的字符串,节省空间,会根据实际的数据长度动态分配空间。

      优点:节省空间;

      缺点:需要动态分配空间,速度慢。

    • char(最长255)

      定长字符串,不管实际的数据长度是多少,分配固定长度的空间去存储数据。

      优点:不需要动态分配空间,速度快;

      缺点:使用不当可能会导致空间的浪费。

      eg:姓名字段因为每个人的名字产长度不同,所以选择 varchar;

      ​ 性别字段因为性别是固定长度的字符串,所以选择char。

    • int(最长11)

      整数型,等同于java的int

    • bigint

      长整型,等同于Java中的 long

    • double

      双精度浮点型数据

    • date

      短日期类型“:只包括年月日信息。【默认格式:%Y-%m-%d】

    • datetime

      长日期类型:包括年月日时分秒信息。【默认格式:%Y-%m-%d %h:%i:%s】

      在mysql中怎么获取系统当前时间?–> now()函数,并且获取的时间带有:时分秒信息!是datetime类型的。

    • clob

      字符大对象,最多可以存储4G的字符串。比如存储一篇文章、说明。超过255个字符都要采用clob字符大对象来存储。(Character Large Object)

    • blob

      二进制大对象,(Binary Large Object)专门用来存储图片、声音、视频等流媒体数据。往BLOG类型的字段上插入数据的时候,例如插入一个图片、视频等。【需要使用IO流才行】


删除表

语法

drop table t_student;  # 当这张表不存在的时候会报错!
  • eg:如果这张表存在的话,删除

    drop table if exists t_student;

插入数据 insert (DML)

语法:

insert into 表名(字段名1, 字段名2, 字段名3...) value(值1, 值2, 值3...);

注意:

  • 字段名和值要一一对应。【数量要对应,类型要对应】
  • insert 语句但凡是执行成功了,那么必然会多一条记录。没有给其它字段指定值的话,默认值是null。
  • 前面的字段名省略的话,等于都写上了,所以值也要都写上。
  • 数据库中的命名规范:所有的标识符都是全部小写,单词和单词之间使用下划线进行衔接。

修改update(DML)

语法格式:

update 表名 set 字段名1 = 值1,字段名2 = 值2,字段名3 = 值3...where 条件;

注意:没有条件限制会导致所有数据全部更新。

# eg:更新所有
update t_user set name = 'abc';

删除数据 delete (DML)

语法格式

delete from 表名 where 条件;

注意:没有条件,整张表的数据会全部删除!

# eg:删除所有
delete from t_user;

DAY03

insert 语句一次可以插入多条记录

  • 语法:

    insert into t_user(字段名1, 字段名2) values(),(),();

快速创建表【了解】

语法:

create table emp2 as select * from emp;

原理:

  • 将一个查询结果当作一张表新建;
  • 这个可以完成表的快速复制;
  • 表创建出来,同时表中的数据页存在了。

将查询结果插入一张表中,Insert相关的!【很少用,了解】

insert into dept_bak select * from dept;

快速删除表中的数据【truncate比较重要】

  1. 删除dept_bak表中的数据

    delete from dept_bak; #这种删除数据的方式比较慢
  2. delete语句删除数据的原理?【delete属于DML语句】

    • 表中的数据被删除了,但是这个数据在硬盘上的真实存储空间不会被释放。
    • 缺点:删除效率比较低。
    • 优点:支持回滚,可以再恢复数据。
  3. truncate语句删除数据的原理?

    • 这种删除效率比较高,表被一次截断,物理删除。

    • 优点:快速。

    • 缺点:不支持回滚。

    • 语法:

      truncate table dept_bak; #这种操作属于DDL操作。
    • 针对于很大的表,上亿条记录:

      删除的时候,使用delete,也许需要执行1个小时才能删除完,效率较低。可以选择使用truncate删除表中的数据,只需要不到1秒的时间就删除结束。效率较高。但是使用truncate之前,必须仔细询问客户是否真的要删除,并警告删除之后不可恢复!


删除表

语法:

drop table 表名;

对表结构的增删改

  1. 是什么时对表结构的修改?
    • 添加一个字段,删除一个字段,修改一个字段。
  2. 对表结构的修改需要使用:alter –> DDL语句
    • DDL包括:create 、 drop 、alter
  3. 在实际开发中,需求一旦确定之后,表一旦设计好之后,很少对表结构修改。因为开发进行中的时候,修改表结构成本比较高,修改表的结构,对应的java代码就需要进行大量的修改。成本时比较高的。这个责任应该由设计人员来承担。
  4. 真的需要修改表结构,可以使用工具。
  5. 修改表结构的操作是不需要写到java程序中的。

约束【重点】

  1. 什么是约束?

    • 约束对应的英语单词:constraint
    • 在创建表的时候,我们可以给表中的字段加上一些约束,来保证这个表中数据的完整性、有效性。
    • 约束的作用就是为了保证:表中的数据有效!
  2. 约束包括哪些?

    • 非空约束:not null

      –> not null约束的字段不能为NULL;

      –> 只有列级约束,没有表级约束


    • 唯一约束:unique

      –> unique约束的字段不能重复,但是可以为NULL;

      什么时候使用表级约束呢?

      –> 需要给多个字段联合起来添加某一个约束的时候,需要使用表级约束。

      在mysql中,如果一个字段同时被not null和unique约束的话,该字段自动变成主键字段。(注意:oracle中不一样!)


    • 主键约束:primary key(简称PK)【重要】

      主键约束的相关术语?

      ​ –> 主键约束:就是一种约束;

      ​ –> 主键字段:该字段上添加了主键约束,这样的字段叫做:主键字段;

      ​ –> 主键值:主键字段中的每一个值都叫做:主键值。

      什么是主键?有什么作用?

      ​ 主键值是每一行记录的唯一标识;主键值是每一行记录的身份证号。

      ​ 任何一张表都应该有主键,没有主键,表无效。

      主键的特征:not null + unique(主键值不能是NULL,同时也不能重复!)

      主键约束的使用:【单一主键】

      create table t_vip(id int primary key, name varchar(255)); # 列级约束

      可以使用表级约束来添加主键:

      create table t_vip(id int, name varchar(255),primary key(id)); # 表级约束

      表级约束主要是给多个字段联合起来添加约束。【复合主键】

      create table t_vip(id int, name varchar(255),email varchar(255),primary key(id,name));

      在实际开发中不建议使用复合主键,建议使用单一主键!

      ​ –> 因为主键值存在的意义就是这行记录的身份证号,只要意义达到即可,单一主键可以做到,复合主键比较复杂,不建议使用!

      主键值建议使用:

      int、bigint、char等类型。

      不建议使用:varchar来做主键。主键值一般都是数字,一般都是定长的。

      主键除了单一主键和复合主键之外,还可以这样进行分类:

      ​ 自然主键:主键值是一个自然数,和业务没关系。

      ​ 业务主键:主键值和业务紧密关联,例如拿银行卡账号做主键值,这就是业务主键。

      在实际开发中使用自然主键比较多,因为主键只要做到不重复就行,不需要有意义。业务主键不好,因为主键一旦和业务挂钩,那么当业务发生变动的时候,可能会影响到主键值,所以业务主键不建议使用。尽量使用自然主键。

      在mysql中,有一种机制,可以帮助我们自动维护一个主键值。

      create table t_vip(id int primary key auto increment, name varchar(255));
      # auto increment 表示自增,从1开始,以1递增


    • 外键约束:foreign key(简称FK)【重要】

      外键约束涉及到的相关术语:

      ​ 外键约束:一种约束(foreign key);

      ​ 外键字段:该字段上添加了外键约束;

      ​ 外键值:外键字段当中的每一个值。

      注意:

      删除表的顺序:先删子,再删父;

      创建表的顺序:先创建父,再创建子;

      插入数据的顺序:先插入父,再插入子。

      语法:

      # 主表
      create table t_class(classno int primary key,classname varchar(255));
      
      # 子表
      create table t_student(no int primary key auto_increment, name varchar(255), cno int, foreign key(cno) references t_class(classno));
      # 外键约束 引用 主表的具有唯一约束的字段

      思考:子表中的外键引用的父表中的某个字段,被引用的这个字段必须是主键吗?

      ​ –> 不一定是主键,但至少具有unique约束。

      外键可以为NULL吗?

      ​ –> 可以。


    • 检查约束:check(mysql不支持,oracle支持)

  3. 技巧:

    • XXXX.sql 这种文件被称为sql脚本文件。其中编写了大量的sql语句。我们执行sql脚本文件的时候,该文件中所有的sql语句会全部执行。批量的执行sql语句,可以使用sql脚本文件。

    • 在mysql中如何执行sql脚本?

      source D:\存放.sql文件的路径\XXXX.sql
    • 实际工作中,执行这个脚本文件电脑上的数据库数据就有了。


存储引擎

  1. 什么是存储引擎?

    存储引擎是mysql中特有的一个术语,其他数据库中没有。(oracle中有,但是不叫这个名字),实际上存储引擎是一个表存储 / 组织数据的方式。不同的存储引擎,表存储数据的方式不同。

  2. 怎么给表添加、指定”存储引擎”呢?

    可以在建表的时候给表指定存储引擎。

    create table t_student(id varchar(255), no int(11))engine = innodb default charset = utf8;

    建表的时候可以在最后小括号的”)”的右边使用:

    ​ engine来指定存储引擎;

    ​ charset来指定这张表的字符编码方式。

    结论:

    ​ mysql默认的存储引擎是:innodb;

    ​ mysql默认的字符编码方式是:utf8

    ​ 建表时指定存储引擎,以及字符编码方式。

  3. 怎么查看mysql支持哪些存储引擎?

    • 命令:show engines \G
    • mysql支持九大存储引擎,版本不同支持情况不同。
  4. 关于mysql常用的存储引擎:

    • MyISAM存储引擎:

      它管理的表具有以下特征:

      ​ 使用三个文件表示每个表:

      格式文件 —— 存储表结构的定义(mytable.frm)

      数据文件 —— 存储表行的内容(mytable.myd)

      索引文件 —— 存储表上索引(mytable.myi):索引是一本书的目录,缩小扫描范围,提高查询效率的一种机制。

      此存储引擎的特点:

      可被转换为压缩、只读表来节省空间。【优势】

      MyISAM不支持事务机制,安全性低。【缺点】

      注意:对于一张表来说,只要是主键,或者加有unique约束的字段上会自动创建索引。


    • Innodb存储引擎:

      是mysql默认的存储引擎,同时也是一个重量级的存储引擎。

      InnoDB支持事务,支持数据库崩溃后自动恢复机制。

      InnoDB存储引擎最主要的特点是:非常安全。

      它管理的表具有下列主要特征:

      ​ — 每个InnoDB表在数据库目录中以.frm 格式文件表示。

      ​ — InnoDB 表空间 tablespace 被用于存储表的内容(表空间是一个逻辑名称。表空间存储数据 + 索引)。

      ​ — 提供一组用来记录事物性活动的日志文件;

      ​ — 用COMMIT(提交)、SAVEPOINT 及 ROLLBACK(回滚)支持事物处理;

      ​ — 提供全 ACID 兼容

      ​ — 在MySQL 服务器崩溃后提供自动恢复;

      ​ — 多版本(MVCC)和行级锁定;

      ​ — 支持外键及引用的完整性,包括级联删除和更新。

      InnoDB最大的特点就是支持事物:

      ​ 以保证数据的安全。效率不是很高,并且也不能压缩,不能转换为只读,不能很好的节省存储空间。


    • MEMORY存储引擎:

      使用MEMORY存储引擎的表,其数据存储在内存中,且行的长度固定,这两个特点使得MEMEORY存储引擎非常快。

      MEMORY存储引擎管理的表具有下列特征:

      ​ — 在数据库目录内,每个表均以.frm格式的文件表示;

      — 表数据及索引被存储在内存中。(目的就是快,查询快)

      ​ — 表级锁机制;

      ​ — 不能包含 TEXT 或 BLOB字段。

      MEMORY引擎以前被称为:HEAP引擎。

      优点:查询效率是最高的,不需要和硬盘交互。

      缺点:不安全,关机之后数据消失,因为数据和索引都是在内存中。


事务

什么是事务?

  • 一个事物其实就是一个完整的业务逻辑。

什么是一个完整的业务逻辑?

​ 假设转账,从A账户向B账户中转账10000.

​ 将A账户的前减去10000 (update语句)

​ 将B账户的钱加上10000 (update语句)

​ 这就是一个完整的业务逻辑。

以上的操作就是一个最小的工作单元,要么同时成功,要么同时失败,不可再分。这两个update语句要求必须同时成功或者同时失败,这样才能保证钱是正确的。

  1. 只有DML语句才会有事务这一说,其他语句和事务无关!

    • insert、delete、update
    • 只有以上的三个语句和事务有关系,其他都没有关系。
    • 因为只有以上三个语句是数据库表中数据进行增、删、改的。
    • 只要操作一旦涉及到数据的增、删、改,那么就一定要考虑安全问题,数据安全第一位!
  2. 假设所有的业务,只要一条DML语句就能完成,还有必要存在事务机制吗?

    • 正是因为做某件事的时候,需要多条DML语句共同联合起来才能完成,所以需要事务的存在,如果任何一件复杂的事都能一条DML语句搞定,那么事务就没有存在的价值了。
  3. 到底什么是事务呢?

    • 本质上,一个事务其实就是多条DML语句同时成功,或者同时失败。
  4. 事务是怎么做到多条DML语句同时成功和同时失败的呢?

    • InnoDB存储引擎:提供一组用来记录事务性活动的日志文件。

    • 在事务的执行过程中,每一条DML的操作都会记录到“事务性活动的日志文件”中。

    • 在事务的执行过程中,我们可以提交事务,也可以回滚事务。

    • 提交事务:

      –> 清空事务性活动的日志文件,将数据全部彻底持久化到数据库表中。提交事务标志着事务的结束,并且是一种全部成功的结束。

    • 回滚事务:

      –> 将之前所有的DML操作全部撤销,并且清空事务性活动的日志文件,回滚事务标志着,事务的结束。并且是一种全部失败的结束。

  5. 怎么提交事务,怎么回滚事务?

    • 提交事务:commit; 语句

    • 回滚事务:rollback; 语句(回滚永远都是只能回滚到上一次的提交点!)

    • 事务对应的英语单词:transaction

    • 在mysql当中默认的是事务行为是怎样的?

      –> mysql默认情况下是支持自动提交事务的。(自动提交)

      什么是自动提交? –> 每执行一条DML语句,则提交一次!

    • 这种自动提交实际上是不符合我们的开发习惯,因为一个业务通常是需要多条DML语句共同执行才能完成的,为了保证数据的安全,必须要求同时成功之后再提交,所以不能执行一条九提交一条。

    • 怎么将mysql的自动提交机制关闭?

      –> 先执行这个命令:start transaction;【默认关闭自动提交,改为手动提交事务】

  6. 事务包括四个特性

    • 原子性:说明事务是最小的工作单元。不可再分。
    • 一致性:所有事务要求,在同一个事务当中,所有操作必须同时成功,或者同时失败。
    • 隔离性:A事务和B事务同时操作一份数据,相互之间不影响。
    • 持久性:事务最终结束的一个保障。事务提交,就相当于将没有保持到硬盘上的数据保持到硬盘上。
  7. 事务和事务之间的隔离级别有哪些呢?【4个级别】

    • 读未提交:read uncommitted(最低的隔离级别)【没有提交就读到了】

      什么是读未提交?

      ​ —— 事务A可以读取到事务B未提交的数据。

      这种隔离级别存在的问题就是:

      ​ —— 脏读现象!(Dirty Read)

      ​ 我们称读到了脏数据。

      这种隔离级别一般都是理论上的,大多数的数据库隔离级别都是二档起步。


    • 读已提交:read committed【提交之后才能读到】

      什么是读已提交?

      ​ —— 事务A只能读取到事务B提交之后的数据。

      这种隔离级别解决了什么问题?

      ​ —— 解决了脏读现象。

      这种隔离级别存在什么问题?

      ​ —— 不可重复读取数据。

      什么是不可重复读取数据呢?

      ​ –> 在事务开启之后,第一次读到的数据是3条,当前事务还没有结束,可能第二次再读取的时候,读到的数据是4条,3不等于4称为不可重复读取。

      这种隔离级别是比较真实的数据,每一次读到的数据是绝对的真实。oracle数据库默认的隔离级别是:read committed


    • 可重复读:repeatable read【提交之后也读不到,永远读取的都是刚开启事务时的数据】

      什么是可重复读取?

      ​ –> 事务A开启之后,不管是多久,每一次在事务A中读取到的数据都是一致的。即使事务B将数据已经修改,并且提交了,事务A读取到的数据还是没有发生改变,这就是可重复读。

      可重复读解决了什么问题?

      ​ –> 解决了不可重复读取数据。

      可重复读存在的问题是什么?

      ​ –> 可能会出现幻影读,每一次读取到的数据都是幻象,不够真实。

      只要事务不结束,读到的数据还是和第一次一样,读到的是假象,不够绝对的真实。

      mysql 中默认的事务隔离级别就是这个!

    • 序列化 / 串行化:serializable(最高的隔离级别)

      这是最高隔离级别,效率最低,解决了所有的问题。这种隔离级别表示事务排队,不能并发!

      –> synchronized,线程同步(事务同步)

      每一次读取到的数据都是最真实的,并且效率是最低的。


  8. 查看隔离级别: select @@tx_isolation

    • 读未提交


    • 读已提交

    • 可重复读验证:


索引

  1. 什么是索引

    • 索引是在数据库表的字段上添加的,是为了提高查询效率存在的一种机制。一张表的一个字段可以添加一个索引,当然多个字段联合起来也可以添加索引。索引相当于一本书的目录,是为了缩小扫描范围而存在的一种机制。

    • eg:

      select * from t_user where name = 'jack';

      如果name字段上没有添加索引(目录),或者说没有给name字段创建索引,MySQL会进行全扫描,会将name字段上的每一个值都比对一遍。效率比较低。

    • MySQL在查询方面主要就是两种方式:

      1. 第一种方式:全表扫描;
      2. 第二种方式:根据索引检索。
    • 注意:

      ​ 在实际中,汉语字典前面的目录是排序的,按照a b c d …排序,为什么排序呢?因为只有排序了才会有区间查找这一说!(缩小扫描范围,其实就是扫描某个区间罢了)。

      在mysql数据库中索引也是需要排序的,并且这个排序和TreeSet数据结构相同。TreeSet(TreeMap)底层是一个自平衡的二叉树!在mysql当中索引是一个B-Tree数据结构。

      遵循左小右大原则存放,采用中序遍历方式遍历数据。

  2. 索引的实现原理

    1. 提醒1:在任何数据库中主键都会自己添加索引对象,id字段上有索引,因为id是PK。另外在mysql当中,一个字段上如果有unique约束的话,也会自动创建索引对象。

    2. 提醒2:在任何数据库中,任何一张表的任何一条记录在硬盘存储上都有一个硬盘的物理存储编号。

    3. 提醒3:在mysql中,索引是一个单独的对象,不同的存储引擎以不同的形式存在,在MyISAM存储引擎中,索引存储在一个.MYI文件中。在InnoDB存储引擎中索引存储在一个逻辑名称叫 tablespace中。在memory存储引擎当中索引被存储在内存当中,不管索引存储在哪里,索引mysql当中都是一个树的形式存在。(自平衡二叉树:B - Tree)。

  3. 在mysql当中,主键上,以及unique字段上都会自动添加索引的。

    什么条件下我们会考虑给字段添加索引呢?

    • 条件1:数据量庞大(到底多大算庞大需要根据硬件环境测试)
    • 条件2:该字段经常出现在where的后面,以条件的形式存在,也就是说这个字段总是被扫描。
    • 条件3:该字段很少的DML(insert delete update)操作。(因为DML之后,索引需要重新排序。)

    建议不要随意添加索引,因为索引也是需要维护的,太多的话反而会降低系统的性能。建议通过主键查询,建议通过unique约束的字段进行查询,效率是比较高的。


索引怎么创建?怎么删除?语法是什么?

  1. 创建索引:

    create index emp_ename_index on emp(ename);
    # 给emp表的ename字段添加索引,起名:emp_ename_index
  2. 删除索引:

    drop index emp_ename_index on emp;
    # 将 emp 表上的emp_ename_index索引对象删除。
  3. 在mysql中,怎么查看一个sql语句是否使用了索引进行检索?

    explain select * from emp where ename = 'KING';
    # type = ALL 说明没有使用索引
  4. 索引的底层是B-tree。


索引什么时候会失效?

第一种情况:

select * from emp where ename like '%T';

ename上即使添加了索引,也不会走索引,为什么?

  • 因为模糊匹配当中,以 “%” 开头了。尽量避免模糊查询的时候以 “%” 开始。这是一种优化的手段 / 策略。


第二种情况:

  • 使用or的时候会失效,如果使用or那么要求or两边的条件字段都要有索引,才会走索引,如果其中一边有一个字段没有索引,那么另一个字段上的索引也会实现。所以这就是为什么不建议使用or的原因。


第三种情况:

  • 使用复合索引的时候,没有使用左侧的列查找,索引失效。

什么是复合索引?

–> 两个字段,或者更多的字段联合起来添加一个索引,叫做复合索引。


第四种情况:

  • 在where当中索引列参加了运算,索引失效。


第5种情况:

  • 在where中索引列使用了函数。


索引是各种数据库进行优化的重要手段。

优化的时候优先考虑的因素就是索引。

  1. 索引在数据库中分了很多类?

    • 单一索引:一个字段上添加索引。

    • 复合索引:两个字段或者更多的字段上添加索引。

    • 主键索引:主键上添加索引。

    • 唯一性索引:具有unique约束的字段上添加索引。【越唯一,效率越高】

    • 注意:唯一性比较弱的字段上添加索引用处不大。

      可能会存在大量的重复数据,这个时候索引不起什么太大的作用。


视图(view)

  1. 什么是视图?

    • view:站在不同的角度去看待同一份数据。
  2. 怎么创建视图对象?怎么删除视图对象?

    • 创建视图对象:

      create view emp_view as select * from emp;
    • 删除视图对象:

      drop view emp_view;
    • 注意:只有DQL语句才能以view的形式创建。

      create view view_name as 这里的语句必须是DQL语句;
  3. 用视图做什么?

    • 我们可以面向视图对象进行增删改查,对视图对象的增删改查,会导致原表被操作! (视图的特点:通过对视图的操作,会影响到原表数据)

  4. 视图对象在实际开发中到底有什么用?【方便,简化开发,利于维护】

    create view 
    emp_dept_view 
    as 
    select e.ename,e.sal,d.dname
    from 
    	emp e
    join 
    	dept d
    on
    	e.deptno = d.deptno;
  5. 假设有一条非常复杂的SQL语句,而这条SQL语句需要在不同的位置上反复使用。每一次使用这个SQL语句的时候都需要重新编写,很长,很麻烦,怎么办?

    • 可以把这条复杂的SQL语句以视图对象的形式新建。
    • 在需要编写这条SQL语句的位置直接使用视图对象,可以大大简化开发。并且利于后期的维护,因为修改的时候也只需要修改一个位置就行。只需要修改视图对象所映射的SQL语句。
  6. 我们以后面向视图开发的时候,使用视图的时候可以像使用table一样。可以对视图进行增删改查等操作。视图不是在内存中,视图对象也是存储在硬盘上的,不会消失。

  7. 注意:

    • 视图对应的语句只能是DQL语句;
    • 但是视图对象创建完成之后,可以对视图进行增删改查等操作;
    • 增删改查,又叫做:CRUD。
    • C:Create(增);
    • R:Retrive(查:检索)
    • U:Update(改)
    • D:Delete(删)

DBA常用命令

  1. DBA常用命令【重点掌握】

    • 数据的导入和导出(数据的备份)
  2. 数据导出

    • 注意:在windows的dos命令窗口中:

      mysqldump bjpowernode>D:\bjpowernode.sql -uroot -p123456
      
      #可以导出指定的表
      mysqldump bjpowernode emp>D:\bjpowernode.sql -uroot -p123456
  3. 数据的导入

    • 注意:需要先登录到mysql数据库服务器上。
    • 然后创建数据库:create database bjpowernode;
    • 使用数据库:use bjpowernode
    • 然后初始化数据库:source D:\bjpowernode.sql

数据库设计三范式

  1. 什么是数据库设计范式?

    • 数据库表的设计依据。教你怎么进行数据库表的设计。
  2. 数据库设计范式共有?【3个】

    • 第一范式:要求任何一张表必须有主键,每一个字段原子性不可再分;

    • 第二范式:建立在第一范式的基础之上,要求所有非主键字段完全依赖主键,不要产生部分依赖;

    口诀:多对多怎么设计:多对多,三张表,关系表两个外键。

    • 第三范式:建立在第二范式的基础之上,要求所有非主键字段直接依赖主键,不要产生传递依赖。

    口诀:一对多,两张表,多的表加外键。

    声明:三范式是面试官经常问的。

    设计数据库表的时候,按照以上的范式进行,可以避免表中数据的冗余,空间的浪费。


设计总结

  • 口诀:一对一,外键唯一。 –> 只能取与主键相同的值。


  • 数据设计三范式是理论上的,实践和理论有的时候有偏差。

  • 最终的目的都是为了满足客户的需求,有的时候会拿冗余换执行速度。因为在sql中,表和表之间连接次数越多,效率越低。(笛卡尔积)

  • 有的时候可能会存在冗余,但是为了减少表的连接次数,这样做也是合理的,并且对于开发人员来说,sql语句的编写难度也会降低。


文章作者: 小白不白
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 小白不白 !
  目录