• Mysql学习入门到放弃ing...


    一:存储数据的演变过程

    数据库的本质

    本质其实就是一款基于网络通信的应用程序,其实每个人都可以开发一款数据库软件,因为它仅仅是一个基于网络通信的应用程序;

    也就意味着数据库软件其实有很多很多,主要分为:

           关系型数据库:

                  MySQL、Oracle、db2、Access、SQL Server

           非关系型数据库:

                  Redis、MongoDB、Memcache

    # 关系型数据库介绍

           1.数据之间彼此有关系或者约束

                  男生表 -- 前女友表

           2.存储数据的表现形式 通常是以表格存储

            

            每个字段还会有存储类型的显示(比如姓名只能存字符串....)   

    # 非关系型介绍

           存储数据通常都是以k,v键值对的形式

    二:MySQL介绍

    任何基于网络通信的应用程序 底层用的都是socket

    - 服务端

           - 基于socket通信

           - 收发消息

           - SQL语句

    - 客户端

           - 基于socket通信

           - 收发消息

           - SQL语句

          

    MySQL不单单支持MySQL自己的客户端APP 还支持其他编程语言来充当客户端操作

           如何解决语言沟通的障碍

                  # 1.让服务端兼容所有的语言(一个人精通多国语言)

                  # 2.采用统一的语言(SQL语句)

     

    三:SQL语句初识介绍:

    1.MySQL中的SQL语句是以分号;作为结束的标志

     2.基本命令

           show databases;    查看所有数据库名

    3.连接服务端的命令可以简写

           mysql -uroot -p 

    4.当你的命令输入不对,又不想让服务端执行并返回报错信息,可以c取消

           错误命令 c

    5.客户端退出 退出命令加不加分号都能执行

           quit   或者exit

    6.当你在连接服务器的时候 发现只输入一个mysql也能连接

           这时候是游客模式 访问有限制

     

    四:统一编码修改

    mysql默认的配置文件  my-default.ini

    ini结尾的一般都是配置文件 程序启动会先加载配置文件中的配置之后才真正的启动

    [mysqld] # 一旦服务端启动立刻加载下面的配置
    sql_mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES
    [mysql] # 一旦客户端启动立刻加载下面的配置
    ...
    [client] # 其他客户端
    ...

    # 修改配置文件后一定要重启服务才能生效
    # 统一编码的配置 无需掌握 直接拷贝即可
    # 偷懒 将管理员的用户名和密码也添加到配置文件中

    [mysqld]
    character-set-server=utf8
    collation-server=utf8_general_ci
    [client]
    default-character-set=utf8
    [mysql]
    user="root"
    password=123456
    default-character-set=utf8

     

    五:基本SQL语句

    针对库的增删改查(文件夹)

    #
    create database db1;
    create database db2 charset='gbk';     # 指定字符编码
    
    #
    show databases;                 # 查所有数据库
    show create database db1;       # 查单个数据库
    
    #
    alter database db2 charset='utf-8';
    
    #
    drop database db2;

     

     针对表的增删改查(文件)

    在操作表(文件)的时候,需要指定所在的库(文件夹)

    # 查看当前所在的库(数据库)的名字
    select database();
    
    # 选择数据库
    use db1;
    
    #
    create table t1(id int, name char(4));     # 创建t1表
    
    #
    show tables;              # 查看当前库下面所有的表名
    show create table t1;     # 查单个表
    describe t1;              # 查看详情信息,支持简写 desc t1;
    
    #
    alter table t1 modify name char(16);
    
    #
    drop table t1;
    
    # 绝对路径的形式操作
    # 在db1下创建db2的t1表
    create table db2.t1(id int)

     

    针对数据的增删改查(一行行的数据)

    一定先要有库 有表 再有记录

    #
    insert into t1 values(1, '小明');
    insert into t1 values(2, '小黄');
    insert into t1 values(3, '小红')
    
    # 多条数据增加
    insert into t1 values(1, '小明'),(2, '小黄'),(3, '小红');
    
    #
    select * from t1;            # 该命令 当数据量特别大的时候 不建议使用
    select id,name from t1;      # 查看t1的id和name
    
    #
    update t1 set name = 'dsb' where id > 1;
    
    #
    delete from t1 where id > 1;
    delete from t1 where name='小明';
    
    # 将表中所有数据清空
    delete from t1;

     

    六:存储引擎

    在日常生活中有很多中,并且针对不同的文件类型会有对应不同存储方式和处理机制(txt、pdf、word、MP4)针对不同的数据,应该有对应的不同的处理机制来存储;

    存储引擎 就是 不同的处理机制

    MySQL主要存储引擎:

      Innodb:

        MySQL5.5版本及之后 默认的存储引擎(支持事务 行锁 外键)

      Myisan:

        MySQL5.5版本之前 默认的存储引擎(速度要比Innodb更快,但是我们更注重的是 数据的安全)

      memory:

        内存引擎(数据全部存放在 内存中)断电数据就丢失

      blackhole:

        无论存什么,都立刻消失(黑洞)

     

    # 查看所有的存储引擎
    show engines;
    
    # 不同的存储引擎 在存储表的时候 异同点:
    create table t1(id int) engine=innodb;
    create table t2(id int) engine=myisam;
    create table t3(id int) engine=blackhole;
    create table t4(id int) engine=memory;
    
    # 文件分析(在mysql的data的数据库里)
    t1.frm    表结构    innodb
    t1.ibd    表数据    innodb
    t2.frm    表结构    myisam
    t2.MYD    表数据    myisam
    t2.MYI    索引(index,类似于 书的目录,基于目录查找对应的数据 速度更快)    myisam
    t3.frm    表结构    blackhole
    t4.frm    表结构    memory    数据存在内存
    
    # 存数据
    insert into t1 values(1);
    insert into t2 values(2);
    insert into t3 values(3);
    insert into t4 values(4);
    
    # 查看数据
    select * from t1;    有数据
    select * from t2;    有数据
    select * from t3;    没数据
    select * from t4;    有数据,但是重启服务或者断电后 会消失

     

    创建表的完整语法

    # 语法
    
    create table 表名(
            字段名1 类型(宽度) 约束条件1,
            字段名2 类型(宽度) 约束条件2,
            字段名3 类型(宽度) 约束条件3
    );

     

    # 注意

        1.在同一张表中 字段名不能重复

        2.宽度 和 约束条件 是可选的(可写可不写)而字段名 和 字段类型 是必须的

        3.约束条件写的话 也支持写多个:

      字段名 类型(宽度) 约束条件1 约束条件2 ......,
      create table t1(id);   # 报错
      create table t1(id int);

        4.最后一行 不能有逗号,不然会报错

       create table t1( id int, name char(8), ); # 报错 

    补充:

    # 宽度

           一般情况下,是对存储数据的限制

      create table t7(name char);    # 默认宽度是1,1个字符
      inert into t1 values('小明');
      inert into t1 values(null);         # 关键字null 允许为空

     针对不同的版本 会出现不同的效果:

      5.6版本默认没有开启严格模式 规定只能存一个字符 如果你给了多个字符 那么我会自动帮你截取

      5.7或以上版本开启了严格模式 规定存几个字符 如果超过了 就会报错 Data too long for...

     

    # 严格模式到底开不开呢?

           MySQL5.7之后的版本默认都是开启严格模式的

          

    # 使用数据库的准则:

           能尽量少的让数据库干活 就尽量少 不要给数据库增加额外的压力

          

    # 约束条件之 null &  not null

        create table t8(id int, name char not null);
    
        insert into t8 values(1, '小明');
    
        insert into t8 values(2, null);           # 会报错
    
        insert into t8 values(3);                  # 也会报错

    # 宽度 和 约束条件到底是什么关系?

           宽度 是用来限制数据的存储

           约束条件 是在宽度的基础之上 增加额外的约束

     

    七:基本数据类型

    1.整型:

    # 分类

           TINYINT  SMALLINT  MEDIUMINT  BIGINT      

    # 作用

           存储年龄、等级、id、号码.... 

    TINYINT

    # 是否有符号?

           默认情况下,是带符号的

          

    # 超出会如何?

           超出限制 只存最大可接受值

        create table t9(id tinyint);
        insert into t9 values(-129),(256);

     # 约束条件 之 unsigned 无符号

        create table t10(id tinyint unsigned);
        insert into t10 values(-1),(256);

    # int默认也是带符号的

    # 整型 默认情况下都是带有符号的

      create table t11(id int);

    # 针对整型 括号内的宽度到底是干什么的?

        create table t12(id int(8));
        insert into t12 values(123456789);

    # 特例:只有整型 括号里面的数字 不是表示限制位数 而是显示长度

        id int(8)

            如果数字没有超出8位   那么用默认用空格填充

            如果数字超出了8位    那么 有几位 就存几位(但是还是要村收最大范围)

          

    # 约束条件 之 zerofill 用0填充至8位

        create table t13(id int(8) unsigned zerofill);
        insert into t13 values(123);
        insert into t13 values(123456789321);

    # 总结:

           针对整型字段 括号内无需指定宽度 因为它默认的宽度已经足够显示所有的数据了

    严格模式:

    # 如何查看严格模式

           show variables like "%mode";

          

    # 模糊匹配/查询

           关键字 like
                  % :匹配任意多个字符
                  _ :匹配任意单个字符

                 

    # 修改严格模式

           set session              只在当前窗口有效
           set global                全局有效
    
           set global sql_mode = "STRICT_TRANS_TABLES"#修改完之后,退出 重新进入即可
      use test;
      create table t14(name char);
      desc t14;
      insert into t14 values('小红');  # 报错 超出范围了

    2.浮点型:

    # 分类

           FLOAT  DOUBLE  DECIMAL    

    # 作用

           记录身高、体重、薪资

    # 存储限制

        float(255,30)    # 总共255位,小数部分占30位
        double(255,30)   # 总共255位,小数部分占30位
        decimal(65,30)   # 总共65位,小数部分占30位
    # 精确度 验证
    # 创建表
    create table t15(id float(255,30));
    create table t16(id double(255,30));
    create table t17(id decimal(65,30));
    
    #插入数据
    insert into t15 values(1.1111111111111111111);
    insert into t16 values(1.1111111111111111111);
    insert into t17 values(1.1111111111111111111);
    
    # 精确度 比较:
           float < double < decimal
    
    # 要结合实际应用场景 三者都能使用

    3.字符类型:

    char:

           定长

           char(4)    数据超过4个 直接报错 不够4个 空格来凑

    varchar

           变长

           varvhar(4)数据超过4个直接报错 不够有几个就存几个

      create table t18(name char(4));
      create table t19(name varchar(4));
    
      insert into t18 values('a');
      insert into t19values('a');
    
      # 介绍一个小方法 char_length统计字段长度
      select char_length(name) from t18;
      select char_length(name) from t19;

    """

    首先可以肯定的是 char硬盘上存的绝对是真正的数据 带有空格的

    但是在显示的时候MySQL会自动将多余的空格剔除

    """

     

    # 再次修改sql_mode 让MySQL不要做自动剔除操作

       set global sql_mode = 'STRICT_TRANS_TABLES,PAD_CHAR_TO_FULL_LENGTH'; 

     

    char 与 varchar 对比:

    char:

           缺点:浪费空间

           优点:存取都很简单

                  直接按照固定的字符存取数据即可

                         jason egon alex wusir tank

                         存 按照5分字符存,取 也直接按照5个字符取

    varchar:

           优点:节省空间

           缺点:存取较为麻烦

                  1bytes+jason 1bytes+egon 1bytes+alex 1bytes+wusir 1bytes+tank

                 

                  存的时候 需要制作 报头

                  取的时候 也需要先读取 报头 

    以前基本上用得都是char 其实现在用varchar的也挺多

     

    # 扩展:

           其实在实际生产中,很多看似需要用整型 或者 浮点型存储的数据

           内部可能都是用的字符类型存储

     

    4.日期类型:

    分类:

    date::年月日 2020-05-04
    
    datetime:年月日时分秒2020-05-04 12:01:03
    
    time:时分秒 12:01:03
    
    Year:年份 2020

     

    使用:

    create table student(
           id int,
           name varchar(16),
           born_year year,
           birth date,
           study_time time,
           reg_time datetime
    );
    
    insert into student values(1,'小明','1988','1988-11-11','11:11:11','2020-11-11 11:11:11');            # 日期要1970年之后

     

    5.枚举 与 集合类型:

    分类

    枚举(enmu => enumerate):多选一 # 选择性别

    集合(set):多选多            # 选择爱好

     

    具体使用

    create table user(
           id int,
           name char(16),
           gender enum('male','female','male')
    );
    
    
    insert into user values(1,'xiaoming','male');    # 正常
    insert into user values(2,'xiaohong','xxoo');    # 报错

     

    # 枚举字段 后期再存数据的时候 只能从枚举里面选择一个存储

    create table teacher(
           id int,
           name char(16),
           gender enum('male','female','other'),
           hobby set('read','DBJ','hecha')
    );
    
     
    insert into teacher values(1,'xiaoming','male','read');            # 正常
    insert into teacher values(2,'xiaohuang','female','DBJ,hecha'); # 正常
    insert into teacher values(3,'xiaolan','male','sport');            # 报错

    # 集合可以只写一个 但是不能写没有列举的

     

    总结

    字段类型

    严格模式

    约束条件

           not null   非空

           zerofill   补零

           unsigned   无符号类型

    基本数据类型

           整型

           浮点型

           字符类型

           日期类型

           枚举 与 集合类型

     

    八:约束条件

    default 默认值

    # 补充知识点  插入数据的时候 可以指定字段顺序

    create table t1(
           id int,
           name char(16)
    );
    
    
    insert into t1(name,id) values('小红',1);

     

    # 设置默认值

    create table t2(
           id int,
           name char(16) not null,
           gender enum('male','female','others') default 'male'
    );
    
    
    insert into t2(name,id) values('小红',1);
    insert into t2 values(2,'小红','female');

     

    Unique 唯一

    # 单列唯一

    create table t3(
           id int unique,
           name char(16)
    );
    
    
    insert into t3 values(1,'小红'),(1,'小蓝');  # 报错
    insert into t3 values(1,'小红'),(2,'小蓝');  # 正确

     

    # 联合唯一

        ip 和 port

        单个 都可以重复,但是 加在一起 必须是唯一的

    create table t4(
           id int,
           ip char(16),
           port int,
           unique(ip,port)
    );
    
    insert into t4 values(1,'127.0.0.1',8080);
    insert into t4 values(2,'127.0.0.1',8081);
    insert into t4 values(3,'127.0.0.2',8080);
    insert into t4 values(4,'127.0.0.1',8080);  # 报错

     

    九:Primary Key 主键

    1.单单从约束效果上来看 primary key = notnull + unique

        非空 并且 唯一

        create table t5(id int paimary key);
        insert into t5 values(null);       # 报错
        insert into t5 values(1),(1);      # 报错
        insert into t5 values(1),(2);      # 正常

     

    2.它除了有约束效果之外 它还是Innodb存储引擎 组织数据的依据

           Innodb存储引擎在创建表的时候 必须有primary key

           因为它类似于书的目录 能够帮助提高查询效率 并且也是建表的依据

     

    ① 一张表中 有且只有一个主键 如果没有设置主键 那么会从上往下搜索

           直到遇到一个 非空且唯一的字段 将它自动生成主键

    create table t6(
           id int,
           name char(16),
           age int not null unique,
           addr char(32) not null unique
    );

     

    ② 如果表中没有主键 也没有其他任何的非空且唯一字段 那么Innodb会采用自己

           内部提供的一个字段 作为主键,隐藏 意味着你无法使用到它 就无法提升查询速度

          

    ③ 一张表中 通常都应该有一个主键字段 并且通常将id/uid/sid字段作为主键

    # 单个字段主键
    
    create table t7(
           id int,
           name char(16)
    );

     

    # 联合主键(多个字段联合起来 作为表的主键 本质还是一个主键)

    # 联合主键
    create table t8(
           id int,
           ip char(16),
           port int,
           primary key(ip,port)
    );

     

    也就意味着 以后我们在创建表的时候 id字段一定要加primary key

     

    十:auto_increment 自增

    # 当编号特别多的时候 人为地去维护太麻烦
    
    create table t9(
           id int primary key auto_increment,
           name char(16)
    );
    
     
    insert into t9(name) values('小明');
    insert into t9(name) values('小蓝');
    insert into t9(name) values('小红');
    
    
    # auto_increment只能加在主键上,不能给普通字段加
    
    create table t10(
           id int primary key auto_increment,
           name char(16),
           cid int auto_increment
    );
    
    # 报错:ERROR 1075 (42000): Incorrect table definition; there can be only one auto column and it must be defined as a key

     

    结论:

    以后再创建表的id(数据库的唯一表示id、uid、sid)字段的时候

    id int primary key auto_increment

      

    补充:

    delete from 在删除表中数据的时候 主键的自增不会停止

     truncate t1 清空表数据 并且重置主键

     

    十一:表与表之间 建立关系(约束)(重点)

    定义一张员工表 表中有很多字段

    id name gender dep_name dep_desc

     

    # 1.该表的组织结构不是很清晰(可以忽视)

    # 2.浪费硬盘空间(可忽视)

    # 3.数据的扩展性极差(无法忽视的)

     

    # 如何优化?

    (上述问题类似于之前写的python代码都在一个py文件中)

    将员工表拆分成 员工表 和 部门表

     

    外键:

    外键 就是用来帮助我们建立 表与表之间关系的

    foreign key

     

    # 外键带来的约束

           1.在创建表的时候 一定要先创建被关联表

           2.在插入数据的时候 也要先插入被关联表

           3.操作数据的时候 会出现多种限制:同步更新 同步删除

     

    表关系:

    表与表之间的关系最多只有四种:

           1.一对多关系

                  在MySQL的关系中 没有多对一这个概念

                  一对多 多对一 都是 一对多

           2.多对多关系

           3.一对一关系

           4.没有关系

     

    1.一对多关系

    判断 表与表之间关系的时候 前期不熟悉的情况下

    一定要换位思考 分别站在2张表的角度考虑

    以 员工表 与 部门表 为例

           站在员工表:

                  1个员工能否对应多个部门(1条员工数据 能否对应多条部门数据)

                  不能!!!

                  (不能直接得出结论 一定发要2张表都考虑完全)

                 

           站在部门表:

                  1个部门能都对应多个员工(1个部门数据 能否对应多条员工数据)

                  能!!!

                 

           得出结论:

                  员工表 与 部门表 是单向的 一对多

                  所以 表关系 就是 一对多

     

    SQL语句建表

    # 1.一对多表关系 外键字段建立在多的一方

    # 2.在创建表的时候 一定要先建被关联表

    # 3.在录入数据的时候 也必须先录入被关联表

    # 创建部门表
    
    create table dep(
           id int primary key auto_increment,
           dep_name char(16),
           dep_desc char(32)
    );
    # 创建员工表
    
    create table emp(
           id int primary key auto_increment,
           name char(16),
           gender enum('male','female','others') default 'male',
           dep_id int,
           foreign key(dep_id) references dep(id)
    );
    insert into emp(name,dep_id) values('xxq',1);  # 报错
    insert into dep(dep_name,dep_desc) values('sb教学部','教书育人'),('外交部','多人外交'),('nb技术部','技术能力有限部');
    insert into emp(name,dep_id) values('小明',2),('小黄',1),('小红',1),('小蓝',3);
    # 修改emp里面的dep_id字段 或者 dep表里面的id字段 update dep set id=200 where id=2; # 报错 # 删除dep表里面的数据 delete from dep; # 报错

     

    # 1.先删除教学部对应的员工数据 再删除部门

           操作太过繁琐

      

    # 2.真正做到数据之间有关系

           更新 就同步更新

           删除 就同步删除

     

    级联更新 级联删除

    # 创建部门表
    
    create table dep(
           id int primary key auto_increment,
           dep_name char(16),
           dep_desc char(32)
    );
    # 创建员工表
    
    create table emp(
           id int primary key auto_increment,
           name char(16),
           gender enum('male','female','others') default 'male',
           dep_id int,
           foreign key(dep_id) references dep(id)
           on update cascade   # 同步更新
           on delete cascade    # 同步删除
    );
    insert into dep(dep_name,dep_desc) values('sb教学部','教书育人'),('外交部','多人外交'),('nb技术部','技术能力有限部');
    insert into emp(name,dep_id) values('小明',2),('小黄',1),('小红',1),('小蓝',3);
    
    update dep set id=200 where id=2; # 正常
    delete from dep where id=1;

     

    2.多对多关系

    以 书本表 与 作者表 为例

           站在书本表:

                  1本书能否对应多个作者(1条书本数据 能否对应多个作者数据)

                  能!!!

                  (不能直接得出结论 一定发要2张表都考虑完全)

                 

           站在作者表:

                  1本作者能否对应多本书(1条作者数据 能否对应多个书本数据)

                  能!!!

                 

           得出结论:

                  书本表 与 作者表 是双向的 一对多

                  所以 表关系 就是 多对多

     

    SQL语句建表

    # 建立图书表
    
    create table book(
           id int primary key auto_increment,
           title varchar(32).
           price int,
           author_id int,
           foreign key(author_id) references author(id)
           on update cascade   # 同步更新
           on delete cascade    # 同步删除
    );
    # 建立作者表
    
    create table author(
           id int primary key auto_increment,
           name varchar(32).
           age int,
           book_id int,
           foreign key(book_id) references book(id)
           on update cascade   # 同步更新
           on delete cascade    # 同步删除
    );

     

    按照上述的方式创建 一个都别想成功!!!

    其实我们只是想记录 书籍和作者的关系

    !针对多对多字段表关系 不能在2张原有的表中 创建外键

    需要你再单独开设一张表 专门用来建立2张表之间的关系

    # 建立图书表
    
    create table book(
           id int primary key auto_increment,
           title varchar(32),
           price int
    );
    # 建立作者表
    
    create table author(
           id int primary key auto_increment,
           name varchar(32),
           age int
    );
    # 建立关联表
    
    create table book2author(
           id int primary key auto_increment,
           author_id int,
           book_id int,
           foreign key(author_id) references author(id)
           on update cascade   # 同步更新
           on delete cascade,   # 同步删除
           foreign key(book_id) references book(id)
           on update cascade   # 同步更新
           on delete cascade    # 同步删除
    );

     

    3.一对一关系

    id name age addr phone hobby email

    如果一个表的字段特别多 每次查询又不是所有的字段都能用到

    将表一分为二:

           用户表信息:

                  用户表:

                         id name age

                  详情表:

                         id addr phone hobby email

                        

           站在用户表:

                  一个用户能否对应多个用户详情       不能!!!

                 

           站在详情表:

                  一个详情能否属于多个用户       不能!!!

                 

           结论:单向的一对多 都不成立 那么这个时候两者之间的表关系

                  就是 一对一

                  或者 没有关系(好判断)

     

    # 一对一 外键字段 建在任意一方都可以 推荐你建在查询频率比较高的表中

    # 创建用户详情表
    
    create table authordetail(
           id int primary key auto_increment,
           phone int,
           address varchar(64)
    );
    # 创建用户表
    
    create table author(
           id int primary key auto_increment,
           name varchar(32),
           age int,
           address varchar(64),
           authordetail_id int,
           foreign key(authordetail_id) references authordetail(id)
           on update cascade   # 同步更新
           on delete cascade    # 同步删除
    );

     

    总结:

    # 表关系的建立 需要用到 foreign key

           一对多

                  外键字段 建立在多的一方   

           多对多

                  自己开设第三张表 存储

           一对一

                  建立在任意一方都可以 推荐建立在查询评论高的表中

                         

    # 判断表之间关系的方式:(换位思考!!!)

           员工 与 部门

           图书 与 作者

           作者 与 作者详情

     

    十二:修改表的完整语法大全(了解)

    # MySQL是不区分大小写的

    1.修改表名
           alter table 表名 rename 新表名
     
    
    2.增加字段
           alter table 表名 add 字段名 字段类型(宽度) 约束条件;
           alter table 表名 add 字段名 字段类型(宽度) 约束条件 first;                 # 添加在最前面
           alter table 表名 add 字段名 字段类型(宽度) 约束条件 after 字段名;      # 添加在字段后面
    
    
    3.删除字段
           alter table 表名 drop 字段名;
          
    
    4.修改字段
           alter table 表名 modify 字段名 字段类型(宽度) 约束条件;
           alter table 表名 chage 旧字段名 新字段名;

    十三:复制表(了解)

    我们sql语句查询的结果 其实也是一张虚拟表

    create table 新表名 select * from 旧表名;         # 不能复制主键、外键、索引  , 只能复制表结构和数据
    
    create table 新表名 select * from 旧表名 where id>3;

    十四:如何查询表

    单表操作:

    select
    where
    group by
    having
    distinct
    order by
    limit
    regexp
    like
    

      

    补充:

    表与表之间如果有关系的话,可以有2种建立联系的方式:

      1.通过外键强制性的建立关系

      2.通过SQL语句 在逻辑层面上建立关系

    注意:创建外键会消耗一定的资源 并且增加了表与表之间的耦合度在实际项目中 如果表特别多其实可以不作任何处理,直接通过sql语句来建立逻辑层面上的关系,到底用不用外键 取决于实际项目需求

     

    各种操作

    create table emp(
      id int not null unique auto_increment,
      name varchar(20) not null,
      sex enum('male','female') not null default 'male', #大部分是男的
      age int(3) unsigned not null default 28,
      hire_date date not null,
      post varchar(50),
      post_comment varchar(100),
      salary double(15,2),
      office int, #一个部门一个屋子
      depart_id int
    );
    #插入记录
    #三个部门:教学,销售,运营
    insert into emp(name,sex,age,hire_date,post,salary,office,depart_id) values
    ('jason','male',18,'20170301','张江第一帅形象代言',7300.33,401,1), #以下是教学部
    ('tom','male',78,'20150302','teacher',1000000.31,401,1),
    ('kevin','male',81,'20130305','teacher',8300,401,1),
    ('tony','male',73,'20140701','teacher',3500,401,1),
    ('owen','male',28,'20121101','teacher',2100,401,1),
    ('jack','female',18,'20110211','teacher',9000,401,1),
    ('jenny','male',18,'19000301','teacher',30000,401,1),
    ('sank','male',48,'20101111','teacher',10000,401,1),
    ('哈哈','female',48,'20150311','sale',3000.13,402,2),#以下是销售部
    ('呵呵','female',38,'20101101','sale',2000.35,402,2),
    ('西西','female',18,'20110312','sale',1000.37,402,2),
    ('乐乐','female',18,'20160513','sale',3000.29,402,2),
    ('拉拉','female',28,'20170127','sale',4000.33,402,2),
    ('僧龙','male',28,'20160311','operation',10000.13,403,3), #以下是运营部门
    ('程咬金','male',18,'19970312','operation',20000,403,3),
    ('程咬银','female',18,'20130311','operation',19000,403,3),
    ('程咬铜','male',18,'20150411','operation',18000,403,3),
    ('程咬铁','female',18,'20140512','operation',17000,403,3);

    # 当表的字段特别多的时候 展示的时候错乱 可以用G来分行展示

    select * from empG;     # 出现乱码或者空白的时候 可以将字符编码设置为GBK

    几个重要关键字的执行顺序:

    # 书写顺序

    select id,name from emp where id > 3;

    # 执行顺序

    from
    
    where
    
    select
    

      

    虽然执行顺序和书写顺序不一致 你在写SQL语句的时候可能不知道怎么写

    你就按照书写顺序的方式写SQL

                 select * :先用*占位

                 之后补全后面的sql语句

                 最后将*替换成想要的具体字段

    十五:where筛选条件

    # 作用:是对整体数据的一个筛选操作

     

    # 1.查询id大于等于3小于等于6的数据

    select * from emp where id>=3 and id<=6;
    select * from emp where id between 3 and 6;

    # 2.查询薪资是20000或者18000或者17000的数据

    select * from emp where salary=20000 or salary=18000 or salary=17000;
    select * from emp where salary in(20000,18000,17000);

    # 3.查询员工姓名中 包含字母o的员工的姓名和薪资

    # 模糊查询 like

                 % : 匹配任意多个字符

                 _ : 匹配任意单个字符

    select name,salary from emp where name like '%o%';     

    # 4.查询员工姓名是由4个字符组成的姓名和薪资   char_length()

    select name,salary from emp where name like '____';      
    select name,salary from emp where char_length(name)=4;    

    # 5.查询id小于3或者id大于6的数据

    select * from emp where id<3 or id>6;
    select * from emp where id not between 3 and 6;

    # 6.查询薪资不在20000,18000,17000范围的数据

    select * from emp where salary not in(20000,18000,17000);

    # 7.查询岗位描述为空的员工姓名和岗位名               针对null不能用 = 要用 is

    select name,post from emp where post_comment = null;                # 报错
    select name,post from emp where post_comment is null;

    十六:group by 分组

    # 分组实际应用场景       分组应用场景非常多

                 男女比例

                 部门平均薪资

                 部门秃头率

                 国家之间的数据统计

                

    # 1.按照部分分组

    select * from emp group by post;

    # 分组之后 最小可操作性单位 应该是 组  还不再是组内的单个数据

                 上述命令在你没有设置严格模式的时候 是可正常执行的 但会的是分组之后 每个组的第一条数据

                 但是这个不符合分组的规范:

                                 分组之后 不应该考虑单个数据 而应该以组为操作单位(分组之后 没办法直接获取组内的单个数据)

                 如果设置了严格模式 那么上述命令会直接报错;

    # 设置严格模式
    set global sql_mode = 'strict_trans_tables,only_full_group_by';

    设置严格模式之后  分组 默认只能拿到分组的依据

    select * from emp group by post;        # 按照上面分组 就只能拿到分组

     

    按照上面分组 就只能拿到分组,其他字段不能直接获取 需要借助一些方法(聚合函数)

                

      聚合函数:
    
               max         # 最大
               min         # 最小
               sum         # 求和
               count       # 计数
               avg         # 平均

    # 什么时候需要分组?

                 关键字:每个、平均、最高、最低

     

    # 1.获取每个部门的最高薪资

    select * from emp group by post;
    select post,max(salary) from emp group by post;

    # as可以给字段起别名,也可以直接省略不写(但是不推荐写 容易错乱)

    select post as '部门',max(salary) as '最高薪资' from emp group by post;
    select post '部门',max(salary) '最高薪资' from emp group by post;

    # 2.获取每个部门的最低薪资

    select post,min(salary) from emp group by post;

    # 3.获取每个部门的平均薪资

    select post,avg(salary) from emp group by post;

    # 4.获取每个部门的工资总和

    select post,sum(salary) from emp group by post;

    # 5.获取每个部门的人数

    select post,count(salary) from emp group by post;
    select post,count(id) from emp group by post;
    select post,count(age) from emp group by post;
    select post,count(post_comment) from emp group by post;            # 不行

    # 6.查询分组之后的部门名称 和 每个部门下所有的员工姓名

    # group_concat 不单单支持 获取分组之后的其他字段值 还支持拼接操作

    select post,group_concat(name) from emp group by post;
    select post,group_concat(name,'_DSB') from emp group by post;
    select post,group_concat(name,':',salary) from emp group by post;

    # concat不分组的时候用

    select concat('NAME:'name),concat('SAL:'salary) from emp;

    # concat_ws() 多个字段之间的连接符是相同的情况下使用

    select concat_ws(':',name,age,salary) from emp;

    # as语法不单单可以给字段起别名  还可以给表起别名

    select emp.id,emp.name from emp;
    select emp.id,emp.name from emp as t1;           # 报错
    select t1.id,t1.name from emp as t1;

    # 查询每个人的年薪 12薪

    select name,salary*12 from emp;

    分组注意事项:

    # 关键字where和group by同时出现的时候 group by必须跟在where后面

    where先对整体数据进行过滤之后 再分组操作

    where筛选条件不能使用聚合函数

    聚合函数只能在分组之后使用

    select id,name,age from emp where max(salary) > 3000;
    select max(salary) form emp;              # 不分组 默认整体就是一组

     

    # 统计各部门年龄在30岁以上的员工平均薪资

                 # 1.先选出所有年龄大于30岁的员工               

    select * from emp where age> 0;

                 # 2.再对结果进行分组

    select * from emp where age>30 group by post;
    select post,avg(salary) from emp where age>30 group by post;

    十七:having分组之后的筛选条件

    having的语法和where是一致的

    只不过 having是在分组之后进行的过滤操作

    即:having是可以直接使用聚合函数的

     

    # 统计各部门年龄在30岁以上的员工薪资 并且保留平均薪资大于10000的部门

    select post,avg(salary) from emp
        where age>30
        group by post
        having avg(salary)>10000;

    十八:distinct 去重

    一定要注意 必须是完全一样的数据才可以去重!!!

    一定不要将逐渐忽视了 有逐渐存在的情况下 是不可能去重的

     

    [

    {'id':1,'name':'jason','age':18},

    {'id':2,'name':'jason','age':18},

    {'id':3,'name':'egon','age':18}

    ]

    ORM  对象关系映射   让不懂SQL语句的人也能够非常牛逼的操作数据库

    表                                             类

    一条条的数据                                   对象

    字段对应的值                                   对象的属性

     

    你再写类 就意味着在创建表

    用类生成对象 就意味着再创建数据

    对象点属性 就是在获取数据字段对应的值

    目的就是减轻python程序员的压力 只需要会python面向对象的知识点就可以操作MySQL

    select distinct id,age from emp;
    select distinct age from emp;

    十九:order by排序

    select * from emp order by salary;
    select * from emp order by salary asc;
    select * from emp order by salary desc;

    order by默认是升序  asc 该asc可以省略不写

    也可以修改为降序     desc

    select * from emp order by age desc,salary asc;     # 先按照age降序排  如果碰到age相同 则再按照salary升序排

    # 统计各部门年龄在10岁以上的员工平均工资并且保留平均薪资大于1000的部门,然后对平均工资降序排

    select post,avg(salary) from emp
            where age>10
            group by post
            having avg(salary) > 1000
            order by avg(salary) desc
            ;

    二十:limit限制展示条数

    select * from emp;
    
    """针对数据过多的情况 我们通常都是做分页处理"""
    select * from emp limit 3;  # 只展示三条数据
    
     
    select * from emp limit 0,5;
    select * from emp limit 5,5;
    
    # 第一个参数是起始位置
    # 第二个参数是展示条数

     

    二十一:正则

    select * from emp where name regexp '^j.*(n|y)$';    # 查询以j开头,以n或y结尾的名字

    多表操作

    前期表准备

    #建表
    
    create table dep(
      id int,
      name varchar(20)
      );
    create table emp(
        id int primary key auto_increment,
        name varchar(20),
        sex enum('male','female') not null default 'male',
        age int,
        dep_id int
        );

     

    #插入数据
    
    insert into dep values
        (200,'技术'),
        (201,'人力资源'),
        (202,'销售'),
        (203,'运营');
    
    
    insert into emp(name,sex,age,dep_id) values
        ('jason','male',18,200),
        ('egon','female',48,201),
        ('kevin','male',18,201),
        ('nick','male',28,202),
        ('owen','male',18,203),
        ('jerry','female',18,204);

    表查询

    select * from dep,emp;  # 结果   笛卡尔积

    # 表关联查询

    select * from emp,dep where emp.dep_id = dep.id;   

    """

    MySQL也知道 你在后面查询数据过程中 肯定会经常用到拼表操作

    所以特地给你开设了对应的方法

                 inner join  内连接

                 left join   左连接

                 right join  右连接

                 union                      全连接

    """

    # inner join  内连接
    select * from emp inner join dep on emp.dep_id = dep.id;
    # 查询平均年龄在25岁以上的部门名称
    select dep.name from emp inner join dep on emp.dep_id = dep.id
      group by dep.name
      having avg(age) >25
      ;
    # 只拼接两张表中公有的数据部分 # left join 左连接 select * from emp left join dep on emp.dep_id = dep.id; # 左表所有的数据都展示出来 没有对应的项就用NULL # right join 右连接 select * from emp right join dep on emp.dep_id = dep.id; # 右表所有的数据都展示出来 没有对应的项就用NULL # union 全连接 左右两表所有的数据都展示出来 select * from emp left join dep on emp.dep_id = dep.id union select * from emp right join dep on emp.dep_id = dep.id;

    # 子查询

    """
    子查询就是我们平时解决问题的思路
                 分步骤解决问题
                                 第一步
                                 第二步
                                 ...
    将一个查询语句的结果当做另外一个查询语句的条件去用
    """
    
    # 查询部门是技术或者人力资源的员工信息
        1 先获取部门的id号
        2 再去员工表里面筛选出对应的员工
    
        select id from dep where name='技术' or name = '人力资源';
        select name from emp where dep_id in (200,201);
    
        select * from emp where dep_id in (select id from dep where name='技术' or name = '人力资源');

     

    # 关键字exists

      只返回布尔值 :True 和False

        返回True的时候外层查询语句执行

        返回False的时候外层查询语句不再执行 

    select * from emp where exists
        (select id from dep where id >3);     # 查询部门编号大于3的  结果True
    
    select * from emp where exists
        (select id from dep where id >300);  # 查询部门编号大于300的  结果False

     二十二:多表查询练习

      创建五张表:class、coursescoreteacher、student

      1 /*
      2  数据导入:
      3  Navicat Premium Data Transfer
      4 
      5  Source Server         : localhost
      6  Source Server Type    : MySQL
      7  Source Server Version : 50624
      8  Source Host           : localhost
      9  Source Database       : sqlexam
     10 
     11  Target Server Type    : MySQL
     12  Target Server Version : 50624
     13  File Encoding         : utf-8
     14 
     15  Date: 10/21/2016 06:46:46 AM
     16 */
     17 
     18 SET NAMES utf8;
     19 SET FOREIGN_KEY_CHECKS = 0;
     20 
     21 -- ----------------------------
     22 --  Table structure for `class`
     23 -- ----------------------------
     24 DROP TABLE IF EXISTS `class`;
     25 CREATE TABLE `class` (
     26   `cid` int(11) NOT NULL AUTO_INCREMENT,
     27   `caption` varchar(32) NOT NULL,
     28   PRIMARY KEY (`cid`)
     29 ) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
     30 
     31 -- ----------------------------
     32 --  Records of `class`
     33 -- ----------------------------
     34 BEGIN;
     35 INSERT INTO `class` VALUES ('1', '三年二班'), ('2', '三年三班'), ('3', '一年二班'), ('4', '二年九班');
     36 COMMIT;
     37 
     38 -- ----------------------------
     39 --  Table structure for `course`
     40 -- ----------------------------
     41 DROP TABLE IF EXISTS `course`;
     42 CREATE TABLE `course` (
     43   `cid` int(11) NOT NULL AUTO_INCREMENT,
     44   `cname` varchar(32) NOT NULL,
     45   `teacher_id` int(11) NOT NULL,
     46   PRIMARY KEY (`cid`),
     47   KEY `fk_course_teacher` (`teacher_id`),
     48   CONSTRAINT `fk_course_teacher` FOREIGN KEY (`teacher_id`) REFERENCES `teacher` (`tid`)
     49 ) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
     50 
     51 -- ----------------------------
     52 --  Records of `course`
     53 -- ----------------------------
     54 BEGIN;
     55 INSERT INTO `course` VALUES ('1', '生物', '1'), ('2', '物理', '2'), ('3', '体育', '3'), ('4', '美术', '2');
     56 COMMIT;
     57 
     58 -- ----------------------------
     59 --  Table structure for `score`
     60 -- ----------------------------
     61 DROP TABLE IF EXISTS `score`;
     62 CREATE TABLE `score` (
     63   `sid` int(11) NOT NULL AUTO_INCREMENT,
     64   `student_id` int(11) NOT NULL,
     65   `course_id` int(11) NOT NULL,
     66   `num` int(11) NOT NULL,
     67   PRIMARY KEY (`sid`),
     68   KEY `fk_score_student` (`student_id`),
     69   KEY `fk_score_course` (`course_id`),
     70   CONSTRAINT `fk_score_course` FOREIGN KEY (`course_id`) REFERENCES `course` (`cid`),
     71   CONSTRAINT `fk_score_student` FOREIGN KEY (`student_id`) REFERENCES `student` (`sid`)
     72 ) ENGINE=InnoDB AUTO_INCREMENT=53 DEFAULT CHARSET=utf8;
     73 
     74 -- ----------------------------
     75 --  Records of `score`
     76 -- ----------------------------
     77 BEGIN;
     78 INSERT INTO `score` VALUES ('1', '1', '1', '10'), ('2', '1', '2', '9'), ('5', '1', '4', '66'), ('6', '2', '1', '8'), ('8', '2', '3', '68'), ('9', '2', '4', '99'), ('10', '3', '1', '77'), ('11', '3', '2', '66'), ('12', '3', '3', '87'), ('13', '3', '4', '99'), ('14', '4', '1', '79'), ('15', '4', '2', '11'), ('16', '4', '3', '67'), ('17', '4', '4', '100'), ('18', '5', '1', '79'), ('19', '5', '2', '11'), ('20', '5', '3', '67'), ('21', '5', '4', '100'), ('22', '6', '1', '9'), ('23', '6', '2', '100'), ('24', '6', '3', '67'), ('25', '6', '4', '100'), ('26', '7', '1', '9'), ('27', '7', '2', '100'), ('28', '7', '3', '67'), ('29', '7', '4', '88'), ('30', '8', '1', '9'), ('31', '8', '2', '100'), ('32', '8', '3', '67'), ('33', '8', '4', '88'), ('34', '9', '1', '91'), ('35', '9', '2', '88'), ('36', '9', '3', '67'), ('37', '9', '4', '22'), ('38', '10', '1', '90'), ('39', '10', '2', '77'), ('40', '10', '3', '43'), ('41', '10', '4', '87'), ('42', '11', '1', '90'), ('43', '11', '2', '77'), ('44', '11', '3', '43'), ('45', '11', '4', '87'), ('46', '12', '1', '90'), ('47', '12', '2', '77'), ('48', '12', '3', '43'), ('49', '12', '4', '87'), ('52', '13', '3', '87');
     79 COMMIT;
     80 
     81 -- ----------------------------
     82 --  Table structure for `student`
     83 -- ----------------------------
     84 DROP TABLE IF EXISTS `student`;
     85 CREATE TABLE `student` (
     86   `sid` int(11) NOT NULL AUTO_INCREMENT,
     87   `gender` char(1) NOT NULL,
     88   `class_id` int(11) NOT NULL,
     89   `sname` varchar(32) NOT NULL,
     90   PRIMARY KEY (`sid`),
     91   KEY `fk_class` (`class_id`),
     92   CONSTRAINT `fk_class` FOREIGN KEY (`class_id`) REFERENCES `class` (`cid`)
     93 ) ENGINE=InnoDB AUTO_INCREMENT=17 DEFAULT CHARSET=utf8;
     94 
     95 -- ----------------------------
     96 --  Records of `student`
     97 -- ----------------------------
     98 BEGIN;
     99 INSERT INTO `student` VALUES ('1', '', '1', '理解'), ('2', '', '1', '钢蛋'), ('3', '', '1', '张三'), ('4', '', '1', '张一'), ('5', '', '1', '张二'), ('6', '', '1', '张四'), ('7', '', '2', '铁锤'), ('8', '', '2', '李三'), ('9', '', '2', '李一'), ('10', '', '2', '李二'), ('11', '', '2', '李四'), ('12', '', '3', '如花'), ('13', '', '3', '刘三'), ('14', '', '3', '刘一'), ('15', '', '3', '刘二'), ('16', '', '3', '刘四');
    100 COMMIT;
    101 
    102 -- ----------------------------
    103 --  Table structure for `teacher`
    104 -- ----------------------------
    105 DROP TABLE IF EXISTS `teacher`;
    106 CREATE TABLE `teacher` (
    107   `tid` int(11) NOT NULL AUTO_INCREMENT,
    108   `tname` varchar(32) NOT NULL,
    109   PRIMARY KEY (`tid`)
    110 ) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;
    111 
    112 -- ----------------------------
    113 --  Records of `teacher`
    114 -- ----------------------------
    115 BEGIN;
    116 INSERT INTO `teacher` VALUES ('1', '张磊老师'), ('2', '李平老师'), ('3', '刘海燕老师'), ('4', '朱云海老师'), ('5', '李杰老师');
    117 COMMIT;
    118 
    119 SET FOREIGN_KEY_CHECKS = 1;

    eg:练习题和答案

    -- 1、查询所有的课程的名称以及对应的任课老师姓名
    SELECT
        course.cname,
        teacher.tname
    FROM
        course
    INNER JOIN teacher ON course.teacher_id = teacher.tid;
    -- 2、查询学生表中男女生各有多少人
    SELECT
        gender,
        COUNT(sid)
    FROM
        student
    GROUP BY
        gender;
    -- 3、查询物理成绩等于100的学生的姓名
    -- 方式一:
    SELECT
        student.sname
    FROM
        student
    WHERE
        student.sid IN (
            SELECT
                score.student_id
            FROM
                score
            WHERE
                score.course_id IN (
                    SELECT
                        cid
                    FROM
                        course
                    WHERE
                        cname = '物理'
                )
            AND score.num = 100
        );
    -- 方式二:
    SELECT
        student.sname
    FROM
        student
    WHERE
        student.sid IN (
            SELECT
                score.student_id
            FROM
                course
            INNER JOIN score ON course.cid = score.course_id
            WHERE
                course.cname = '物理'
            AND score.num = '100'
        );
    -- 4、查询平均成绩大于八十分的同学的姓名和平均成绩
    SELECT
        student.sname,
        t1.avg_num
    FROM
        student
    INNER JOIN (
        SELECT
            student_id,
            avg(num) AS avg_num
        FROM
            score
        GROUP BY
            student_id
        HAVING
            avg(num) > 80
    ) AS t1 ON student.sid = t1.student_id;
    -- 5、查询所有学生的学号,姓名,选课数,总成绩
    SELECT
        student.sid,
        student.sname,
        t1.count_course,
        t1.sum_num
    FROM
        student
    INNER JOIN (
        SELECT
            score.student_id,
            sum(num) AS sum_num,
            COUNT(score.course_id) AS count_course
        FROM
            score
        GROUP BY
            score.student_id
    ) AS t1 ON student.sid = t1.student_id;
    -- 6、 查询姓李老师的个数 
    SELECT
        count(*)
    FROM
        teacher
    WHERE
        tname LIKE '李%';
    -- 7、 查询没有报李平老师课的学生姓名
    SELECT
        student.sname
    FROM
        student
    WHERE
        student.sid NOT IN (
            SELECT DISTINCT
                score.student_id
            FROM
                score
            WHERE
                score.course_id IN (
                    SELECT
                        course.cid
                    FROM
                        course
                    INNER JOIN teacher ON course.teacher_id = teacher.tid
                    WHERE
                        teacher.tname = '李平老师'
                )
        );
    -- 8、 查询物理课程比生物课程高的学生的学号
    SELECT
        t1.student_id
    FROM
        (
            SELECT
                student_id,
                num
            FROM
                score
            WHERE
                course_id = (
                    SELECT
                        cid
                    FROM
                        course
                    WHERE
                        cname = '物理'
                )
        ) AS t1
    INNER JOIN (
        SELECT
            student_id,
            num
        FROM
            score
        WHERE
            course_id = (
                SELECT
                    cid
                FROM
                    course
                WHERE
                    cname = '生物'
            )
    ) AS t2 ON t1.student_id = t2.student_id
    WHERE
        t1.num > t2.num;
    -- 方式二:
    SELECT
        t1.id
    FROM
        (
            SELECT
                score.student_id AS id,
                score.num AS num
            FROM
                course
            INNER JOIN score ON course.cid = score.course_id
            WHERE
                cname = '物理'
        ) AS t1
    JOIN (
        SELECT
            score.student_id AS id,
            score.num AS num
        FROM
            course
        INNER JOIN score ON course.cid = score.course_id
        WHERE
            cname = '生物'
    ) AS t2 ON t1.id = t2.id
    WHERE
        t1.num > t2.num
    
    -- 9、 查询没有同时选修物理课程和体育课程的学生姓名(只查找选修一门的)
    SELECT
        sname
    FROM
        student
    WHERE
        sid IN (
            SELECT
                student_id
            FROM
                course
            INNER JOIN score ON course.cid = score.course_id
            WHERE
                course.cname IN ('物理', '体育')
            GROUP BY
                student_id
            HAVING
                COUNT(cname) = 1
        );
    
    -- 10、查询挂科超过两门(包括两门)的学生姓名和班级
    SELECT
        student.sname,
        class.caption
    FROM
        class
    INNER JOIN student ON class.cid = student.class_id
    WHERE
        student.sid IN (
            SELECT
                student_id
            FROM
                score
            WHERE
                num < 60
            GROUP BY
                score.student_id
            HAVING
                COUNT(student_id) >= 2
        );
    
    -- 11、查询选修了所有课程的学生姓名
    SELECT
        student.sname
    FROM
        student
    WHERE
        student.sid IN (
            SELECT
                student_id
            FROM
                score
            GROUP BY
                student_id
            HAVING
                count(student_id) = 4
        );
    
    -- 12、查询李平老师教的课程的所有成绩记录
    SELECT
        score.course_id,
        score.num
    FROM
        score
    WHERE
        score.course_id IN (
            SELECT
                course.cid
            FROM
                teacher
            INNER JOIN course ON teacher.tid = course.teacher_id
            WHERE
                teacher.tname = '李平老师'
        )
    
    -- 13、查询全部学生都选修了的课程号和课程名
    -- 14、查询每门课程被选修的次数
    -- 15、查询之选修了一门课程的学生姓名和学号
    -- 16、查询所有学生考出的成绩并按从高到低排序(成绩去重)
    -- 17、查询平均成绩大于85的学生姓名和平均成绩
    -- 18、查询生物成绩不及格的学生姓名和对应生物分数
    -- 19、查询在所有选修了李平老师课程的学生中,这些课程(李平老师的课程,不是所有课程)平均成绩最高的学生姓名
    -- 20、查询每门课程成绩最好的前两名学生姓名
    -- 21、查询不同课程但成绩相同的学号,课程号,成绩
    -- 22、查询没学过“叶平”老师课程的学生姓名以及选修的课程名称;
    -- 23、查询所有选修了学号为1的同学选修过的一门或者多门课程的同学学号和姓名;
    -- 24、任课最多的老师中学生单科成绩最高的学生姓名

     

    二十三:Python操作MySQL(pymysql模块)

      pymysql模块操作

    # 支持python代码操作数据库MySQL
        安装pymysql:
            pip3 install pymysql
    
    # 导入pymysql模块
    import pymysql
    # 连接数据库
    conn = pymysql.connect(
        host='127.0.0.1',      # 'ip'
        port=3306,              # '端口'
        user='root',          # 'mysql用户名'
        password='yumi_0405', # 'mysql密码'
        database='day48',     # '要连接的库名'
        charset='utf8'        # 编码千万不要加-
    )
    # 产生一个游标对象(就是用来帮你执行命令的)
    cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)  
    """
    cursor=pymysql.cursors.DictCursor将查询结果以字典的形式返回
    """
    sql = 'select * from teacher;'
    res = cursor.execute(sql)
    # print(res)  # execute返回的是你当前sql语句所影响的行数  改返回值一般不用
    # 获取命令执行的查询结果
    # print(cursor.fetchone())  # 只拿一条
    # print(cursor.fetchall())  # 拿所有
    # print(cursor.fetchmany(2))  # 可以指定拿几条
    print(cursor.fetchone())
    print(cursor.fetchone())  # 读取数据类似于文件光标的移动
    # cursor.scroll(1,'relative')  # 相对于光标所在的位置继续往后移动1位
    cursor.scroll(1,'absolute')  # 相对于数据的开头往后继续移动1位
    print(cursor.fetchall())

    二十四:sql注入问题

    ""
    利用一些语法的特性 书写一些特点的语句实现固定的语法
    MySQL利用的是MySQL的注释语法
    select * from user where name='jason' -- jhsadklsajdkla' and password=''
    
    select * from user where name='xxx' or 1=1 -- sakjdkljakldjasl' and password=''
    """
    日常生活中很多软件在注册的时候都不能含有特殊符号
    因为怕你构造出特定的语句入侵数据库 不安全
    
    # 敏感的数据不要自己做拼接 交给execute帮你拼接即可
    # 结合数据库完成一个用户的登录功能?
    import pymysql
    
    
    conn = pymysql.connect(
        host = '127.0.0.1',
        port = 3306,
        user = 'root',
        password = '123456',
        database = 'day48',
        charset = 'utf8'  # 编码千万不要加-
    )  # 链接数据库
    cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
    
    username = input('>>>:')
    password = input('>>>:')
    sql = "select * from user where name=%s and password=%s"
    # 不要手动拼接数据 先用%s占位 之后将需要拼接的数据直接交给execute方法即可
    print(sql)
    rows = cursor.execute(sql,(username,password))  # 自动识别sql里面的%s用后面元组里面的数据替换
    if rows:
        print('登录成功')
        print(cursor.fetchall())
    else:
        print('用户名密码错误')

     

    二十五:视图(View)

    1.什么是视图?

      视图 就是通过查询得到一张虚拟表,然后保存下来,下次可以直接使用。

    2.为什么要用视图?

      如果要频繁地操作一张虚拟表(拼接表组成的),你就可以制作成视图 后续可以直接操作

    3.怎么用视图?

    # 固定语法

     create view 表名 as 虚拟表的查询sql语句 

    # 具体操作

    create view teacher2course as
    
    select * from teacher inner join course
    
    on teacher.tid = course.teacher_id;

    注意:

      1.创建视图 在硬盘上只会有表结构 没有表数据(数据还是来自于之前的表)

      2.视图一般只用来查询 里面的数据不要继续修改 可能会影响原来的表

    # 视图使用的频率高不高?

        不高

        当你创建了视图之后 会造成表的不好维护

     

    二十六:触发器(Trigger)

    介绍:

      在满足对表数据进行增、删、改的情况下,自动触发的功能。

      使用触发器 可以帮助我们实现监控、日志、自动处理异常etc...

      触发器可以在6种情况下自动触发:

      增前、增后、 删前、删后、 改前、改后

    基本语法结构:

    reate trigger 触发器的名字 before/after insert/update/delete on 表名
    for each row
    begin
        sql语句
    end
    
    # 具体使用 针对触发器的名字 我们通常需要做到见名知意(看见名字 就知道意思)
    
    # 针对 增
    create trigger tri_before_t1 before insert t1
    for each row
    begin
        sql语句
    end
    
    create trigger tri_after_t1 after insert t1
    for each row
    begin
        sql语句
    end
    
    # 针对 删
    create trigger tri_before_t1 before delete t1
    for each row
    begin
        sql语句
    end
    
    create trigger tri_after_t1 after delete t1
    for each row
    begin
        sql语句
    end
    
    # 针对 改
    create trigger tri_before_t1 before update t1
    for each row
    begin
        sql语句
    end
    
    create trigger tri_after_t1 after update t1
    for each row
    begin
        sql语句
    end

    修改MySQL默认的语句结束符(只作用于当前窗口):

    delimiter $$    # 将默认的结束符号由;改成$$
    
    delimiter ;     # 将默认的结束符号由改回;

    示例eg:

    CREATE TABLE cmd (
        id INT PRIMARY KEY auto_increment,
        USER CHAR (32),
        priv CHAR (10),
        cmd CHAR (64),
        sub_time datetime, #提交时间
        success enum ('yes', 'no') #0代表执行失败
    );
    
    CREATE TABLE errlog (
        id INT PRIMARY KEY auto_increment,
        err_cmd CHAR (64),
        err_time datetime
    );
    """
    当cmd表中的记录succes字段是no那么就触发触发器的执行去errlog表中插入数据
    NEW指代的就是一条条数据对象
    """
    delimiter $$
    create trigger tri_after_insert_cmd after insert on cmd 
    for each row
    begin
        if NEW.success = 'no' then
            insert into errlog(err_cmd,err_time) values(NEW.cmd,NEW.sub_time);
        end if;
    end $$
    delimiter ;
    
    # 朝cmd表插入数据
    INSERT INTO cmd (
        USER,
        priv,
        cmd,
        sub_time,
        success
    )
    VALUES
        ('jason','0755','ls -l /etc',NOW(),'yes'),
        ('jason','0755','cat /etc/passwd',NOW(),'no'),
        ('jason','0755','useradd xxx',NOW(),'no'),
        ('jason','0755','ps aux',NOW(),'yes');
    
    # 删除触发器
    drop trigger tri_after_insert_cmd;

    二十七:事务

    1.什么是事务?

      要开启一个事务 可以包含多条SQL语句 这些SQL语句

      要么同时成功 要么一个都别想成功

      称之为事务的原子性

    2.为什么要用事务?(事务的作用)

      保证了对数据的安全性

    # 例子:转账

    egon要还jason1000块钱:

    1.将egon支付宝账户减去1000块钱

    2.将jason支付宝账户加1000块钱

    3.事务的四大特性(ACID

      A:原子性

    一个事务是一个不可分割的单位,事务中包含的诸多操作

    要么同时成功要么同时失败

      C:一致性

    事务必须是使数据库从一个一致性的状态变到另外一个一致性的状态

    一致性跟原子性是密切相关的

      I:隔离性

    一个事务的执行不能被其他事务干扰

    (即一个事务内部的操作及使用到的数据对并发的其他事务是隔离的,并发执行的事务之间也是互相不干扰的)

      D:持久性

    也叫"永久性"

    一个事务一旦提交成功执行成功 那么它对数据库中数据的修改应该是永久的

    接下来的其他操作或者故障不应该对其有任何的影响

    4.怎么用事务?

    # 事务相关的关键字
    
    # 1 开启事务
    
    start transaction;
    
    # 2 回滚(回到事务执行之前的状态)
    
    rollback;
    
    # 3 确认(确认之后就无法回滚了)
    
    commit;
    """模拟转账功能"""
    
    create table user(
        id int primary key auto_increment,
        name char(16),
        balance int
    );
    
    insert into user(name,balance) values
    ('jason',1000),
    ('egon',1000),
    ('tank',1000);
    
    
    # 1 先开启事务
    start transaction;
    
    # 2 多条sql语句
    update user set balance=900 where name='jason';
    update user set balance=1010 where name='egon';
    update user set balance=1090 where name='tank';

    5.总结

      当你想让多条sql语句保持一致性 要么同时成功要么同时失败 

      你就应该考虑使用事务

    二十八:存储过程

      存储过程就类似于python中的自定义函数;它的内部包含了一系列可以执行的sql语句,存储过程存放于MySQL服务端中,你可以直接通过调用存储过程触发内部sql语句的执行

    基本使用:

    create procedure 存储过程的名字(形参1,形参2,...)
    
    begin
    
        sql代码
    
    end

    # 调用

    call 存储过程的名字();

    1.三种开发模型

    第一种

      应用程序:程序员写代码开发

      MySQL:提前编写好存储过程,供应用程序调用

      优点:开发效率提升了 执行效率也上去了

      缺点:考虑到认为元素、跨部门沟通的问题  后续的存储过程的扩展性差

     

    第二种

      应用程序:程序员写代码开发之外 设计到数据库操作也自己动手写

      优点:扩展性很高

      缺点:

    开发效率降低

    编写sql语句太过繁琐 而且后续还需要考虑sql优化的问题

     

    第三种

      应用程序:只写程序代码 不写sql语句 基于别人写好的操作MySQL的python框架直接调用操作即可ORM框架  

      优点:开发效率比上面两种情况都要高 

      缺点:语句的扩展性差 可能会出现效率低下的问题 

    第一种基本不用。一般都是第三种,出现效率问题再动手写sql

    存储过程具体演示

    delimiter $$
    
    create procedure p1(
        in m int,  # 只进不出  m不能返回出去
        in n int,
        out res int  # 该形参可以返回出去
    )
    begin
        select tname from teacher where tid>m and tid<n;
        set res=666;  # 将res变量修改 用来标识当前的存储过程代码确实执行了
    end $$
    
    delimiter ;

    # 针对形参res 不能直接传数据 应该传一个变量名

    # 定义变量
    
    set @ret = 10;
    
    # 查看变量对应的值
    
    select @ret;

    在pymysql模块中如何调用存储过程呢?

    import pymysql
    
    conn = pymysql.connect(
        host = '127.0.0.1',
        port = 3306,
        user = 'root',
        passwd = '123456',
        db = 'day48',
        charset = 'utf8',
        autocommit = True
    )
    
    cursor = conn.cursor(pymysql.cursors.DictCursor)
    # 调用存储过程
    cursor.callproc('p1',(1,5,10))
    
    """
    @_p1_0=1
    @_p1_1=5
    @_p1_2=10
    """
    
    # print(cursor.fetchall())
    
    cursor.execute('select @_p1_2;')
    
    print(cursor.fetchall())

    二十九:内置函数

    跟存储过程是有区别的,存储过程是自定义函数,函数就类似于是内置函数

    # 例如('jason','0755','ls -l /etc',NOW(),'yes')
    
    CREATE TABLE blog (
        id INT PRIMARY KEY auto_increment,
        NAME CHAR (32),
        sub_time datetime
    );
    
    INSERT INTO blog (NAME, sub_time)
    VALUES
        ('第1篇','2015-03-01 11:31:21'),
        ('第2篇','2015-03-11 16:31:21'),
        ('第3篇','2016-07-01 10:21:31'),
        ('第4篇','2016-07-22 09:23:21'),
        ('第5篇','2016-07-23 10:11:11'),
        ('第6篇','2016-07-25 11:21:31'),
        ('第7篇','2017-03-01 15:33:21'),
        ('第8篇','2017-03-01 17:32:21'),
        ('第9篇','2017-03-01 18:31:21');
    
    select date_format(sub_time,'%Y-%m'),count(id) from blog group by date_format(sub_time,'%Y-%m');

    三十:流程控制

    # if判断
    delimiter //    
    CREATE PROCEDURE proc_if ()
    BEGIN
        declare i int default 0;
        if i = 1 THEN
            SELECT 1;
        ELSEIF i = 2 THEN
            SELECT 2;
        ELSE
            SELECT 7;
        END IF;
    END //
    delimiter ;
    # while循环 delimiter
    // CREATE PROCEDURE proc_while () BEGIN DECLARE num INT ; SET num = 0 ; WHILE num < 10 DO SELECT num ; SET num = num + 1 ; END WHILE ;

    三十一:索引理论

     ps:数据都是存在与硬盘上的,查询数据不可避免的需要进行IO操作

    索引:就是一种数据结构,类似于书的目录。意味着以后在查询数据的应该先找目录再找数据,而不是一页一页的翻书,从而提升查询速度降低IO操作

    索引在MySQL中也叫“键”,是存储引擎用于快速查找记录的一种数据结构

      * primary key
      * unique key
      * index key

    注意foreign key不是用来加速查询用的,不在我们的而研究范围之内

    上面的三种key,前面两种除了可以增加查询速度之外各自还具有约束条件,而最后一种index key没有任何的约束条件,只是用来帮助你快速查询数据

    **本质**

    通过不断的缩小想要的数据范围筛选出最终的结果,同时将随机事件(一页一页的翻)

    变成顺序事件(先找目录、找数据);也就是说有了索引机制,我们可以总是用一种固定的方式查找数据

     

    一张表中可以有多个索引(多个目录)

    索引虽然能够帮助你加快查询速度但是也有缺点
      1 当表中有大量数据存在的前提下 创建索引速度会很慢
      2 在索引创建完毕之后 对表的查询性能会大幅度的提升 但是写的性能也会大幅度的降低

    索引不要随意的创建!!!
    b+树
    只有叶子节点存放的是真实的数据 其他节点存放的是虚拟数据 仅仅是用来指路的;树的层级越高查询数据所需要经历的步骤就越多(树有几层查询数据就需要几步)

    一个磁盘块存储是有限制的
    为什么建议你将id字段作为索引
      占得空间少 一个磁盘块能够存储的数据多
      那么久降低了树的高度 从而减少查询次数
    聚集索引(primary key)

    聚集索引指的就是主键
    Innodb 只有两个文件 直接将主键存放在了idb表中
    MyIsam 三个文件 单独将索引存在一个文件

    辅助索引(unique,index)
    查询数据的时候不可能一直使用到主键,也有可能会用到name,password等其他字段
    那么这个时候你是没有办法利用聚集索引。这个时候你就可以根据情况给其他字段设置辅助索引(也是一个b+树)

    叶子节点存放的是数据对应的主键值;先按照辅助索引拿到数据的主键值;之后还是需要去主键的聚集索引里面查询数据

    覆盖索引
    在辅助索引的叶子节点就已经拿到了需要的数据

    select name from user where name='jason'select age from user where name='jason';

    示例eg:测试索引是否有效

    *准备**
    
    ​```mysql
    #1. 准备表
    create table s1(
    id int,
    name varchar(20),
    gender char(6),
    email varchar(50)
    );
    
    #2. 创建存储过程,实现批量插入记录
    delimiter $$ #声明存储过程的结束符号为$$
    create procedure auto_insert1()
    BEGIN
        declare i int default 1;
        while(i<3000000)do
            insert into s1 values(i,'jason','male',concat('jason',i,'@oldboy'));
            set i=i+1;
        end while;
    END$$ #$$结束
    delimiter ; #重新声明分号为结束符号
    
    #3. 查看存储过程
    show create procedure auto_insert1G 
    
    #4. 调用存储过程
    call auto_insert1();
    ​```
    
    ​``` mysql 
    # 表没有任何索引的情况下
    select * from s1 where id=30000;
    # 避免打印带来的时间损耗
    select count(id) from s1 where id = 30000;
    select count(id) from s1 where id = 1;
    
    # 给id做一个主键
    alter table s1 add primary key(id);  # 速度很慢
    
    select count(id) from s1 where id = 1;  # 速度相较于未建索引之前两者差着数量级
    select count(id) from s1 where name = 'jason'  # 速度仍然很慢
    
    
    """
    范围问题
    """
    # 并不是加了索引,以后查询的时候按照这个字段速度就一定快   
    select count(id) from s1 where id > 1;  # 速度相较于id = 1慢了很多
    select count(id) from s1 where id >1 and id < 3;
    select count(id) from s1 where id > 1 and id < 10000;
    select count(id) from s1 where id != 3;
    
    alter table s1 drop primary key;  # 删除主键 单独再来研究name字段
    select count(id) from s1 where name = 'jason';  # 又慢了
    
    create index idx_name on s1(name);  # 给s1表的name字段创建索引
    select count(id) from s1 where name = 'jason'  # 仍然很慢!!!
    """
    再来看b+树的原理,数据需要区分度比较高,而我们这张表全是jason,根本无法区分
    那这个树其实就建成了“一根棍子”
    """
    select count(id) from s1 where name = 'xxx';  
    # 这个会很快,我就是一根棍,第一个不匹配直接不需要再往下走了
    select count(id) from s1 where name like 'xxx';
    select count(id) from s1 where name like 'xxx%';
    select count(id) from s1 where name like '%xxx';  # 慢 最左匹配特性
    
    # 区分度低的字段不能建索引
    drop index idx_name on s1;
    
    # 给id字段建普通的索引
    create index idx_id on s1(id);
    select count(id) from s1 where id = 3;  # 快了
    select count(id) from s1 where id*12 = 3;  # 慢了  索引的字段一定不要参与计算
    
    drop index idx_id on s1;
    select count(id) from s1 where name='jason' and gender = 'male' and id = 3 and email = 'xxx';
    # 针对上面这种连续多个and的操作,mysql会从左到右先找区分度比较高的索引字段,先将整体范围降下来再去比较其他条件
    create index idx_name on s1(name);
    select count(id) from s1 where name='jason' and gender = 'male' and id = 3 and email = 'xxx';  # 并没有加速
    
    drop index idx_name on s1;
    # 给name,gender这种区分度不高的字段加上索引并不难加快查询速度
    
    create index idx_id on s1(id);
    select count(id) from s1 where name='jason' and gender = 'male' and id = 3 and email = 'xxx';  # 快了  先通过id已经讲数据快速锁定成了一条了
    select count(id) from s1 where name='jason' and gender = 'male' and id > 3 and email = 'xxx';  # 慢了  基于id查出来的数据仍然很多,然后还要去比较其他字段
    
    drop index idx_id on s1
    
    create index idx_email on s1(email);
    select count(id) from s1 where name='jason' and gender = 'male' and id > 3 and email = 'xxx';  # 快 通过email字段一剑封喉 
    ​```
    
    #### 联合索引
    
    ​```mysql
    select count(id) from s1 where name='jason' and gender = 'male' and id > 3 and email = 'xxx';  
    # 如果上述四个字段区分度都很高,那给谁建都能加速查询
    # 给email加然而不用email字段
    select count(id) from s1 where name='jason' and gender = 'male' and id > 3; 
    # 给name加然而不用name字段
    select count(id) from s1 where gender = 'male' and id > 3; 
    # 给gender加然而不用gender字段
    select count(id) from s1 where id > 3; 
    
    # 带来的问题是所有的字段都建了索引然而都没有用到,还需要花费四次建立的时间
    create index idx_all on s1(email,name,gender,id);  # 最左匹配原则,区分度高的往左放
    select count(id) from s1 where name='jason' and gender = 'male' and id > 3 and email = 'xxx';  # 速度变快
    ​```
    
    # 慢查询日志(设定一个时间检测所有超出该时间的sql语句,然后针对性的进行优化!)

     

     

  • 相关阅读:
    C语言II作业01
    C语言寒假大作战04
    C语言寒假大作战03
    C语言寒假大作战02
    C语言寒假大作战01
    C语言I博客作业12—学期总结
    第一次作业
    C语言I博客作业02
    C语言I博客作业11
    C语言||作业01
  • 原文地址:https://www.cnblogs.com/Tang-Yuan/p/14060291.html
Copyright © 2020-2023  润新知