Mysql教程

1.mysql基础知识:
    ①mysql安装目录下bin文件夹中,mysqld.exe表示mysql服务器程序
    ②登录:cmd-->mysql -uroot -p123/或则:cmd-->mysql.exe -hlocalhost -p3306 -uroot -p123退出:exit/quit\q
    ③安装目录下:my.ini这个文件里面存放mysql配置文件
    ④mysql的数据库位于:C:\ProgramData\MySQL\MySQL Server 5.5\data,数据库中所有的表都存放在这里
    ⑤什么是SQL:Structured Query Language(结构化查询语言)
    ⑥SQL的作用:客户端使用SQL来操作服务器(客户端和服务器的对话语言)
    ⑦mysql服务器内部的四层对象:系统(DBMS)-->数据库(DB)-->数据表(Table)-->字段(Field)
    ⑧查看mysql字符编码: show variables like '%char%'; 解决乱码问题:set names gbk/utf8;
2.SQL语言分类:
    DDL:(数据库定义语言)数据库或表的结构操作
    DML:(数据库操作语言)对表的记录进行更新(增、删、改)
    DQL:(数据库查询语言)对表的记录的查询
    DCL:(数据库控制语言)对用户的创建以及授权
3.mysql数据库数据类型:
  标准类型:
    double:浮点型,如double(5,2)表示最多5位,其中必须有两位小数,即最大值为999.99
    decimal:浮点型,在表单钱方面使用该类型,因为不会出现轻度缺失问题(这个类型对十进制运算比较精确)
    char:固定长度字符串类型,char(255),数据长度不足指定长度,会补足到指定长度
    varchar:可变长度字符串类型,varchar(65535),zhangsan
    blob:二进制(存储图片、视频文件)
    data:日期类型,格式为:yyyy-MM-dd
    time:时间类型,格式为:hh:mm:ss
    timestamp:时间戳类型,格式为:yyyy-MM-dd hh:mm:ss
  mysql独有类型:
    text(clob):字符串类型    
4.DQL部分
    1>.DQL语法
        SELECT
            字段列表
        FROM
            表名列表
        WHERE 
            条件列表         --分组之前过滤,不满足WHERE条件不参与分组
        GROUP BY
            分组字段列表    --分组之后查询的一般为聚合函数和分组字段,查询其他字段无意义
        HAVING
            分组后条件列表    --分组之后过滤
        ORDER BY
            排序字段列表
        LIMIT
            分页参数
        
        WHERE与HAVING的不同:
            1>>.执行时机不同,WHERE是分组之前过滤,不满足WHERE条件不参与分组,HAVING是对分组之后的条件进行过滤  
            2>>.判断条件不同:WHERE不能对聚合函数进行判断,而HAVING可以
            3>>.执行顺序不同:WHERE > GORUP BY >HAVING    
    2>.条件查询        
        IS NULL/IS NOT NULL
            SELECT * FROM TEST  WHERE GENDER IS NULL;    -- IS NULL
            SELECT * FROM TEST  WHERE GENDER IS NOT NULL;    -- IS NOT NULL
        !=和<>
            SELECT * FROM TEST WHERE ID != 1;    -- !=
            SELECT * FROM TEST WHERE ID <> 1;    -- <>
        &&、AND和    BETWEEN...AND...
            SELECT * FROM TEST WHERE ID >= 1 && ID <= 5;    -- &&
            SELECT * FROM TEST WHERE ID >= 1 && NAME = '李华';
            SELECT * FROM TEST WHERE ID >= 1 AND ID <= 5;    -- AND
            SELECT * FROM TEST WHERE ID >= 1 AND NAME = '李华';    -- AND
            SELECT * FROM TEST WHERE ID BETWEEN 1 AND 5;    -- 包含最小值和最大值
        OR和||
            SELECT * FROM TEST WHERE ID = 1 OR ID = 2;
            SELECT * FROM TEST WHERE ID = 1 || ID = 2;
        LIKE(_和%)
            _:匹配一个字符
            %:匹配多个/任意个字符
            SELECT * FROM TEST WHERE NAME LIKE '张_';    -- 查询姓名为两个字,而且是以 张开头的人
            SELECT * FROM TEST WHERE NAME LIKE '_三';    -- 查询姓名为两个字,而且是以 三结尾的人
            SELECT * FROM TEST WHERE NAME LIKE '__';    -- 查询姓名为两个字的人
            SELECT * FROM TEST WHERE NAME LIKE '%';        -- 查询所有人
            SELECT * FROM TEST WHERE IDCARD LIKE '%X';    -- 查询身份证号以X结尾的人
    3>.聚合函数(将一列数据作为整体,进行纵向计算)    
        SELECT AVG(COMM) FROM EMP;
        SELECT MAX(COMM) FROM EMP;
        SELECT MIN(COMM) FROM EMP;
        SELECT COUNT(COMM) FROM EMP;
        SELECT SUM(COMM) FROM EMP;
    4>.分组查询
        注意:分组之后查询的一般为聚合函数和分组字段,查询其他字段无意义
        SELECT NAME, COUNT(*) FROM TEST GROUP BY GENDER; -- 根据GENDER分组,并统计每一组里面有多少人
        SELECT NAME,GENDER ,COUNT(*) 总数 FROM TEST GROUP BY GENDER;    -- 根据GENDER分组,并统计每一组里面有多少人
        SELECT SAL,COUNT(*) FROM EMP WHERE SAL >=3000 GROUP BY JOB,SAL HAVING COUNT(*) >1;    -- 薪水大于等于3000,根据JOB分组,获取到员工数量大于1的员工
    5>.排序查询
        ASC/DESC
        SELECT * FROM EMP ORDER BY SAL ASC;        --根据SAL升序排序
        SELECT * FROM EMP ORDER BY SAL DESC;    --根据SAL降序排序
        SELECT * FROM EMP ORDER BY SAL ASC,COMM DESC;    --根据SAL降序排序,如果SAL相同,则按照COMM降序排序
    6>.子查询Oracle
        1>>.标量子查询:子查询的结果返回的是单个值(数字、字符串、日期等)最简单的形式,称为标量子查询
            查询在SALES部门的所有员工信息
            SELECT * FROM EMP WHERE DEPTNO = (SELECT DEPTNO FROM DEPT WHERE DNAME = 'SALES');  
        2>>.列子查询:子查询返回的结果是一列或者多列
            查询所有比SALES部门所有人的SAL都高的人员信息
            SELECT *
              FROM EMP
              WHERE SAL >
                   ALL(SELECT SAL
                      FROM EMP
                     WHERE DEPTNO = (SELECT DEPTNO FROM DEPT WHERE DNAME = 'SALES'));
        3>>.行子查询:查询结果返回一行,可以是一列或者多列
            查询SAL、MGR和WARD相同的所有人员信息
                注意:可以使用两个值和子查询的结果对比,通常都是使用一个值
            SELECT * FROM EMP WHERE (SAL,MGR) = (SELECT SAL,MGR FROM EMP WHERE ENAME = 'WARD');
        4>>.表子查询:子查询返回的结果是多行多列,称为表子查询
            --查询与WARD和SCOTT职位和薪资相同的员工信息
            SELECT *
              FROM EMP
             WHERE (JOB, SAL) IN
                   (SELECT JOB, SAL FROM EMP WHERE ENAME IN ('WARD', 'SCOTT'));
5.DDL部分(数据定义语言:主要用来操作表)
    1>.数据库相关
       1>>.查询所有的数据库:SHOW DATABASES; ;
       2>>.使用数据库:
            USER 数据库名称;
            USE MYSQL;
       3>>.查询某一数据库中所有表:SHOW TABLES;
       4>>.查询当前所在的数据库:SELECT DATABASE();
       5>>.创建数据库:
            基本语法:
                create database 数据库名[库选项];
                库选项:用来约束数据库,分为两个选项
                字符集设定:charset/character set 具体字符集(数据存储的编码格式)
                校对集设定:collate 具体校对集(数据比较的规则)
                  create database [if not exists] mydb1 [CHARSET=utf8];
       6>>.删除数据库:
                DROP DATABASE 数据库名称;
                DROP DATABASE TEST;
       7>>.修改数据库编码:
            ALTER DATABASE 数据库名称 CHARACTER SET utf8;
            ALTER DATABASE TEST CHARACTER SET utf8;
       8>>.查询表信息(数据长度、索引长度、创建时间、更新时间等)
            USE information_schema;
            SELECT * FROM TABLES WHERE TABLE_SCHEMA = 'test' AND TABLE_NAME = 'USER';
    2>.表相关
       1>>.查询表结构:desc 表名 ;
       2>>.查看建表语句:show create table 表名;
    3>.删除表命令
        1>>.DROP命令
            删除表的内容和结构:drop table 表名;(注意:drop databases 数据库名称是删除数据库)
            删除表的所有内容而不删除结构:delete from 表名;
            删除表的部分内容而不删除结构:delete from 表名 where ....;     
        2>>.TRUNCATE命令删除表:删除表结构和表中数据,然后再创建一个和之前表结构一模一样的表
            用途:可以用于清空表中数据,而不破坏表结构,truncate不支持回滚
            truncate table 表名;
            TRUNCATE TABLE TEST;
        3>>.truncate与drop,delete的对比:
            truncate与drop是DDL语句,执行后无法回滚;delete是DML语句,可回滚。
            truncate只能作用于表;delete,drop可作用于表、视图等。
            truncate会清空表中的所有行,但表结构及其约束、索引等保持不变;drop会删除表的结构及其所依赖的约束、索引等。
            truncate会重置表的自增值;delete不会。
            truncate不会激活与表有关的删除触发器;delete可以。
            truncate后会使表和索引所占用的空间会恢复到初始大小;delete操作不会减少表或索引所占用的空间,drop语句将表所占用的空间全释放掉。

    4>.创建表:(注意:comment 用于加注释)
        create table [if not exists] 表名
          (    列名 列类型 comment 'xxx',
            列名 列类型 comment 'xxx',
            列名 列类型 comment 'xxx',
            列名 列类型 comment 'xxx',
            列名 列类型 comment 'xxx'
          ) comment 'xxx';
          eg:   create table test
           (    number char(11)  comment '数字',
            name varchar(50) comment '姓名',
            age int comment '年龄',
            gend varchar(10) comment '性别'
            ) comment '测试表';
    5>.修改表(修改数据库/修改数据表):
        a.修改表本身:表名和选项
            修改表名/重命名表:alter table 原表名 rename to 新表名
                               alter table emp rename to emp1;
                               注意:Oracle重命名表有两种方法,MySql只有一种方法
            修改表选项:字符集 alter table 表名 charset = gbk;            
                               alter table emp charset = gbk;
        b.修改字段(修改列、修改表字段):增加、修改、重命名、删除
            默认的新增字段/增加字段/增加列:(不设置插入字段的位置,默认插入位置是最后一个字段后面)
                语法一:在末尾添加字段
                    alter table 表名 add 列名 字段类型;
                    alter table test sid varchar(50);
                语法二:在开头添加字段
                    alter table 表名 add 列名 字段类型 FIRST;
                    ALTER TABLE TEST ADD UNAME_FIRST VARCHAR(20)  COMMENT '用户名_在开头添加字段' FIRST;
                语法三:在中间添加字段
                    alter table 表名 add 列名 字段类型 AFTER UNAME_FIRST;
                    ALTER TABLE TEST ADD UNAME_MIDDLE VARCHAR(20) COMMENT '在中间添加字段' AFTER UNAME_FIRST;                
                注意:把新字段增加到指定位置是mysql独有
            删除字段/删除字段/删除列:
                ALTER TABLE 表名 DROP 列名;
                ALTER TABLE TEST DROP NAME;
                或:
                ALTER TABLE 表名 DROP COLUMN 列名;
                ALTER TABLE TEST DROP COLUMN NAME;
            关于增加列和删除列的注意事项:
                ①操作一列add/drop 列名 数据类型,操作多列:add/drop (列名一 数据类型一,列名二 数据类型二...)
                ②add/drop后面可以加column也可以不加column
            修改列字段类型和字段长度:
                修改字段类型:
                alter table 表名 modify 原来的列名 新的数据类型(新的长度);
                alter table test modify name char(30);
                修改字段长度:
                alter table 表名 modify 原来的列名 原来的数据类型(新的长度);
                alter table test modify name varchar(30);(修改数据长度,类型不变)              
            修改列名/字段名:(修改字段名的同时也可以修改字段类型/字段长度)
                alter table 表名 change 原列名 新列名 列类型;(这个语法是mysql独有,oracle中并不适用)
                alter table test change name name1 varchar(50);

6.DML部分(数据操作语言:对表的记录的操作,增、删、改)
    ①在数据库中插入数据:
        给某几个字段赋值:
        INSERT INTO 表名(字段1,字段2) VALUES(值1,值2);
        INSERT INTO TEST (ID,NAME,GENDER) VALUES (001,'张三','男');
          给所有字段赋值:
        INSERT INTO 表名 VALUES(值1,值2,值3);
        INSERT INTO TEST VALUES(004,'王五','男');
       给某几个字段批量插入数据:
          INSERT INTO 表名(字段1,字段2) VALUES(值1,值2),(值3,值4),(值5,值6);
        INSERT INTO TEST (ID,NAME) VALUES (01,'张三'),(02,'李四'),('03','王五') ;   --注意:'03'是字符串之所以可以当成int类型数据插入进数据库,是因为插入之前先进行了隐式转换
       给所有字段批量插入数据:
        INSERT INTO 表名 VALUES (值1,值2,值3),(值4,值5,值6),(值7,值8,值9);
        INSERT INTO TEST VALUES (0001,'张三','男'),(0002,'李四','男'),(0003,'王五','女');
    ②修改记录:
       修改字段内容:
              update 表名 set 字段名1=值1,字段名2=值2 where ...;
        修改一个字段:
              update test set number='001',age=19 ;
           UPDATE TEST SET NAME = 'ZS' WHERE ID = '1';
        修改多个字段:
           UPDATE TEST SET NAME = 'ZS',GENDER = '男' WHERE ID = '2';
        修改整张表的数据(不带where条件):
            UPDATE TEST SET NAME = 'ZS',GENDER = '男';
        将某一个   
    ③删除记录语句
        删除某几条记录:
          DELETE FROM 表名 WHERE 条件;
          DELETE FROM TEST WHERE ID = '1';
          DELETE FROM TEST WHERE ID IN (2,3);  --关键词IN的使用
        删除表中所有记录:
          DELETE FROM 表名;
7.DCL部分
    概述:DCL(数据库控制语言),用来管理数据库用户、控制数据库的访问权限
    用户管理:管理哪些用户可以访问当前MYSQL数据库
    权限控制:控制用户访问当前数据库后有有哪些操作权限
    建议:一个项目创建一个用户,这个用户只能对这个数据库有权限,无法操作其他数据库
    1>.查询可以访问数据库的所有用户信息
        USE MYSQL;
        SELECT * FROM USER;
    2>.创建用户
        create user '用户名'@'ip地址/主机名' identified by '密码';
            用户名只能在指定的 ip地址/主机名 上登录
        create user '用户名'@'%' identified by '密码';
            用户名可以在任意的ip地址上登录
            CREATE USER 'test'@'%' IDENTIFIED BY '1234567'; --创建test/1234567用户
    3>.修改用户密码
        ALTER USER '用户名'@'%' IDENTIFIED WITH MYSQL_NATIVE_PASSWORD BY '密码';
            ALTER USER 'test'@'%' IDENTIFIED WITH MYSQL_NATIVE_PASSWORD BY '123456'; --修改test用户密码
    4>.解决低版本Navicate连接高版本Mysql报错(CLINET DOSE NOT SUPPORT AUTHENTICATION PROTOCOL REQUEST BY SERVER ;CONSIDER UPGRADING MYSQL CLIENT)
            修改root用户的host值为%,才能进行下一步更改加密方式;
                use mysql;
                select user,host from user;
                update user set host ='%' where user = 'root';
            更加加密方式:    
                ALTER USER 'root'@'%' IDENTIFIED BY '123456' PASSWORD EXPIRE NEVER;
                ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY '123456';
                FLUSH PRIVILEGES;
    4>.删除用户    
        DROP USER '用户名'@'ip地址/主机名';
        DROP USER 'test'@'%';
    5>.查询用户权限
        SHOW GRANTS FOR '用户名'@'ip地址/主机名';
        SHOW GRANTS FOR 'test'@'%';
    6>.给用户授权
        1>>.给用户分派在指定数据库上的权限
            grant 权限1,权限2 ...(权限列表,所谓权限用ALL表示) on 数据库.* to '用户名'@ip'地址';
            grant create ,alter,drop,insert,update,delete,select on mydb1.* to 'user'@'localhsot' ; 
                --给user用户分派在mydb1数据库上的create,alter,drop,insert,update,delete,select 权限;
        2>>.给用户分配在指定数据库上的所有权限
            grant all on 数据库.* to '用户名'@'ip地址'
            GRANT ALL ON MYSQL.* TO 'test'@'%'; -- 给test用户授予操作MYSQL数据库的所有权限
    7>.撤销授权
        1>>.撤销用户在指定数据库上的权限
            REVOKE 权限1,权限2 ...(权限列表,所谓权限用ALL表示) on 数据库.* FROM '用户名'@ip'地址';
            REVOKE create ,alter,drop,insert,update,delete,select on mydb1.* FROM 'user'@'localhsot' ;  
                --撤销user用户在mydb1数据库上的create,alter,drop,insert,update,delete,select 权限;
        2>>.撤销用户在指定数据库上的所有权限
            grant all on 数据库.* FROM '用户名'@'ip地址'
            GRANT ALL ON MYSQL.* FROM 'test'@'%'; -- 撤销test用户操作MYSQL数据库的所有权限        
8.MYSQL函数
    1>.字符串函数
        1>>.CONCAT:拼接字符串
        CONCAT(str1,str2…) ,注意:拼接时一般用单引号连接字符串
            SELECT CONCAT('T','EST');
            SELECT CONCAT('T','E','ST');
            SELECT * FROM TEST WHERE NAME = CONCAT('张','三');
        CONCAT(拼接字符,str1,str2…)
            SELECT CONCAT_WS(',','First name','Second name','Last Name');
            SELECT CONCAT_WS('-','First name','Second name','Last Name');
            SELECT CONCAT_WS('*','First name','Second name','Last Name');
        2>>.LOWER(str):将字符串转为全部小写
            SELECT LOWER('SsAa');
        3>>.UPPER(str):将字符串转为全部大写
            SELECT UPPER('SsAa');
        4>>.LPAD(str,len,填充符):左填充
            SELECT LPAD('00',5,'x'); --执行结果:xxx00
        5>>.RPAD(str,len,填充符):右填充
            SELECT RPAD('00',5,'x'); --执行结果:00xxx    
        6>>.TRIM(str):去掉字符串左右两端空格,不包含中间的空格
            SELECT TRIM(' Hello World    '); --执行结果:Hello World
        7>>.SUBSTR(str,beginIndex,len):截取指定长度的字符串(注意:Mysql和Oracle的索引都是从1开始)
            SELECT SUBSTRING('Hello World',1,5);  --执行结果:Hello 
    2>.数值函数
        1>>.CEIL(x):向上取整
            SELECT CEIL(1.5);        --2
        2>>.FLOOR(x):向下取整
            SELECT FLOOR(1.5);        --1
        3>>.MOD(x,y):返回x/y的模
            SELECT MOD(5,2);        --1
            SELECT MOD(5,3);        --2
        4>>.RAND():返回0~1内的随机数
            SELECT RAND();    --0.053181587767956395
            SELECT RAND();  --0.77559073434732
        5>>.ROUND(x,y):求参数x的四舍五入的值,保留y位小数点
            SELECT ROUND(2.34,2);   --2.34
            SELECT ROUND(2.346,2);  --2.35
        综合案例:随机生成一个六位数的验证码    
            SELECT ROUND(RAND()*1000000,0);  --错误示例,因为RAND()可能生成0.01xxx或0.001xxx,那么计算出的数只有5位或4位
            SELECT LPAD(ROUND(RAND()*1000000,0),6,'0'); --正确示例,解决方式:左边补零或者右边补零            
    3>.日期函数
        1>>.CURDATE():返回当前日期
            SELECT CURDATE();    --2022-03-31
        2>>.CURTIME():返回当前时间
            SELECT CURTIME();   --18:31:14
        3>>.NOW():返回当前日期和时间
            SELECT NOW();   --2022-03-31 18:31:50
        4>>.YEAR(date):获取指定date的年份
            SELECT YEAR(NOW());   --2022
        5>>.MONTH(date):获取指定date的月份
            SELECT MONTH(NOW());   --3
        6>>.DAY(date):获取指定date的日期
            SELECT DAY(NOW());   --31
        7>>.DATE_ADD(date,INTERVAL expr type):返回一个日期/时间值加上一个时间间隔expr后的时间值
            SELECT DATE_ADD(NOW(),INTERVAL 365 DAY);    --2023-03-31 18:35:37
            SELECT DATE_ADD(NOW(),INTERVAL 12 MONTH);    --2023-03-31 18:35:37
            SELECT DATE_ADD(NOW(),INTERVAL 1 YEAR);    --2023-03-31 18:35:37
        8>>.DATEDITT(date1,date2):返回起始时间date1和结束时间date2之间的天数    
            SELECT DATEDIFF('2020-5-1','2020-4-1'); --30
            SELECT DATEDIFF('2020-1-1','2020-1-3'); --2
        综合案例:查询所有员工的入职天数,并按照入职天数倒序排列
            SELECT DATEDIFF(CURDATE(),HIREDATE) AS HIREDATES FROM EMP ORDER BY  HIREDATES DESC;
    4>.流程函数
        1>>.IF(value,t,f):如果value为true,则返回t,否则返回f
            SELECT IF(true,'OK','False');    --OK
            SELECT IF(false,'OK','False');    --False
            SELECT IF(1,'OK','False');      --Ok
            SELECT IF(0,'OK','False');      --False
            SELECT IF(10,'OK','False');      --Ok
            SELECT IF(' ','OK','False');    --False
        2>>.IFNULL(value1,value2):如果value1不为空则返回value1,否则返回value2
            SELECT IFNULL(' ','DEFAULT');    --空字符串
            SELECT IFNULL('1','DEFAULT');   --1
            SELECT IFNULL(null,'DEFAULT');  --DEFAULT
        3>>.CASE WHEN[value1] THEN [res1] ...ELSE[default] END
            如果value1位true,则返回res1,...否则返回default默认值            
            SELECT 
              EMPNO,
              ENAME,
              SAL,
              CASE WHEN SAL >3000 THEN '高工资' 
                   WHEN SAL >1000 AND SAL <2000 THEN '中工资' 
                   ELSE '低工资' END  AS  工资等级
            FROM EMP;
        4>>.CASE[expr] WHEN [value1] THEN [res1]...ELSE[default] END
            如果expr的值等于value1,则返回res1,...否则返回default默认值
            查询员工编号、员工姓名和职位,如果职位是CLERK,则显示店员,如果职位是SALESMAN,则显示售货员,
                如果职位是MANAGER,则显示管理人员,否则显示其他职位
            SELECT 
              EMPNO,
              ENAME,
              JOB,
              CASE JOB WHEN 'CLERK' THEN '店员'
                       WHEN 'SALESMAN' THEN '售货员'
                       WHEN 'MANAGER'  THEN '管理人员'
                       ELSE '其他职位'
                       END AS 中文职位名称
            FROM EMP;
9.limit(mysql独有)
    limit用来限定查询结果的起始行以及总行数
    如:查询起始行为第5行,一共查询3行记录
        select * from emp limit 4,3;
        其中4表示从第5行开始查询(mysq同Java,以0开始计数),其中3表示一共查询3行,即第5、6、7行的记录
    mysql分页:    
    如,每页十条记录,要求查询第3页记录
    (当前页-1) * 每页记录数
    (3-1)      *   10
    select * from emp limit (3-1)*10,10;
    所以:每页n条记录,查询第currentPage页的记录:
    select * from 表名 limit (currentPage-1)*n,n;
10.Mysql编码问题(字符编码/编码格式/数据库编码)
    查看数据库中所采用的字符编码:show variables like 'char%';
    查看数据库服务器(mysql服务器)所支持的字符编码:show charset;或 show character set ;(显示:39 rows in set (0.00 sec)说明共有39种编码格式)
    查看数据库服务器(mysql服务器)默认的对外处理的字符编码:show variables like 'char%';
    Mysql客户端编码:character_set_client = utf8/gbk
        character_set_client = utf8的意思是:无论客户端发送的是什么编码的数据,mysql都当成utf8的数据,也就是说
        如果客户端发送的是gbk格式的数据,服务器当成utf8对待,必然会出现乱码问题。
        处理这个原因引起的乱码问题有两种方式:
        ①让客户端发送utf8的数据(这个行不通的)
        ②把character_set_client修改为gbk,使用语句:character_set_client = gbk或者set names utf8/gbk
    Mysql查询结果编码:character_set_results = utf8/gbk
            处理这个原因引起的乱码问题有两种方式:
        ①让服务器发送可以识别的编码格式的数据(这个行不通的)
        ②character_set_results = gbk/utf-8或者set names utf8/gbk;
    注意:1.set character_set_results = utf8/gbk 、set character_set_client = gbk、set names utf8/gbk;这三个语句的作用
          域都是在一个session之内,也就是说,重新登录mysql后,刚才的设置就会失效
          2.set names utf8/gbk= set character_set_client = gbk + set character_set_connections= utf8/gbk  +set character_set_results = utf8/gbk 
          一个效果相当于三个的效果,可以用来快捷的设置字符集
    为了使修改对所有窗口都有效果,可以修改总配置文件my.ini,在这里面进行设置,可以一劳永逸。
    [client]
    port=3306
    [mysql]
    default-character-set=utf8-->这个语句可以修改:client、results、connection的编码
    查询修改结果:show variables like 'char%';
    创建含有中文的字符的数据库:
    set names gbk;
    create database 中国 charset utf8;
11.DOS命令控制mysql服务器的启动与停止
    cmd启动mysql服务:net start mysql;
    cmd停止mysql服务:net stop mysql;
12.mysql的备份与恢复
    ①使用SQLyog对mysql的备份与恢复:
        备份: 数据库-->sql脚本:右键数据库-->备份/导出-->以SQL转储文件备份数据库-->导出
        恢复:sql脚本-->数据库
    ②mysql原生
        备份:先退出mysql(cmd-->exit),然后
              cmd-->mysqldump -uroot -p123 数据库名>导出路径
              cmd-->mysqldump -uroot -p123 mydb1>d:/a.sql
        恢复:方式一:登录时恢复
              create database mydb1;(先创建要恢复的数据库,因为只能恢复数据库内容,不能恢复数据库)先退出mysql,然后
              cmd-->mysql -uroot -p213 数据库名<导出路径
              cmd-->mysql -uroot -p123 mydb1<d:/a.sql
              注意:①必须在登录的时候进行
                ②这是备份数据库内容,并不是备份数据库
                    ③回复时文件要为xxx.sql才能恢复,xxx.txt不能恢复
              方式二:登录后恢复(source):
              cmd-->登录mysql-->create database mydb1;-->use mydb1;-->source d:/a.sql
              注意:①使用source命令恢复数据库之前也要先创建数据库
                ②创建数据库后要先使用/进入数据库,才能执行source语句恢复数据库
13.mysql的约束
    1>.主键约束:
        特点:非空、唯一、被引用
    2>.添加约束的三种方法:
        方法一:列级约束
            CREATE TABLE test2
            (
              empno INT PRIMARY KEY,
              ename VARCHAR(50)
            );
        方法二:表级约束
            CREATE TABLE test2
            (
              empno INT,
              ename VARCHAR(50),
              PRIMARY KEY(empno)
            );
        示例二:创建表成功后追加主键约束:
            CREATE TABLE test2
            (
              empno INT PRIMARY KEY,
              ename VARCHAR(50)
            );
            ALTER TABLE test3 ADD PRIMARY KEY(empno);
        如果违反主键约束,会报错:Duplicate entry '1' for key 'PRIMARY'
    3>.删除主键约束
        alter table 表名 drop primary key ;
        alter table test3 drop primary key ;
    4>.主键自增长
        create table test4
        (
            id int primary key AUTO_INCREMENT,
            name varchar(20),
            age int,
            gender varchar(10)
        );
        注意:primary key 前面必须为int类型数据才能自增长
        测试主键自增长:
        第一次插入记录:insert into test4 values(1111,'zs',28,'male');
        第二次插入记录:insert into test4 values(null,'zs',28,'male');
        第三次插入记录:insert into test4 values(null,'zs',28,'male');
        查询结果为:
            | id   | name | age  | gender |
            +------+------+------+--------+
            | 1111 | zs   |   28 | male   |
            | 1112 | zs   |   28 | male   |
            +------+------+------+--------+
        可以发现主键已经自增长
        注意:1>>.若是先执行删除表操作:delete from test4;
              再执行插入操作:insert into test4 values(null,'zs',28,'male');
              查询结果为:
                +------+------+------+--------+
                | id   | name | age  | gender |
                +------+------+------+--------+
                | 1113 | zs   |   28 | male   |
                | 1114 | zs   |   28 | male   |
                +------+------+------+--------+
              可以发现:id是从1113开始计数(上一次是从id=1112处结束的),也就是说删除并不会影响主键自增长,类似于序列
              2>>.若是执行插入语句:
              insert into test4 values(1,'zs',28,'male');
              insert into test4 values(99999,'zs',28,'male');
              查询结果为:
                +-------+------+------+--------+
                | id    | name | age  | gender |
                +-------+------+------+--------+
                |     1 | zs   |   28 | male   |
                |  1113 | zs   |   28 | male   |
                |  1114 | zs   |   28 | male   |
                | 99999 | zs   |   28 | male   |
                +-------+------+------+--------+
              可以发现:mysql会自动按照id值的升序排序记录,所以,1在1113前面,99999在1114后面
    5>.非空约束、唯一约束: 
        create table test4
        (
            id int primary key AUTO_INCREMENT,
            name varchar(20) NOT NULL UNIQUE,
            age int,
            gender varchar(10)
        );
        注意:非空约束(NOT NULL)+唯一约束(UNIQUE) 不等于 主键约束(PRIMARY KEY)
        原因:主键约束所有三个特点:非空、唯一、被引用
    6.外键约束    
        特点:1>>.外键的值必须是另一张表主键的值(外键要引用主键)
              2>>.外键可以重复
              3>>.外键可以为空
              4>>.一张表中可以有多个外键/一张表中的外键可以引用多张表的主键
              5>>.外键不能引用不存在主键
        create table emp
        (
            empno int primary key AUTO_INCREMENT,
            name varchar(20),
            dno int,
            CONSTRAINT fk_emp_dept FORENGN KEY(dno) REFERENCES dept(deptno)
        );
15.概念模型
    对象模型:可以双向关联,而且引用的是对象,而不是一个主键。
    关系模型:只能多方引用一方,而且引用的只是主键,而不是一整行记录。
16.数据库一对一关系
    一对一关系时一张是表,一张是从表
    先创建一个主键自增长的表hasband:
    create table hasband
    (
        hid int primary key auto_increment,
        hname varchar(50)
    );
    给刚才建的表中插入数据:
    insert into hasband(hid,hname) values(null,'zs');
    insert into hasband(hid,hname) values(null,'ls');
    insert into hasband(hid,hname) values(null,'ww');

    再创建一个主键自增长并且带有外键约束的表hasband:
    create table wife
    (
        wid int primary key auto_increment,
        wname varchar(50),
        constraint fk_wife_hasband foreign key(wid) references hasband(hid)
    );
    wid要求:唯一、非空、引用hid
    再给wife表插入数据:
    insert into wife values(1,'小红');
    insert into wife values(1,'效率');
17.数据库多对多关系(需要使用中间表)
    多对多关系时两张表都是主表
    先创建两张没有外键的表:
    create table student
    (
        sid int primary key auto_increment,
        sname varchar(50)
    );
    create table teacher
    (
        tid int primary key auto_increment,
        tname varchar(50)
    );
    创建一张表,里面有两个外键
    create table stu_tea
    (
        sid int ,
        tid int ,
        constraint fk_studet foreign key(sid) references student(sid),    
        constraint fk__teacher foreign key(tid) references teacher(tid)
    );
    再给student表插入数据:
    insert into student values(null,'aa');
    insert into student values(null,'bb');
    insert into student values(null,'cc');
    再给teacher表插入数据:
    insert into teacher values(null,'AA');
    insert into teacher values(null,'BB');
    insert into teacher values(null,'CC');
    再给stu_tea表插入数据:
    insert into stu_tea values(1,1);
    insert into stu_tea values(2,1);
    insert into stu_tea values(3,1);

    insert into stu_tea values(1,2);
    insert into stu_tea values(2,2);
    insert into stu_tea values(3,2);
18.多表查询,分类:集合运算、连接查询、子查询    
        集合运算:
        创建aa表:
            create table aa(id int,name varchar(20));
            insert into aa values(1,'aa');
            insert into aa values(1,'aa');
            insert into aa values(1,'aa');
        创建bb表:
            create table bb(id int,name varchar(20));
            insert into aa values(2,'bb');
            insert into aa values(2,'bb');
            insert into aa values(2,'bb');
        测试集合运算:
        ①union all不去除重复列:
            select * from aa
            union all
            select * from bb;
        ②union去除所有重复的列
            select * from aa
            union 
            select * from bb;
    连接查询
        1>>.内连接
         *方言:select * from 表1,别名1,表2,别名2, where 别名1.xx=别名2.xx;
            select e.ename,e.sal,d.dname from emp e,dept d where e.deptno = d.deptno;
         *标准:select * from 表1,别名1 inner join 表2 别名2 on 别名1.xx=别名2.xx;
            select e.ename,e.sal,d.dname from emp e inner join dept d on e.deptno=d.deptno;
         *省略inner
            select e.ename,e.sal,d.dname from emp e join dept d on e.deptno=d.deptno;
         *自然:select * from 表1,别名1 natural join 表2 别名2;
            select e.ename,e.sal,d.dname from emp e natural join dept d;
            特点:自动查找两张表中相同的列
         *隐式内连接
            SELECT * FROM EMP E,DEPT D WHERE E.DEPTNO = D.DEPTNO;

        2>>.外连接
         *左外连接:查询左表数据以及两张表交集部分数据,所以左表数据会全部出现
            SELECT * FROM EMP E LEFT JOIN DEPT D ON E.DEPTNO = D.DEPTNO;
         *右外连接:查询右表数据以及两张表交集部分数据,所以右表数据会全部出现
            SELECT * FROM EMP E RIGHT JOIN DEPT D ON E.DEPTNO = D.DEPTNO;
         *全外连接(MySql不支持全外连接)
        3>>.自连接:当前表与自身的连接查询,必须使用别名
          查询员工信息及其领导信息
            SELECT E1.ENAME,E2.ENAME FROM EMP E1 INNER JOIN EMP E2 ON E1.MGR = E2.EMPNO;
          查询员工信息及其领导信息,即使该员工没有领导,也要查询出来(要完全包含员工信息,所以是左外连接)
            SELECT E1.ENAME,E2.ENAME FROM EMP E1 LEFT JOIN EMP E2 ON E1.MGR = E2.EMPNO;
19.查询数据库时使用模糊查询,如:查询名字以information开头的数据库
    show databases like 'information%';
20.查看创建数据库的语句:
    例如:查看mydb1的创建语句:show create database mydb1;
   查看创建数据库中表的语句:
        例如,查看mydb1数据库中创建emp表的语句:show create table emp;
21.修改数据库的字符集:
    例如:修改mydb1的字符集/字符编码:alter database mydb1 charset gbk;
22.校对集问题:
    校对集:数据的比较方式
    校对集有三种格式:
        _bin:binary:二进制比较,取出二进制位,一位一位的比较

MySql查询练习:
    分析方法:
    列:xx
    表:xx
    条件:xx
    1.列出所有员工的姓名及其直接上级的姓名
    列:e.ename,m.ename
    表:emp e,emp m
    条件:e.mgr=m.empno
    select e.empno, e.ename,m.empno ,m.ename from emp e,emp m where e.mgr=m.empno order by 1 asc;
    2.查询出受雇日期早于直接上级的所有员工的编号、姓名、部门名称
    分析方法:
    列:e.epmno、e.ename、d.dname
    表:emp e、dept d、emp m
    条件:e.hiredate<m.hiredate
    可以写出如下语句:
    select distinct e.empno,e.ename,d.dname 
    from emp e,dept d,emp m
    where e.hiredate<m.hiredate;

    发现上面语句行不通,换一种思路继续分析,
    先不查部门名称,只查部门编号:
    列:e.empno,e.ename,e.deptno
    表:emp e,emp m,dept d
    条件:e.mgr = m.empno ,e.hirdate<m.hiredate
    可以写出如下语句:
    select e.hiredate ,m.hiredate ,e.ename,m.ename
    from emp e,emp m,dept d
    where e.mgr = m.empno and e.hiredate<m.hiredate and e.deptno=d.deptno;
    3.列出薪金高于公司平均薪金的所有员工信息,所在部门名称,上级领导,工资等级
    select 
    from 
    where

Mysql存储引擎
    1>.概念
        存储数据、建立索引、更新、查询数据等技术的实现方式。存储引擎是基于表的,不是基于库的,所以存储引擎也可以被称为表类型。
        注意:索引是在存储引擎中实现的
    2>.查看某一张表的存储引擎
        show create table 表名;查询出建表语句后查看ENGINE=后面的值
    3>.    默认存储引擎
        InnoDB
    4>.创建表时指定存储引擎
        CREATE TABLE 表名() ENGINE =INNODB;
    5>.查看所有引擎
        SHOW ENGINS;
        mysql> show engines;
        +--------------------+---------+----------------------------------------------------------------+--------------+------+------------+
        | Engine             | Support | Comment                                                        | Transactions | XA   | Savepoints |
        +--------------------+---------+----------------------------------------------------------------+--------------+------+------------+
        | MEMORY             | YES     | Hash based, stored in memory, useful for temporary tables      | NO           | NO   | NO         |
        | MRG_MYISAM         | YES     | Collection of identical MyISAM tables                          | NO           | NO   | NO         |
        | CSV                | YES     | CSV storage engine                                             | NO           | NO   | NO         |
        | FEDERATED          | NO      | Federated MySQL storage engine                                 | NULL         | NULL | NULL       |
        | PERFORMANCE_SCHEMA | YES     | Performance Schema                                             | NO           | NO   | NO         |
        | MyISAM             | YES     | MyISAM storage engine                                          | NO           | NO   | NO         |
        | InnoDB             | DEFAULT | Supports transactions, row-level locking, and foreign keys     | YES          | YES  | YES        |
        | BLACKHOLE          | YES     | /dev/null storage engine (anything you write to it disappears) | NO           | NO   | NO         |
        | ARCHIVE            | YES     | Archive storage engine                                         | NO           | NO   | NO         |
        +--------------------+---------+----------------------------------------------------------------+--------------+------+------------+
    6>.各种不同的引擎区别
        1>>.InnoDB
            介绍:
                一种兼顾高可靠性和高性能的引擎,是Mysql(5.5版本之后)的默认引擎
            特点:
                DML遵循ACID模型,支持事务
                行级锁,提高并发访问性能
                支持外键FOREIGN KEY约束,保持数据的完整性和正确性
            文件:
                xxx.ibd,xxx代表的是表名,innoDB引擎的每张表都会对应这样一个表空间文件,存储该表的表结构(frm、sdi)、数据和索引,文件格式是二进制,不能直接打开查看
                参数:innodb_file_per_table,决定是每一个表都有独立的表空间还是多张表共用一个表空间文件
                
                查看innodb_file_per_table的值:ON代表每一张表都对应一个表空间文件,OFF代表多张表公用一个表空间文件
                    show variables like '%innodb_file_per_table%';
                    mysql> show variables like '%innodb_file_per_table%';
                    +-----------------------+-------+
                    | Variable_name         | Value |
                    +-----------------------+-------+
                    | innodb_file_per_table | ON    |
                    +-----------------------+-------+
                    1 row in set, 1 warning (0.00 sec)
                
                查看test库中user表在磁盘中对应的表空间文件的位置:
                    C:\ProgramData\MySQL\MySQL Server 8.0\Data\test\user.ibd
                
                查看ibd文件内容:ibd2sdi xx.ibd
                    C:\ProgramData\MySQL\MySQL Server 8.0\Data\test>ibd2sdi user.ibd
        2>>.MyISAM
            介绍:
                MyISAM是Mysql早期的默认存储引擎
            特点:
                不支持事务,不支持外键
                支持表锁,不支持行锁
                访问速度快
            文件:
                xxx.sdi:存储表结构信息
                xxx.MYD:存储数据
                xxx.MYI:存储索引
            补充:InnoDB和MyISAM引擎区别:事务、外键、行级锁    
        3>>.Memory
            介绍:
                Memory引擎的表数据是存储在内存中,由于受到硬件问题、或断电问题的英雄,只能将这些表作为临时表或缓存使用
            特点:
                内存存放
                hash索引(默认)
            文件:
                xxx.sdi存储表结构信息
        4>>.选择存储引擎
                选择存储引擎时,应该根据应用系统的特点选择合适的引擎。对于复杂的应用系统,还以根据实际情况选择多种引擎进行组合。
                InnoDB:是Mysql的默认引擎,支持事务、外键。如果应用对事务的完整性有较高的要求,在并发条件下要求数据的一致性,
                       数据操作除了查询之外,还包含很多的更新、删除操作、那么选择InndDB是比较合适的选择
                MyISAM:如果应用是以读操作插入操作为主,是有很少的更新和删除操作,并且对事务完整性、并发性要求不是很高,那么可以选择这个存储引擎
                        如日志数据和评论数据。
                Memory:将所有数据存储在内存中,访问速度快,通常用于临时表以及缓存。Memory表的缺陷就是对表的大小有限制,太大的表无法缓存在内存中,而且无法保障数据的安全性    
Mysql索引
    1>.分类
        按照数据结构类型分类:
            B+Tree索引:
                为什么不用二叉树?
                    二叉树顺序插入时,会形成一个链表,查询性能大大降低。大量数据情况下,层级较深 ,检索速度慢。
            Hash索引:只支持根据key的hash值来进行精确查询,不支持范围查询,不支持排序,优点是查询效率高,不出现哈希碰撞的前
                        提下只需要一次检索就可以查询到需要的数据
            R-Tree索引:主要用于地理空间数据
            Full-Text:全文索引
        按照主建用途分类:
            主键索引、唯一索引、常规索引、全文索引
        按照节点存储内容不同分类:
            聚集索引:节点存储内容是ID和一整行的数据
            二级索引(辅助索引):节点存储内容是某几个字段的内容和ID
    2>.创建索引:
        语法:
            CREATE [UNIQUE/FULLTEXT] IDNEX index_name ON table_name(index_colname,index_colname...);
            单列索引:只为某一列创建的索引
            组合索引/联合索引:为多列创建的索引
            index_name命名规范:idx_字段名
            eg:为emp表中ename,job,mgr这三个字段创建联合索引
            CREATE INDEX IDX_ENAME_JOB_MGR ON EMP(ENAME,JOB,MGR);
            为emp表中empno创建(单列索引):
            CREATE INDEX IDX_EMPNO ON EMP(EMPNO);
    3>.查看索引
        语法:
            SHOW INDEX FROM TABLE_NAME;
            SHOW INDEX FROM USER;
    4>.删除索引
        语法:
            DROP INDEX INDEX_NAME ON TABLE_NAME;
            DROP INDEX IDX_NAME ON USER;
    5>.回表查询
        SELECT * FROM EMP WHERE ENAME = 'SCOTT';
        回表查询过程:
            1>.根据name的值在二级索引的B+Tree中查询到ID值
            2>.根据得到的ID值在聚集索引中查询该行数据
        二级索引叶子节点中存放的是字段的值和ID
        聚集索引叶子节点中存放的是字段的ID和该ID对应的那一行数据
    6>.索引使用法则(SQL优化:指的是建立索提高查询效率,但是索引会降低增删改的效率)
        总的原则;建立索引并且避免索引失效,在使用了索引的前提下避免回表查询
        创建测试用表:
            #创建一个表,里面有四个字段ID,NAME,AGE,SCHOOL
            CREATE TABLE USER(
                ID INT PRIMARY KEY AUTO_INCREMENT,
                NAME VARCHAR(20),
                AGE INT,
                SCHOOL VARCHAR(20),
                HOME VARCHAR(20)
            );    
            #插入数据方式一:不使用主键自增长
            INSERT INTO USER VALUES(1,'张三',20,'UFE','西安');
            INSERT INTO USER VALUES(2,'张三',30,'UFE','北京');
            INSERT INTO USER VALUES(3,'张三',40,'UFE','上海');    
            INSERT INTO USER VALUES(4,'李四',40,'XDU','上海');
            #插入数据方式一:使用主键自增长
            INSERT INTO USER(NAME,AGE,SCHOOL,HOME) VALUES('张三',20,'UFE','西安');
            INSERT INTO USER(NAME,AGE,SCHOOL,HOME) VALUES('张三',30,'UFE','北京');
            INSERT INTO USER(NAME,AGE,SCHOOL,HOME) VALUES('张三',40,'UFE','上海');
            INSERT INTO USER(NAME,AGE,SCHOOL,HOME) VALUES('李四',40,'XDU','上海');
            #为NAME、AGE、SCHOOL这三个字段创建索引
            CREATE INDEX IDX_NAME_AGE_SCHOOL ON USER(NAME,AGE,SCHOOL);
            #为NAME字段创建单独的索引
            CREATE INDEX IDX_NAME ON USER(NAME);
            测试表特别说明:
            1>>.以下测试值针对于表中有ID,NAME,AGE,SCHOOL,HOME五个字段,仅为中间三个字段NAME,AGE,SCHOOL创建了索引的情况
              如果为NAME,AGE,SCHOOL,HOME四个字段创建索引,则测试情况和下面测试结果有所不同
            2>>.NAME索引长度为83,AGE索引长度为5,SCHOOL索引长度为83,索引总长度为83+5+83=171
            索引使用特别说明:下面两种查询列顺序不同的SQL不会对索引产生影响
                SELECT * FROM USER WHERE NAME = '张三'  AND AGE = 30 AND  SCHOOL = 'UFE';
                SELECT * FROM USER WHERE AGE = 30 AND  SCHOOL = 'UFE' AND  NAME = '张三';
        1>>.最左前缀法:查询返回结果中索引最左边的字段(NAME)必须出现在SQL中,在SQL中的位置无限制如:
            SELECT * FROM USER WHERE NAME = '张三' AND AGE = 20 AND SCHOOL = 'UFE';和
            SELECT * FROM USER WHERE AGE = 20 AND SCHOOL = 'UFE' AND NAME = '张三';
            这两种写法索引都是可以生效的
            如果索引了多列(使用了联合索引),最左前缀法则指的是查询从索引的最左列开始,且不跳过索引中的列,如果跳过了
            某一列,则索引部分失效(后面的字段索引),即如果想要让索引生效,则最左边的列必须出现在查询字段中
                                    
            #索引全部生效示例1:
            SELECT * FROM USER WHERE NAME = '10' AND AGE = 20 AND SCHOOL = 'UFE';            
            #测试索引是否生效:索引全部生效,key字段的值不为NULL,ref有三个值,key_len=171 
            EXPLAIN SELECT * FROM USER WHERE NAME = '张三' AND AGE = 20 AND SCHOOL = 'UFE';
            #SQL执行结果:
            mysql> EXPLAIN SELECT * FROM USER WHERE NAME = '张三' AND AGE = 20 AND SCHOOL = 'UFE';
            +----+-------------+-------+------------+------+---------------------+---------------------+---------+-------------------+------+----------+-------------+
            | id | select_type | table | partitions | type | possible_keys       | key                 | key_len | ref               | rows | filtered | Extra       |
            +----+-------------+-------+------------+------+---------------------+---------------------+---------+-------------------+------+----------+-------------+
            |  1 | SIMPLE      | USER  | NULL       | ref  | IDX_NAME_AGE_SCHOOL | IDX_NAME_AGE_SCHOOL | 171     | const,const,const |    1 |   100.00 | Using index |
            +----+-------------+-------+------------+------+---------------------+---------------------+---------+-------------------+------+----------+-------------+
            1 row in set, 1 warning (0.00 sec)
            
            #索引部分生效示例1:    (未使用SCHOOL字段索引)            
            SELECT * FROM USER WHERE NAME = '张三' AND AGE = 20;
            #查看索引是否生效:索引部分生效,key字段的值不为NULL,ref有两个值,SCHOOL字段索引未生效,key_len=88
            EXPLAIN SELECT * FROM USER WHERE NAME = '张三' AND AGE = 20;
            #SQL执行结果
            mysql> EXPLAIN SELECT * FROM USER WHERE NAME = '张三' AND AGE = 20;
            +----+-------------+-------+------------+------+---------------------+---------------------+---------+-------------+------+----------+-------------+
            | id | select_type | table | partitions | type | possible_keys       | key                 | key_len | ref         | rows | filtered | Extra       |
            +----+-------------+-------+------------+------+---------------------+---------------------+---------+-------------+------+----------+-------------+
            |  1 | SIMPLE      | USER  | NULL       | ref  | IDX_NAME_AGE_SCHOOL | IDX_NAME_AGE_SCHOOL | 88      | const,const |    1 |   100.00 | Using index |
            +----+-------------+-------+------------+------+---------------------+---------------------+---------+-------------+------+----------+-------------+
            1 row in set, 1 warning (0.00 sec)
            
            #索引部分生效示例2:(跳过了AGE字段的索引,违反了最左前缀法则)
            SELECT * FROM USER WHERE NAME = '张三' AND SCHOOL = 'UFE';
            #查看索引是否生效:索引部分失效,key字段的值不为NULL,ref有一个值,AGE和SCHOOL字段的索引失效了,key_len=83
            EXPLAIN SELECT * FROM USER WHERE NAME = '张三' AND SCHOOL = 'UFE';
            #SQL执行结果
            mysql> EXPLAIN SELECT * FROM USER WHERE NAME = '张三' AND SCHOOL = 'UFE';
            +----+-------------+-------+------------+------+---------------------+---------------------+---------+-------+------+----------+--------------------------+
            | id | select_type | table | partitions | type | possible_keys       | key                 | key_len | ref   | rows | filtered | Extra                    |
            +----+-------------+-------+------------+------+---------------------+---------------------+---------+-------+------+----------+--------------------------+
            |  1 | SIMPLE      | USER  | NULL       | ref  | IDX_NAME_AGE_SCHOOL | IDX_NAME_AGE_SCHOOL | 83      | const |    1 |   100.00 | Using where; Using index |
            +----+-------------+-------+------------+------+---------------------+---------------------+---------+-------+------+----------+--------------------------+
            1 row in set, 1 warning (0.00 sec)
            
            #索引全部失效示例1:(跳过了NAME字段索引,违反了最左前缀法则,NAME、AGE和SCHOOL字段的索引失效了)
            SELECT * FROM USER WHERE AGE = 20 AND SCHOOL = 'UFE';
            #查看索引是否生效:索引全部失效,key字段的值为NULL,key_len=0,type=all代表全表扫描
            EXPLAIN SELECT * FROM USER WHERE AGE = 20 AND SCHOOL = 'UFE';
            #SQL执行结果:
            mysql> EXPLAIN SELECT * FROM USER WHERE AGE = 20 AND SCHOOL = 'UFE';
            +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
            | id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
            +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
            |  1 | SIMPLE      | USER  | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    1 |   100.00 | Using where |
            +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
            1 row in set, 1 warning (0.00 sec)
            
            #索引全部失效示例2:(跳过了NAME字段、AGE字段索引,违反了最左前缀法则,NAME、AGE和SCHOOL字段的索引失效了)
            SELECT * FROM USER WHERE SCHOOL = 'UFE';
            #查看索引是否生效:索引全部失效,key字段的值为NULL,key_len=0,type=all代表全表扫描
            EXPLAIN SELECT * FROM USER WHERE SCHOOL = 'UFE';
            #SQL执行结果:
            mysql> EXPLAIN SELECT * FROM USER WHERE SCHOOL = 'UFE';
            +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
            | id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
            +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
            |  1 | SIMPLE      | USER  | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    1 |   100.00 | Using where |
            +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
            1 row in set, 1 warning (0.00 sec)
                            
            #索引全部失效示例3:(HOME字段未使用索引)
            SELECT * FROM USER WHERE HOME = 'UFE';
            #查看索引是否生效:索引全部失效,key字段的值为NULL,key_len=0
            EXPLAIN SELECT * FROM USER WHERE HOME = 'UFE';
            #SQL执行结果:
            mysql> EXPLAIN SELECT * FROM USER WHERE HOME = 'UFE';
            +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
            | id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
            +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
            |  1 | SIMPLE      | USER  | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    1 |   100.00 | Using where |
            +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
            1 row in set, 1 warning (0.00 sec)
            
        2>>.范围查询 
            联合索引中,出现在范围查询(>,<)列右侧的列索引全部失效,
            特别注意:
                与范围查询条件在SQL中的位置无关,这个右侧指的是创建索引时创建顺序的右侧,即CREATE INDEX IDX_NAME_AGE_SCHOOL ON USER(NAME,AGE,SCHOOL);中AGE右侧的SCHOOL索引会失效,>=,<=范围查询右侧的列索引不会失效,如:            
                #key_len = 88 SQL写法1
                EXPLAIN SELECT * FROM USER WHERE NAME = '张三' AND AGE > 30 AND SCHOOL = 'UFE'; 
                #key_len = 88 SQL写法2
                EXPLAIN SELECT * FROM USER WHERE NAME = '张三' AND SCHOOL = 'UFE' AND AGE > 30;
                        
            #范围查询右侧的列索引全部失效示例1:
            SELECT * FROM USER WHERE NAME = '张三' AND AGE > 30 AND SCHOOL = 'UFE';
            #查看索引是否生效:索引部分生效,key_len=88,如果索引全部生效,则key_len=171
            EXPLAIN SELECT * FROM USER WHERE NAME = '张三' AND AGE > 30 AND SCHOOL = 'UFE';
            #SQL执行结果:
            mysql> EXPLAIN SELECT * FROM USER WHERE NAME = '张三' AND AGE > 30 AND SCHOOL = 'UFE';
            +----+-------------+-------+------------+-------+---------------------+---------------------+---------+------+------+----------+-----------------------+
            | id | select_type | table | partitions | type  | possible_keys       | key                 | key_len | ref  | rows | filtered | Extra                 |
            +----+-------------+-------+------------+-------+---------------------+---------------------+---------+------+------+----------+-----------------------+
            |  1 | SIMPLE      | USER  | NULL       | range | IDX_NAME_AGE_SCHOOL | IDX_NAME_AGE_SCHOOL | 88      | NULL |    1 |    33.33 | Using index condition |
            +----+-------------+-------+------------+-------+---------------------+---------------------+---------+------+------+----------+-----------------------+
            1 row in set, 1 warning (0.00 sec)
            
            #范围查询右侧的列索引全部生效示例1:(在业务允许的情况下将SQL中的>改为>=,将<改为<=)
            SELECT * FROM USER WHERE NAME = '张三' AND AGE >= 30 AND SCHOOL = 'UFE';
            #查看索引是否生效:索引部分生效,key_len=171,如果索引全部生效,则key_len=171
            EXPLAIN SELECT * FROM USER WHERE NAME = '张三' AND AGE >= 30 AND SCHOOL = 'UFE';
            #SQL执行结果:
            mysql> EXPLAIN SELECT * FROM USER WHERE NAME = '张三' AND AGE >= 30 AND SCHOOL = 'UFE';
            +----+-------------+-------+------------+-------+---------------------+---------------------+---------+------+------+----------+-----------------------+
            | id | select_type | table | partitions | type  | possible_keys       | key                 | key_len | ref  | rows | filtered | Extra                 |
            +----+-------------+-------+------------+-------+---------------------+---------------------+---------+------+------+----------+-----------------------+
            |  1 | SIMPLE      | USER  | NULL       | range | IDX_NAME_AGE_SCHOOL | IDX_NAME_AGE_SCHOOL | 171     | NULL |    2 |    33.33 | Using index condition |
            +----+-------------+-------+------------+-------+---------------------+---------------------+---------+------+------+----------+-----------------------+
            1 row in set, 1 warning (0.00 sec)
        3>>.不要在索引列上进行运算或函数操作(等号左边),否则索引列将会失效,具体是在哪一列上进行了运算,则从那个列开始包括那个列上的索引就开始失效,这个失效的索引列与SQL的 
            书写顺序无关,只与索引创建顺序有关,索引的创建顺序为CREATE INDEX IDX_NAME_AGE_SCHOOL ON USER(NAME,AGE,SCHOOL);,即在AGE列上进行计算操作后,    
                SELECT * FROM USER WHERE NAME  = '张三' AND SUBSTR(AGE,1,1) = 2 AND SCHOOL = 'UFE';
                SELECT * FROM USER WHERE NAME  = '张三' AND SCHOOL = 'UFE' AND SUBSTR(AGE,1,1) = 2;
                上面两个SQL中的AGE,SCHOOL列的索引都会失效
             1>>>.隐式类型转换有时也会导致索引失效    
                
                #原来的sql,age为INT类型,NAME为VARCHAR类型,索引生效
                EXPLAIN SELECT * FROM USER WHERE NAME = '张三' AND AGE = 30 AND SCHOOL = 'UFE';
                #SQL执行结果:
                mysql> EXPLAIN SELECT * FROM USER WHERE NAME = '张三' AND AGE = 30 AND SCHOOL = 'UFE';
                +----+-------------+-------+------------+------+---------------------+---------------------+---------+-------------------+------+----------+-------+
                | id | select_type | table | partitions | type | possible_keys       | key                 | key_len | ref               | rows | filtered | Extra |
                +----+-------------+-------+------------+------+---------------------+---------------------+---------+-------------------+------+----------+-------+
                |  1 | SIMPLE      | USER  | NULL       | ref  | IDX_NAME_AGE_SCHOOL | IDX_NAME_AGE_SCHOOL | 171     | const,const,const |    1 |   100.00 | NULL  |
                +----+-------------+-------+------------+------+---------------------+---------------------+---------+-------------------+------+----------+-------+
                1 row in set, 1 warning (0.00 sec)
                
                #隐式类型转换:将VARCHAR类型AGE转为数据库需要的INT类型AGE,索引生效
                EXPLAIN SELECT * FROM USER WHERE NAME = '张三' AND AGE = '30' AND SCHOOL = 'UFE';
                #SQL执行结果:
                mysql> EXPLAIN SELECT * FROM USER WHERE NAME = '张三' AND AGE = '30' AND SCHOOL = 'UFE';
                +----+-------------+-------+------------+------+---------------------+---------------------+---------+-------------------+------+----------+-------+
                | id | select_type | table | partitions | type | possible_keys       | key                 | key_len | ref               | rows | filtered | Extra |
                +----+-------------+-------+------------+------+---------------------+---------------------+---------+-------------------+------+----------+-------+
                |  1 | SIMPLE      | USER  | NULL       | ref  | IDX_NAME_AGE_SCHOOL | IDX_NAME_AGE_SCHOOL | 171     | const,const,const |    1 |   100.00 | NULL  |
                +----+-------------+-------+------------+------+---------------------+---------------------+---------+-------------------+------+----------+-------+
                1 row in set, 1 warning (0.00 sec)


                #隐式类型转换:将INT类型NAME转为数据库需要的VARCHAR类型NAME,索引会失效,由被操作的列决定索引全部失效还是部分失效
                EXPLAIN SELECT * FROM USER WHERE NAME = 5 AND AGE = '30' AND SCHOOL = 'UFE';
                #SQL执行结果:
                mysql> EXPLAIN SELECT * FROM USER WHERE NAME = 5 AND AGE = '30' AND SCHOOL = 'UFE';
                +----+-------------+-------+------------+------+---------------------+------+---------+------+------+----------+-------------+
                | id | select_type | table | partitions | type | possible_keys       | key  | key_len | ref  | rows | filtered | Extra       |
                +----+-------------+-------+------------+------+---------------------+------+---------+------+------+----------+-------------+
                |  1 | SIMPLE      | USER  | NULL       | ALL  | IDX_NAME_AGE_SCHOOL | NULL | NULL    | NULL |    3 |    33.33 | Using where |
                +----+-------------+-------+------------+------+---------------------+------+---------+------+------+----------+-------------+
                1 row in set, 3 warnings (0.00 sec)
                
            2>>>.模糊查询有时也会导致索引失效:尾部模糊匹配索引生效,头部模糊匹配索引会失效,由被操作的列决定索引全部失效还是部分失效
                所以在大数据量查询的业务中要尽量避免使用头部模糊匹配
                
                #尾部模糊匹配索引生效1
                EXPLAIN SELECT * FROM USER WHERE NAME LIKE '张%' AND AGE = 30 AND SCHOOL AND SCHOOL = 'UFE';
                #SQL执行结果:
                mysql> EXPLAIN SELECT * FROM USER WHERE NAME LIKE '张%' AND AGE = 30 AND SCHOOL AND SCHOOL = 'UFE';
                +----+-------------+-------+------------+-------+---------------------+---------------------+---------+------+------+----------+-----------------------+
                | id | select_type | table | partitions | type  | possible_keys       | key                 | key_len | ref  | rows | filtered | Extra                 |
                +----+-------------+-------+------------+-------+---------------------+---------------------+---------+------+------+----------+-----------------------+
                |  1 | SIMPLE      | USER  | NULL       | range | IDX_NAME_AGE_SCHOOL | IDX_NAME_AGE_SCHOOL | 171     | NULL |    3 |    33.33 | Using index condition |
                +----+-------------+-------+------------+-------+---------------------+---------------------+---------+------+------+----------+-----------------------+
                1 row in set, 2 warnings (0.00 sec)
                
                #尾部模糊匹配索引生效2
                EXPLAIN SELECT * FROM USER WHERE NAME LIKE '张_' AND AGE = 30 AND SCHOOL AND SCHOOL = 'UFE';
                #SQL执行结果:
                mysql> EXPLAIN SELECT * FROM USER WHERE NAME LIKE '张_' AND AGE = 30 AND SCHOOL AND SCHOOL = 'UFE';
                +----+-------------+-------+------------+-------+---------------------+---------------------+---------+------+------+----------+-----------------------+
                | id | select_type | table | partitions | type  | possible_keys       | key                 | key_len | ref  | rows | filtered | Extra                 |
                +----+-------------+-------+------------+-------+---------------------+---------------------+---------+------+------+----------+-----------------------+
                |  1 | SIMPLE      | USER  | NULL       | range | IDX_NAME_AGE_SCHOOL | IDX_NAME_AGE_SCHOOL | 171     | NULL |    3 |    33.33 | Using index condition |
                +----+-------------+-------+------------+-------+---------------------+---------------------+---------+------+------+----------+-----------------------+
                1 row in set, 2 warnings (0.00 sec)
                
                #头部模糊匹配索引会失效1,由被操作的列决定索引全部失效还是部分失效
                EXPLAIN SELECT * FROM USER WHERE NAME LIKE '%三' AND AGE = 30 AND SCHOOL AND SCHOOL = 'UFE';
                #SQL执行结果:
                mysql> EXPLAIN SELECT * FROM USER WHERE NAME LIKE '%三' AND AGE = 30 AND SCHOOL AND SCHOOL = 'UFE';
                +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
                | id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
                +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
                |  1 | SIMPLE      | USER  | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    3 |    33.33 | Using where |
                +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
                1 row in set, 2 warnings (0.00 sec)
                
                #头部模糊匹配索引会失效2,由被操作的列决定索引全部失效还是部分失效
                EXPLAIN SELECT * FROM USER WHERE NAME LIKE '_三' AND AGE = 30 AND SCHOOL AND SCHOOL = 'UFE';
                #SQL执行结果:
                mysql> EXPLAIN SELECT * FROM USER WHERE NAME LIKE '_三' AND AGE = 30 AND SCHOOL AND SCHOOL = 'UFE';
                +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
                | id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
                +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
                |  1 | SIMPLE      | USER  | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    3 |    33.33 | Using where |
                +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
                1 row in set, 2 warnings (0.00 sec)
                
                #头部尾部同时模糊匹配包括尾部模糊匹配1,所以索引会失效,由被操作的列决定索引全部失效还是部分失效
                EXPLAIN SELECT * FROM USER WHERE NAME = '张三' AND AGE = 30 AND SCHOOL AND SCHOOL LIKE '%F%';
                #SQL执行结果:
                mysql> EXPLAIN SELECT * FROM USER WHERE NAME = '张三' AND AGE = 30 AND SCHOOL AND SCHOOL LIKE '%F%';
                +----+-------------+-------+------------+------+---------------------+---------------------+---------+-------------+------+----------+-----------------------+
                | id | select_type | table | partitions | type | possible_keys       | key                 | key_len | ref         | rows | filtered | Extra                 |
                +----+-------------+-------+------------+------+---------------------+---------------------+---------+-------------+------+----------+-----------------------+
                |  1 | SIMPLE      | USER  | NULL       | ref  | IDX_NAME_AGE_SCHOOL | IDX_NAME_AGE_SCHOOL | 88      | const,const |    1 |    33.33 | Using index condition |
                +----+-------------+-------+------------+------+---------------------+---------------------+---------+-------------+------+----------+-----------------------+
                1 row in set, 2 warnings (0.00 sec)
                
                #头部尾部同时模糊匹配包括尾部模糊匹配2,所以索引会失效,由被操作的列决定索引全部失效还是部分失效
                EXPLAIN SELECT * FROM USER WHERE NAME = '张三' AND AGE = 30 AND SCHOOL AND SCHOOL LIKE '_F_';
                #SQL执行结果:
                mysql> EXPLAIN SELECT * FROM USER WHERE NAME = '张三' AND AGE = 30 AND SCHOOL AND SCHOOL LIKE '_F_';
                +----+-------------+-------+------------+------+---------------------+---------------------+---------+-------------+------+----------+-----------------------+
                | id | select_type | table | partitions | type | possible_keys       | key                 | key_len | ref         | rows | filtered | Extra                 |
                +----+-------------+-------+------------+------+---------------------+---------------------+---------+-------------+------+----------+-----------------------+
                |  1 | SIMPLE      | USER  | NULL       | ref  | IDX_NAME_AGE_SCHOOL | IDX_NAME_AGE_SCHOOL | 88      | const,const |    1 |    33.33 | Using index condition |
                +----+-------------+-------+------------+------+---------------------+---------------------+---------+-------------+------+----------+-----------------------+
                1 row in set, 2 warnings (0.00 sec)    
                
            3>>>.在索引列上进行运算或函数操作    
                #在索引列上进行运算操作导致索引全部失效示例1:对NAME列进行了函数截取操作,导致NAME,AGE,SCHOOL列索引全部失效
                SELECT * FROM USER WHERE SUBSTR(NAME,1,1) = '张';
                #查看索引是否生效:索引全部失效
                EXPLAIN SELECT * FROM USER WHERE SUBSTR(NAME,1,1) = '张';
                #SQL执行结果:
                mysql> EXPLAIN SELECT * FROM USER WHERE SUBSTR(NAME,1,1) = '张';
                +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
                | id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
                +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
                |  1 | SIMPLE      | USER  | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    3 |   100.00 | Using where |
                +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
                1 row in set, 1 warning (0.00 sec)
                
                #在索引列上进行运算操作导致索引部分失效示例1:对SCHOOL列进行了函数截取操作,导致SCHOOL列索引失效
                SELECT * FROM USER WHERE NAME  = '张三' AND AGE = 20 AND SUBSTR(SCHOOL,1,1) = 'U';
                #查看索引是否生效:索引部分失效
                EXPLAIN SELECT * FROM USER WHERE NAME  = '张三' AND AGE = 20 AND SUBSTR(SCHOOL,1,1) = 'U';
                #SQL执行结果:
                mysql> EXPLAIN SELECT * FROM USER WHERE NAME  = '张三' AND AGE = 20 AND SUBSTR(SCHOOL,1,1) = 'U';
                +----+-------------+-------+------------+------+---------------------+---------------------+---------+-------------+------+----------+-----------------------+
                | id | select_type | table | partitions | type | possible_keys       | key                 | key_len | ref         | rows | filtered | Extra                 |
                +----+-------------+-------+------------+------+---------------------+---------------------+---------+-------------+------+----------+-----------------------+
                |  1 | SIMPLE      | USER  | NULL       | ref  | IDX_NAME_AGE_SCHOOL | IDX_NAME_AGE_SCHOOL | 88      | const,const |    1 |   100.00 | Using index condition |
                +----+-------------+-------+------------+------+---------------------+---------------------+---------+-------------+------+----------+-----------------------+
                1 row in set, 1 warning (0.00 sec)
                    
                #在索引列上进行运算操作导致索引部分失效示例2:(SQL中查询的字段按照创建索引的顺序进行排列):对AGE列进行了函数截取操作,导致AGE,SCHOOL列索引失效
                SELECT * FROM USER WHERE NAME  = '张三' AND SUBSTR(AGE,1,1) = 2 AND SCHOOL = 'UFE';
                #查看索引是否生效:索引部分失效,key_len=83
                EXPLAIN SELECT * FROM USER WHERE NAME  = '张三' AND SUBSTR(AGE,1,1) = 2 AND SCHOOL = 'UFE';
                #SQL执行结果:
                mysql> EXPLAIN SELECT * FROM USER WHERE NAME  = '张三' AND SUBSTR(AGE,1,1) = 2 AND SCHOOL = 'UFE';
                +----+-------------+-------+------------+------+---------------------+---------------------+---------+-------+------+----------+-----------------------+
                | id | select_type | table | partitions | type | possible_keys       | key                 | key_len | ref   | rows | filtered | Extra                 |
                +----+-------------+-------+------------+------+---------------------+---------------------+---------+-------+------+----------+-----------------------+
                |  1 | SIMPLE      | USER  | NULL       | ref  | IDX_NAME_AGE_SCHOOL | IDX_NAME_AGE_SCHOOL | 83      | const |    3 |    33.33 | Using index condition |
                +----+-------------+-------+------------+------+---------------------+---------------------+---------+-------+------+----------+-----------------------+
                1 row in set, 1 warning (0.00 sec)
                
                #在索引列上进行运算操作导致索引部分失效示例3:(SQL中查询的字段没有按照创建索引的顺序进行排列):对AGE列进行了函数截取操作,导致AGE,SCHOOL列索引失效
                SELECT * FROM USER WHERE NAME  = '张三' AND SCHOOL = 'UFE' AND SUBSTR(AGE,1,1) = 2;
                #查看索引是否生效:索引部分失效,key_len=83
                EXPLAIN SELECT * FROM USER WHERE NAME  = '张三' AND SCHOOL = 'UFE' AND SUBSTR(AGE,1,1) = 2;
                #SQL执行结果:
                mysql> EXPLAIN SELECT * FROM USER WHERE NAME  = '张三' AND SCHOOL = 'UFE' AND SUBSTR(AGE,1,1) = 2;
                +----+-------------+-------+------------+------+---------------------+---------------------+---------+-------+------+----------+-----------------------+
                | id | select_type | table | partitions | type | possible_keys       | key                 | key_len | ref   | rows | filtered | Extra                 |
                +----+-------------+-------+------------+------+---------------------+---------------------+---------+-------+------+----------+-----------------------+
                |  1 | SIMPLE      | USER  | NULL       | ref  | IDX_NAME_AGE_SCHOOL | IDX_NAME_AGE_SCHOOL | 83      | const |    3 |    33.33 | Using index condition |
                +----+-------------+-------+------------+------+---------------------+---------------------+---------+-------+------+----------+-----------------------+
                1 row in set, 1 warning (0.00 sec)
                
                #在索引列上进行运算操作导致索引部分失效示例4:对AGE列进行了加运算操作,导致AGE,SCHOOL列索引失效
                EXPLAIN SELECT * FROM USER WHERE NAME = '张三' AND AGE+10 = 30 AND SCHOOL = 'UFE';
                #查看索引是否生效:索引部分失效,在SCHOOL列上进行了运算,AGE列和SCHOOL列的索引失效,key_len=83
                EXPLAIN EXPLAIN SELECT * FROM USER WHERE NAME = '张三' AND AGE+10 = 30 AND SCHOOL = 'UFE';
                #SQL执行结果:            
                mysql> EXPLAIN SELECT * FROM USER WHERE NAME = '张三' AND AGE+10 = 30 AND SCHOOL = 'UFE';
                +----+-------------+-------+------------+------+---------------------+---------------------+---------+-------+------+----------+-----------------------+
                | id | select_type | table | partitions | type | possible_keys       | key                 | key_len | ref   | rows | filtered | Extra                 |
                +----+-------------+-------+------------+------+---------------------+---------------------+---------+-------+------+----------+-----------------------+
                |  1 | SIMPLE      | USER  | NULL       | ref  | IDX_NAME_AGE_SCHOOL | IDX_NAME_AGE_SCHOOL | 83      | const |    3 |    33.33 | Using index condition |
                +----+-------------+-------+------------+------+---------------------+---------------------+---------+-------+------+----------+-----------------------+
                1 row in set, 1 warning (0.00 sec)        
            
        4>>.OR连接的条件
            用Or分割开的条件,如果OR前的条件中有索引,而后面的列中没有索引,那么涉及到的索引都不会被用到,即Or会导致索引失效,
            改进方式是使用UNION代替OR来完成查询
            
            #使用OR索引依然生效1:OR连接两个主键索所在列
            SELECT * FROM USER WHERE ID = 1 OR ID = 2;
            #查看索引是否生效:索引生效
            EXPLAIN SELECT * FROM USER WHERE ID = 1 OR ID = 2;
            #SQL执行结果:    
            mysql> EXPLAIN SELECT * FROM USER WHERE ID = 1 OR ID = 2;
            +----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
            | id | select_type | table | partitions | type  | possible_keys | key     | key_len | ref  | rows | filtered | Extra       |
            +----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
            |  1 | SIMPLE      | USER  | NULL       | range | PRIMARY       | PRIMARY | 4       | NULL |    2 |   100.00 | Using where |
            +----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
            1 row in set, 1 warning (0.00 sec)

            #使用OR索引依然生效2:OR连接两个联合索引所在列
            SELECT * FROM USER WHERE NAME = '张三' OR NAME = '李四';
            #查看索引是否生效:索引生效
            EXPLAIN SELECT * FROM USER WHERE NAME = '张三' OR NAME = '李四';
            #SQL执行结果:    
            mysql> EXPLAIN SELECT * FROM USER WHERE NAME = '张三' OR NAME = '李四';
            +----+-------------+-------+------------+-------+---------------------+---------------------+---------+------+------+----------+-----------------------+
            | id | select_type | table | partitions | type  | possible_keys       | key                 | key_len | ref  | rows | filtered | Extra                 |
            +----+-------------+-------+------------+-------+---------------------+---------------------+---------+------+------+----------+-----------------------+
            |  1 | SIMPLE      | USER  | NULL       | range | IDX_NAME_AGE_SCHOOL | IDX_NAME_AGE_SCHOOL | 83      | NULL |    4 |   100.00 | Using index condition |
            +----+-------------+-------+------------+-------+---------------------+---------------------+---------+------+------+----------+-----------------------+
            1 row in set, 1 warning (0.00 sec)

            #使用OR索引依然生效3:OR连接主键索引所在列和联合索引首个字段所在列
            SELECT * FROM USER WHERE ID = 1 OR NAME = '李四';
            #查看索引是否生效:索引生效
            EXPLAIN SELECT * FROM USER WHERE ID = 1 OR NAME = '李四';
            #SQL执行结果:
            mysql> EXPLAIN SELECT * FROM USER WHERE ID = 1 OR NAME = '李四';
            +----+-------------+-------+------------+-------------+-----------------------------+-----------------------------+---------+------+------+----------+------------------------------------------------------------+
            | id | select_type | table | partitions | type        | possible_keys               | key                         | key_len | ref  | rows | filtered | Extra                                                      |
            +----+-------------+-------+------------+-------------+-----------------------------+-----------------------------+---------+------+------+----------+------------------------------------------------------------+
            |  1 | SIMPLE      | USER  | NULL       | index_merge | PRIMARY,IDX_NAME_AGE_SCHOOL | IDX_NAME_AGE_SCHOOL,PRIMARY | 83,4    | NULL |    2 |   100.00 | Using sort_union(IDX_NAME_AGE_SCHOOL,PRIMARY); Using where |
            +----+-------------+-------+------------+-------------+-----------------------------+-----------------------------+---------+------+------+----------+------------------------------------------------------------+
            1 row in set, 1 warning (0.00 sec)    
                    
            #OR导致索引全部失效1:OR连接主键索引所在列和非联合索引首个字段所在列,会全表扫描
            SELECT * FROM USER WHERE ID = 1 OR AGE = 30;
            #查看索引是否生效:索引全部失效,key的值为NULL
            EXPLAIN SELECT * FROM USER WHERE ID = 1 OR AGE = 30;
            #SQL执行结果:
            mysql> EXPLAIN SELECT * FROM USER WHERE ID = 1 OR AGE = 30;
            +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
            | id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
            +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
            |  1 | SIMPLE      | USER  | NULL       | ALL  | PRIMARY       | NULL | NULL    | NULL |    4 |    50.00 | Using where |
            +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
            1 row in set, 1 warning (0.00 sec)

            #OR导致索引全部失效2:OR连接联合索引中不同字段所在列,会全表扫描
            SELECT * FROM USER WHERE NAME = '张三' AND AGE = 30 OR SCHOOL = 'XDU';
            #查看索引是否生效:索引全部失效,key的值为NULL
            EXPLAIN SELECT * FROM USER WHERE NAME = '张三' AND AGE = 30 OR SCHOOL = 'XDU';
            #SQL执行结果:    
            mysql> EXPLAIN SELECT * FROM USER WHERE NAME = '张三' AND AGE = 30 OR SCHOOL = 'XDU';
            +----+-------------+-------+------------+------+---------------------+------+---------+------+------+----------+-------------+
            | id | select_type | table | partitions | type | possible_keys       | key  | key_len | ref  | rows | filtered | Extra       |
            +----+-------------+-------+------------+------+---------------------+------+---------+------+------+----------+-------------+
            |  1 | SIMPLE      | USER  | NULL       | ALL  | IDX_NAME_AGE_SCHOOL | NULL | NULL    | NULL |    4 |    39.06 | Using where |
            +----+-------------+-------+------------+------+---------------------+------+---------+------+------+----------+-------------+
            1 row in set, 1 warning (0.00 sec)
                
            #OR导致索引全部失效2:OR连接的列中出现未建立索引的列,会全表扫描
            SELECT * FROM USER WHERE ID = 1 OR HOME = '上海';
            #查看索引是否生效:索引全部失效,key的值为NULL
            EXPLAIN SELECT * FROM USER WHERE ID = 1 OR HOME = '上海';
            #SQL执行结果:    
            mysql> EXPLAIN SELECT * FROM USER WHERE ID = 1 OR HOME = '上海';
            +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
            | id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
            +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
            |  1 | SIMPLE      | USER  | NULL       | ALL  | PRIMARY       | NULL | NULL    | NULL |    4 |    50.00 | Using where |
            +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
            1 row in set, 1 warning (0.00 sec)
            
            #使用UNION关键字代替OR:解决了索引不生效的问题
            SELECT * FROM USER WHERE ID = 1 UNION SELECT * FROM USER WHERE HOME = '上海';
            #查看索引是否生效:索引生效
            EXPLAIN SELECT * FROM USER WHERE ID = 1 UNION SELECT * FROM USER WHERE HOME = '上海';
            #SQL执行结果:
            mysql> EXPLAIN SELECT * FROM USER WHERE ID = 1 UNION SELECT * FROM USER WHERE HOME = '上海';
            +----+--------------+------------+------------+-------+---------------+---------+---------+-------+------+----------+-----------------+
            | id | select_type  | table      | partitions | type  | possible_keys | key     | key_len | ref   | rows | filtered | Extra           |
            +----+--------------+------------+------------+-------+---------------+---------+---------+-------+------+----------+-----------------+
            |  1 | PRIMARY      | USER       | NULL       | const | PRIMARY       | PRIMARY | 4       | const |    1 |   100.00 | NULL            |
            |  2 | UNION        | USER       | NULL       | ALL   | NULL          | NULL    | NULL    | NULL  |    4 |    25.00 | Using where     |
            | NULL | UNION RESULT | <union1,2> | NULL       | ALL   | NULL          | NULL    | NULL    | NULL  | NULL |     NULL | Using temporary |
            +----+--------------+------------+------------+-------+---------------+---------+---------+-------+------+----------+-----------------+
            3 rows in set, 1 warning (0.00 sec)            
            
        5>>.数据分布影响
            如果Mysql评估使用索引比全表扫描更慢,则不使用索引
            
            #Mysql自动自动评估后,不使用索引,使用全表扫描
            SELECT * FROM USER;
            #查看索引是否生效:索引没有生效,key=NULL
            EXPLAIN SELECT * FROM USER;
            #SQL执行结果:
            mysql> EXPLAIN SELECT * FROM USER;
            +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------+
            | id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra |
            +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------+
            |  1 | SIMPLE      | USER  | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    4 |   100.00 | NULL  |
            +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------+
            1 row in set, 1 warning (0.00 sec)
        6>>.sql提示
            sql提示,是优化数据库的一个重要手段,就是在SQL语句中加入一些提示来达到优化操作的目的
            例如为NAME字段创建一个索引:CREATE INDEX IDX_NAME ON USER(NAME);
            这时NAME字段拥有两个索引,一个是普通索引IDX_NAME,一个是联合索引IDX_NAME_AGE_SCHOOL
            
            #默认情况下,mysql会自动选择一个索引
            SELECT * FROM USER WHERE NAME = '张三';
            #查看mysql使用了哪个索引:key=IDX_NAME_AGE_SCHOOL
            EXPLAIN SELECT * FROM USER WHERE NAME = '张三';
            #SQL执行结果:
            mysql> EXPLAIN SELECT NAME FROM USER;
            +----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+-------------+
            | id | select_type | table | partitions | type  | possible_keys | key      | key_len | ref  | rows | filtered | Extra       |
            +----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+-------------+
            |  1 | SIMPLE      | USER  | NULL       | index | NULL          | IDX_NAME | 83      | NULL |    4 |   100.00 | Using index |
            +----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+-------------+
            1 row in set, 1 warning (0.00 sec)
            
            #使用SQL提示让Mysql使用联合索引,但是Mysql不一定使用我们指定的index,mysql还会判断一下,我们指定的索引速度的确高,mysql就会使用我们指定的索引
            SELECT * FROM USER USE INDEX(IDX_NAME) WHERE NAME = '张三';
            #查看mysql使用了哪个索引:key=IDX_NAME
            EXPLAIN SELECT * FROM USER USE INDEX(IDX_NAME) WHERE NAME = '张三';
            #SQL执行结果:
            mysql> EXPLAIN SELECT NAME FROM USER USE INDEX(IDX_NAME);
            +----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+-------------+
            | id | select_type | table | partitions | type  | possible_keys | key      | key_len | ref  | rows | filtered | Extra       |
            +----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+-------------+
            |  1 | SIMPLE      | USER  | NULL       | index | NULL          | IDX_NAME | 83      | NULL |    4 |   100.00 | Using index |
            +----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+-------------+
            1 row in set, 1 warning (0.00 sec)
            
            #使用SQL提示让忽略某个索引:不忽略的情况下使用IDX_NAME这个索引,忽略了就使用IDX_NAME_AGE_SCHOOL
            SELECT * FROM USER IGNORE INDEX(IDX_NAME) WHERE NAME = '张三';
            #查看mysql使用了哪个索引:key=IDX_NAME_AGE_SCHOOL
            EXPLAIN SELECT * FROM USER IGNORE INDEX(IDX_NAME) WHERE NAME = '张三';
            #SQL执行结果:
            mysql> EXPLAIN SELECT NAME FROM USER IGNORE INDEX(IDX_NAME);
            +----+-------------+-------+------------+-------+---------------+---------------------+---------+------+------+----------+-------------+
            | id | select_type | table | partitions | type  | possible_keys | key                 | key_len | ref  | rows | filtered | Extra       |
            +----+-------------+-------+------------+-------+---------------+---------------------+---------+------+------+----------+-------------+
            |  1 | SIMPLE      | USER  | NULL       | index | NULL          | IDX_NAME_AGE_SCHOOL | 171     | NULL |    4 |   100.00 | Using index |
            +----+-------------+-------+------------+-------+---------------+---------------------+---------+------+------+----------+-------------+
            1 row in set, 1 warning (0.00 sec)
            
            #强制Mysql使用某个索引:强制Mysql使用IDX_NAME_AGE_SCHOOL,不强制的话Mysql会使用IDX_NAME这个索引
            SELECT * FROM USER FORCE INDEX(IDX_NAME) WHERE NAME = '张三';            
            #查看mysql使用了哪个索引:key=IDX_NAME_AGE_SCHOOL
            EXPLAIN SELECT * FROM USER FORCE INDEX(IDX_NAME) WHERE NAME = '张三';    
            #SQL执行结果:
            mysql> EXPLAIN SELECT NAME FROM USER FORCE INDEX(IDX_NAME_AGE_SCHOOL);
            +----+-------------+-------+------------+-------+---------------+---------------------+---------+------+------+----------+-------------+
            | id | select_type | table | partitions | type  | possible_keys | key                 | key_len | ref  | rows | filtered | Extra       |
            +----+-------------+-------+------------+-------+---------------+---------------------+---------+------+------+----------+-------------+
            |  1 | SIMPLE      | USER  | NULL       | index | NULL          | IDX_NAME_AGE_SCHOOL | 171     | NULL |    4 |   100.00 | Using index |
            +----+-------------+-------+------------+-------+---------------+---------------------+---------+------+------+----------+-------------+
            1 row in set, 1 warning (0.00 sec)
        
        7>>.覆盖索引(本质上是数据库底层只需要一次查询就可以直接返回数据,没有回表查询的过程,即只走二级索引,就称为覆盖索引)
            尽量使用覆盖索引(查询使用了索引,并且需要返回的列在索引中全部能找到),尽量避免使用select *,使用select *查询很容易会触发回表查询
            注意:覆盖索引不是没有使用索引,只是避免了回表查询
            
            #没有经过回表查询,因为在PRIMARY这个聚集索引中就可以找到ID这个数据,直接返回即可
            EXPLAIN SELECT ID FROM USER WHERE ID = 1;
            
            #没有经过回表查询,因为在IDX_NAME_AGE_SCHOOL这个辅助索引中就可以找到ID和NAME这两个数据,直接返回即可
            EXPLAIN SELECT ID,NAME FROM USER WHERE NAME = '张三';
            
            #经过了回表查询这个过程,因为首先使用了WHERE条件中用到的辅助索引IDX_NAME_AGE_SCHOOL,在这个索引中只能找到ID和NAME字段,再经过回表查询可以得到HOME字段的值
            EXPLAIN SELECT ID,NAME,HOME FROM USER WHERE NAME = '张三';
            
            所以当用不到HOME这个字段的时候,就不要使用SELECT *去查询,这样可能会导致回表查询,不回表查询的效率比额外经历一次回表查询的效率要高
            总结;第一步要建立索引,第二步要避免索引失效,第三步要尽量避免回表查询
        8>>.前缀索引:主要针对大文本类型的数据    
            当字段类型为字符串(varchar,text等)时,有时需要索引很长的字符串,这会让索引变得很大,查询时浪费大量IO,影响查询效率。此时可以只将字符串的一部分前缀
            建立索引,这样可以大大节约索引空间,从而提高索引效率。
            
            语法:CREATE INDEX INDEX_NAME ON TABLE_NAME(Column(n));
                n表示只取字段的前n个值建立索引
            索引长度:可以根据索引的选择性来决定,而选择性是指不重复的索引值(基数)和数据表记录总数的比值,索引选择性越高则查询效率越高,唯一索引的选择性是1,这是最好的索引选择性,性能也是最好的
            计算公式:
                不截取时的选择性 : SELECT COUNT(DISTINCT NAME)/COUNT(*) FROM USER;
                截取n个字符串时的选择性:SELECT COUNT(DISTINCT SUBSTR(NAME,1,n))/COUNT(*) FROM USER;
                截取n-1个字符串时的选择性:SELECT COUNT(DISTINCT SUBSTR(NAME,1,n-1))/COUNT(*) FROM USER;
                截取n-2个字符串时的选择性:SELECT COUNT(DISTINCT SUBSTR(NAME,1,n-2))/COUNT(*) FROM USER;
            根据截取后的选择性和原始选择性做对比,同时看我们需要多大的选择性,来确定n的值,n的值就是建立索引时截取的字符前缀的长度
            
            EG:CREATE INDEX INDEX_NAME5 ON TABLE_NAME(Column(5));
            辅助索引查询过程;根据NAME字段的前5个值这个前缀在二级索引中查询到一个ID值,根据这个ID值回表一整行查询,得到一整行数据后不会直接返回,用这一整行数据中的
                    NAME字段的值和条件中NAME字段的值进行对比,如果相同,则一条数据就查询出来了,然后则将链表指针向后移动一位,然后根据ID回表查询,得到一整行数据,再次与
                    条件中NAME字段值进行对比,如果相同,则第二条数据被查询出来,如果不同,则查询过程结束。
            因为要使用前缀索引,使用前缀来定位到B+TREE的某几个节点,所以前缀模糊匹配必然导致索引失效,因为如果前面的几个字符不确定,就无法定位到具体的某些个节点,索引必然无法生效        
        9>>.索引使用
            单列索引:即一个索引只包含单个列
            联合索引: 即一个索引包含了多个列
            在业务场景中,如果存在多个查询条件,针对于查询字段建立索引时,建议联合索引,而非单列索引
        10>>.索引设计原则
            1>>>.针对数据量较大,且查询比较频繁的表建立索引
            2>>>.针对于常作为查询条件(where)、排序(order by)、分组(group by)操作的字段建立索引
            3>>>.尽量选择区分度高的列作为索引,尽量建立唯一索引,区分度越高,使用索引的效率越高
            4>>>.如果是字符串类型的字段,字段的长度较长,可以针对字段的特点,建立前缀索引
            5>>>.尽量使用联合索引,减少单列索引,查询时,联合索引很多时候可以覆盖 索引,节省存储空间
            6>>>.要控制索引的数量,索引并不是多多益善,索引越多,维护索引的代价就会越大,会影响增删改的效率
            7>>>.如果索引列不能存储NULL值,请在建表时使用NOT NULL约束它。当优化器知道每列是否包含NULL值时,它可以知道哪个索引最有效的用于查询.
Mysql性能分析:            
    1>.查看当前数据库中执行INSERT、DELETE、UPDATE、DELETE的执行次数(或者这四种操作的执行权重):        
        SHOW  SESSION STATUS LIKE 'Com_______';
       查看所有数据库中执行INSERT、DELETE、UPDATE、DELETE的执行次数(或者这四种操作的执行权重):    
        SHOW GLOBAL STATUS LIKE 'Com_______';
    2>.慢查询日志:查看哪些些SQL执行效率比较低
        1>>.查询慢查询日志开关是否开启
            #OFF关闭状态,ON打开状态
            show variables like 'slow_query_log'; 
            #查看慢查询日志的位置
            show variables like 'slow_query_log_file';
            #模糊匹配查询查询日志开关和慢查询日志的位置:
            show variables like 'slow_quer%';
            #将慢查询日志开关设置为开启状态
            set global slow_query_log='ON';    
            #设置慢查询日志的存放位置
            set global slow_query_log_file='C:\\slow_mysql.log';
        2>>.在Mysql配置文件中加入如下配置:
            Linux:/etc/my.cnf
            Windows:my.ini
            #开启Mysql慢查询日志开关
            slow_query_log = 1;/slow_query_log = ON;
            #设置慢日志的时间为2秒,sql语句执行时间超过2秒,就会记录慢查询日志
            long_query_time=2;
            #没有使用到索引的查询也将被记录在日志中
            log-queries-not-using-indexes = ON;
            #log-slow-queries:代表MYSQL慢查询的日志存储目录,此目录文件一定要有写权限
            log-slow-queries="C:/Program Files (x86)/MySQL/MySQL Server 5.0/log/mysql-slow.log";
        3>>.查看慢查询日志
            Linuxri日志保存位置:/var/lib/mysql/localhost-slow.log
            Windows日志保存位置:C:\ProgramData\MySQL\MySQL Server 8.0\Data
    3>.使用show profiles;查看sql执行耗时情况
        首先查看是否支持profiles操作:select @@have_profiling; 查询结果为yes表示支持profiles操作
        然后打开profiles,set [global/session] profiling = 1;
            #查看每一条sql的耗时基本情况:
            show profiles;
            #查看指定query_id的sql语句各个阶段的耗费时情况
            show profile for query query_id;
            #查看指定query_id的sql语句的cpu使用情况
            show profile cpu for query query_id;        
    4.explain执行计划
        #直接在任意select语句之前按加上explain/desc
        EXPLAIN SELECT 字段列表 FROM 表名 WHERE 条件;
        显示结果分析:
        id字段: id相同:从上往下执行,id不相同:值越大执行的优先级越高
        key:该sql使用到的索引名称,没有使用索引,则值为NULL
        key_len:索引的长度
        type:表示连接类型,性能从好到坏依次是null system const re_ref ref range index all
            根据主键/唯一索引查询type为const,根据非唯一索引查询为type为ref,范围查询type为range,type为index,代表索引全扫描

                

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值