MySQL笔记总结

编程知识 更新时间:2023-04-17 10:52:21

typora-copy-images-to: ./assets

1.什么是数据库

字面意思就是存放数据的仓库,我们之前的数据存储在文件中,我们之前存放数据的文件是普通的txt,md中,它的读取效率低,操作方法不灵活(修改更慢),等等。

数据库我们称之为DB.

百度的解释:

数据库,简而言之可视为电子化的文件柜——存储电子文件的处所,用户可以对文件中的数据运行新增、截取、更新、删除等操作。
所谓“数据库”系以一定方式储存在一起、能予多个用户共享、具有尽可能小的冗余度、与应用程序彼此独立的数据集合。

我们要是以原来的操作文件open的方式进行开发,效率低,操作复杂,肯定是不合适的。

数据库可分为两种:

  • 磁盘级别。
  • 内存级别。

比如我们现在开发一套程序:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aIDQ5Ox8-1668513210130)(F:\newschool\笔记总结\MySQL笔记总结\MySQL笔记总结.assets\E9AC35E9-191E-420A-88FA-1ABAFBE86B17.png)]

我们现在以这种形式开发项目,会遇到问题,我们通过编程语言构建逻辑部分没有问题的,但是一旦涉及到我们要想从数据库中获取数据,比如(登录,注册),我们还应该开发一套程序,基于C/S架构的程序,这套程序是要结合我们的项目代码,向服务器3部署数据库的服务器来回获取数据。每个项目都需要开发一套这样的C/S架构用于获取数据的程序,所以,为了避免重复造轮子,有人专门开发了一套基于C/S架构用于对数据库数据进行增删改查等一系列操作的程序。我们要想写这套程序我们需要注意哪几点:

  • 这套程序虽然也是基于磁盘读取的,但是效率要高。
  • 我们要注意数据的安全性(不能让数据丢失等等)。
  • 我们要给操作数据库中数据文件进行加锁。(防止数据同一时刻被多个程序操作从而破坏数据)
  • 统一一套对数据进行增删改查的标准命令。(sql语句)
  • 支持并发处理。

这套软件有一个专有名词:DBMS ,数据库管理系统。

2.什么是数据库管理系统

字面解释:数据库管理系统:它是一套基于C/S架构的程序,这套程序的作用在于对数据库中的数据进行系统管理,可以对数据进行增删改查等各种复杂操作,并且可以保证数据的安全性,支持并发,效率高等等数据库管理程序。

百度解释:

数据库管理系统(英语:Database Management System,简称DBMS)是为管理数据库而设计的电脑软件系统,一般具有存储、截取、安全保障、备份等基础功能。数据库管理系统可以依据它所支持的数据库模型来作分类,例如关系式、XML;或依据所支持的计算机类型来作分类,例如服务器群集、移动电话;或依据所用查询语言来作分类,例如SQL、XQuery;或依据性能冲量重点来作分类,例如最大规模、最高运行速度;亦或其他的分类方式。不论使用哪种分类方式,一些DBMS能够跨类别,例如,同时支持多种查询语言。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EJ7hNB1T-1668513210131)(F:\newschool\笔记总结\MySQL笔记总结\MySQL笔记总结.assets\021EC749-FB0C-4698-B314-F288643867CD.png)]

3.数据库的分类

数据库分为两大类:

3.1关系型数据库

库:在我们这里就是文件夹,比如建一个库,就是创建一个文件夹。

表:在我们这里就是文件,我们建一个表就是创建一个文件,只不过这种文件比较特殊,效率高,查询快,方法多。

记录:文件中的每行数据。我们就称之为记录。

我们现在就可以把表看似成excel文件,关系型数据库指的是表与表之间是存在关联性的。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zFooAQQK-1668513210131)(F:\newschool\笔记总结\MySQL笔记总结\MySQL笔记总结.assets\967964DA-A328-4A71-914B-C47042B2E0AF.png)]

​ 操作关系型数据库的命令,我们称之为SQL,看解释

结构化查询语言(Structured Query Language)简称SQL(发音:/ˈes kjuː ˈel/ "S-Q-L"),是一种特殊目的的编程语言,是一种数据库查询和程序设计语言,用于存取数据以及查询、更新和管理关系数据库系统;同时也是数据库脚本文件的扩展名。
结构化查询语言是高级的非过程化编程语言,允许用户在高层数据结构上工作。它不要求用户指定对数据的存放方法,也不需要用户了解具体的数据存放方式,所以具有完全不同底层结构的不同数据库系统, 可以使用相同的结构化查询语言作为数据输入与管理的接口。结构化查询语言语句可以嵌套,这使它具有极大的灵活性和强大的功能。
1986年10月,美国国家标准协会对SQL进行规范后,以此作为关系式数据库管理系统的标准语言(ANSI X3. 135-1986),1987年得到国际标准组织的支持下成为国际标准。不过各种通行的数据库系统在其实践过程中都对SQL规范作了某些编改和扩充。所以,实际上不同数据库系统之间的SQL不能完全相互通用

​ 其中最后一句挺重要的:不同数据库系统之间的SQL不能完全相互通用

  1. 常见的关系型数据库
  2.1.1  oracle数据库

        Oracle前身叫SDL、由Larry Ellison和两个变成任意在1977创办,他们开发了主机的拳头产品,在市场上大量销售。Oracle公司是最早开发关系型数据库的厂商之一,其产品支持最广泛的操作系统平台。目前Oracle关系数据库产品的市场占有率数一数二 。
        Oracle公司是目前全球最大的数据库软件公司,也是近年业务增长极为迅速的软件提供与服务商 
        主要应用范围:传统大企业、大公司、政府、金融、证券等。 
        版本升级:oracle8i,oracle9i,oracle10g,oracle11g,oracle12c

2.1.2  MySQL

        MySQL被广泛的应用在Internet上的大中小型网站中。由于体积小、速度快、总体拥有成本低,开放源代码

2.1.3  MariaDB数据库

        MAriaDB数据库管理系统是MySQL数据库的一个分支,主要由开元社区维护,采用GPL授权许可。开发这个MariaDB的原因之一是:甲骨文公司收购了MySQL后,有MySQL闭源的潜在风险,因此MySQL开元社区采用分支的方式来避开这个风险。 

        MariaDB基于事务的Maria存储引擎,替换了MySQL的MyISAM的存储引擎,它使用了Percona的XtraDB(InnoDB的变体)这个版本还包括了PrimeBase XT (PBXT)和Federated X存储引擎。

2.1.4 SQL Server数据库

            Microsoft SQL Server是微软公司开发的大型关系数据库系统。SQL Server的功能比较全面,效率高,可以作为中型企业或单位的数据库平台。SQL Server可以与Winodws操作系统紧密集成,不论是应用程序开发速度还是系统事务处理运行速度,都得到较大的提升,对于在Windows平台上开发的各种企业级信息管理系统来说,不论是C/S(客户机/服务器)架构还是B/S(浏览器/服务器)架构。SQL Server都是一个很好的选择。SQL Server的缺点是只能在Windows系统下运行

2.1.5  Access数据库

        Access是入门级小型桌面数据库,性能安全性都很一般,可供个人管理或小型企业只用 
        Access不是数据库语言,只是一个数据库程序,目前最新版本为Office 2007,其特点主要如下: 
        (1)完善地管理各种数据库对象,具有强大的数据组织,用户管理、安全检查等功能 
        (2)强大的数据处理功能,在一个工作组级别的网络环境中,使用Access开发的多用户管理系统具有传统的XSASE(DBASE、FoxBASE的统称)数据库系统所无法实现的客户服务器(Ckient/Server)结构和响应的数据库安全机制,Access具备了许多先进的大型数据管理管理系统所具备的特征。 
        (3)可以方便地生成各种数据对象,利用存储的数据建立窗体和报表 
        (4)作为Office套件的一部分,可以与Office集成,实现无缝连接 
        (5)能够利用Web检索和发布数据,实现与Internet的连接,Access主要适用于中小企业应用系统,或作为客户机/服务器系统中的客户端数据库。

2.1.6  其他不常用关系型数据库

        DB2,PostgreSQL,Informix,Sybase等。这些关系型数据库逐步的淡化了普通运维的实现,特别是互联网公司几乎见不到

3.2非关系型数据库

非关系型数据库也被称为NoSQL数据库,NOSQL的本意是“Not Olnly SQL”
    指的是非关系型数据库,而不是“No SQL”的意思,因此,NoSQL的产生并不是要彻底地否定关系型数据库,而是作为传统关系型数据库的一个有效补充。NOSQL数据库在特定的场景下可以发挥出难以想象的高效率和高性能。

随着互联网Web2.0网站的兴起,传统的关系型数据库在应付web2,0网站,特别是对于规模日益扩大的海量数据,超大规模和高并发的微博、微信、SNS类型的web2.0纯动态网站已经显得力不从心,暴露了很多难以克服的问题。

例如:传统的关系型数据库IO瓶颈、性能瓶颈都难以有效突破,于是出现了大批针对特定场景,以高性能和使用便利为目的功能特异化的数据库产品。NOSQL(非关系型)类的数据就是在这样的情景下诞生并得到了非常迅速的发展

高性能、高并发、对数据一致性要求不高
    开源的NoSQL体系,如Facebook的Cassandra,Apache的HBase,也得到了广泛认同,Redis,mongb也逐渐越来越受到各类大中小型公司的欢迎和追捧

NOSQL非关系型数据库小结:
    1、NOSQL不是否定关系数据库,而是作为关系数据库的一个重要补充
    2、NOSQL为了高性能、高并发而生,忽略影响高性能,高并发的功能
    3、NOSQL典型产品memcached (纯内存),redis(持久化缓存),mongodb(文档的数据库)

​ 非关系型数据库又分为以下4种:

(1)键值(Key-Value)存储数据库
        
        键值数据库就类似传统语言中使用哈希表,可以通过key来添加、查询或删除数据,因为使用key主键访问,所以会获得很高的性能及扩展性 
        键值(Key-Value)数据库主要是使用一个哈希表,这个表中有一个特定的键和一个指针指向特定的数据。Key/value模型对于IT系统来说的优势在于简单、易部署、高并发 
        k1—>数据 
        k2—>数据 
        典型产品:Memcached、Redis、MemcacheDB、BerkeleyDB 

(2)列存储(Column-oriedted)数据库 ======>了解即可,一般公司用不到 
         这部分数据库通常用来分布式存储的海量数据,键仍然存在,但是他们的特点是指向了多个列。 
        典型产品:Cassandra,HBase 

(3)面向文档(Document-Oriented)数据库
        面向文档数据库会将以文档的形式存储。每个文档都是自包含的数据单元,是一系列数据项的集合。每个数据项都有一个名称与对应的值,值既可以是简单的数据类型,如字符串、数字和日期等;也可以是复杂的类型,如有序列表和关系对象。数据存储的最小单位是文档,同一个表中存储的文档属性可以是不同的,数据可以使用XML、JSON或者JSONB等多种形式存储 
        典型产品:MorgoDB、CouchDB 

(4)图形(Graph)数据库

​ 常见的非关系型数据库

2.2.1  memcached(key-value)

  Memcaced是一个开源的、高性能的、具有分布式内存对象的缓存系统。通过它可以减轻数据库负载,加速动态的web应用,最初版本由LiveJoumal 的Brad Fitzpatrick在2003年开发完成。目前全球有非常多的用户都在使用它来架构主机的大负载网站或提升主机的高访问网站的响应速度。注意:Memcache 是这个项目的名称,而Memcached是服务端的主程序文件名。 
缓存一般用来保存一些进程被存取的对象或数据,通过缓存来存取对象或数据要比在磁盘上存取块很多,前者是内存,后者是磁盘、Memcached是一种纯内存缓存系统,把经常存取的对象或数据缓存在memcached的内存中,这些被缓存的数据被程序通过API的方式被读取,memcached里面的数据就像一张巨大的hash表,数据以key-value对的方式存在。Memcached通过缓存经常被存取的对象或数据,从而减轻频繁读取数据库的压力,提高网站的响应速度,构建出快速更快的可扩展的Web应用。 
  官网:http://memcached/ 
  由于memcached为纯内存缓存软件,一旦重启所有数据都会丢失,因此,新浪网基于Memcached开发了一个开源项目Memcachedb。通过为Memcached增加Berkeley DB的特久化存储机制和异步主复制机制,使Memcached具备了事务恢复能力、持久化数据能力和分布式复制能力,memcached非常适合需要超高性能读写速度、持久化保存的应用场景,但是最近几年逐渐被其他的持久化产品替代如Redis

  Memcached小结: 
  1、key-value行数据库 
  2、纯内存数据库 
  3、持久化memcachedb(sina)

2.2.2  Redis(key-value)

        和Memcached类似,redis也是一个key-value型存储系统。但redis支持的存储value类型相对更多,包括string(字符串)、list(链表)、set(集合)和zset(有序集合)等。这些数据类型都支持push/pop、add/remove及取交集、并集和差集及更丰富的操作,而且这些操作都是原子性的。为了保证效率,redis的数据都是缓存在内存中。区别是redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在基础上实现了master-slave(主从)同步。 

  redis是一个高性能的key-value数据库。redis的出现、很大程度补偿了memcached这类key/value存储的不足,在部分场合可以对关系数据库起到很好的补充作用。它提供了Python、Ruby、Erlang、PHP客户端,使用方便。 
  官方:http://www.redis.io/documentation 
  redis特点: 
  1)支持内存缓存,这个功能相当于memcached 
  2)支持持久化存储,这个功能相当于memcachedb,ttserver 
  3)数据库类型更丰富。比其他key-value库功能更强 
  4)支持主从集群、分布式 
  5)支持队列等特殊功能 
  应用:缓存从存取memcached更改存取redis

2.2.3  MongoDB(Document-oriented)

  MongoDB是一个介于关系型数据库和非关系型数据库之间的产品,是非关系型数据库当中功能最丰富,最像关系数据库的。他支持的数据库结构非常松散,类似json的bjson格式,因此可以存储比较复杂的数据类型。Mongodb最大的特点是他支持的查询语言非常强大,其语法有点类似于面向对象的查询语言,几乎可以实现类似关系数据库单表查询的绝大部分功能,而且还支持对数据建立索引。 

  特点: 
    高性能、易部署、易使用、存储数据非常方便 
  主要功能特性: 

    1.面向集合存储,易存储对象类型的数据 
    2.“面向集合”(Collenction-Orented)意思是数据库被分组存储在数据集中,被称为一个集合(Collenction)每个 集合在数据库中都有一个唯一的标识名,并且可以包含无限数目的文档,集合的概念类似关系型数据库(RDBMS)里的表(table)不同的是它不需要定义任何模式(schema) 
    3.模式自由 
      模式自由(schema-free)意为着存储在mongodb数据库中的文件,我们不需要知道它的任何结构定义。 
    4.支持动态查询 
    5.支持完全索引,包含内部对象 
    6.支持查询 
    7.支持复制和故障恢复 
    8.使用高效的二进制数据存储,包括大型对象 
    9.自动处理碎片、以支持云计算层次的扩展性
2.2.4  Cassandra(Column-oriented)

  Apache Cassndra是一套开源分布式Key-Value存储系统。它最初由Facebook开发,用于存储特别大的数据。Facebook目前在使用此系统。 
主要特点: 

  1.分布式 
  2.基于column的结构化 
  3.高伸展性 
  4.Cassandra的主要特点就是它不是一个数据库,而是由一堆数据库节点共同构成一个分布式网络服务,对Cassandra的一个写操作,会被复制到其他节点上去,对Cassandra的读操作。也会被路由到某个节点上面去读取。 
  Cassandir是一个混合型的非关系的数据库,类似于Google的BigTable。其主要功能比Dynomie(分布式的key-value存储系统)更丰富,Cassandra最初由Facebook开发,后转变成了开源项目。

2.2.5  其他不常用非关系型数据库

  HBase、MemcacheDB、BerkeleyDB、Tokyo Cabinet\Tokyo Tyrant(ttserver) 
  ttserver 持久化输出,缺点存储2千万条 性能下降(由日本人发明)

看了这么多的数据库,我们主要讲的是MySQL,这个公司里面非常常用的又非常nb的关系型数据库,后面还会将一些非关系型数据库的使用,来吧,我们好好认识一下MySQL,看下一节介绍!

4.MySQL数据库的介绍

MySQL版本
    双授权版本:社区版(完全免费,功能也够nb了)和商业版(更好,功能更多更强大一些,但是收费,VIP,有售后服务,也会参考和吸收社区版的一些nb的功能,安全性和稳定性都是最好的,大几十万),一般NB的开源软件都是双授权的
    每个版本又分四个版本依次进行发布:
      Alpha版:一般只在开发公司内部使用,不对外公开,测试、自我检查的版本
      Beta版:一般是开发完也测试完的版本,一般不会出现比较大的性能bug(一般我们不用,阿里、去哪儿会使用这个版本,有些新功能,内部有高手能调,也能评估新功能的性能)
      RC版:根据Beta版测试之后收集到一些bug等在进行完善的一个版本
      GA版:软件正式发布的版本,没有特别的需求一定要使用GA版,有些公司追求新功能会使用Beta版,这是个例。

MySQL的产品线:(mysql是C++写的,oracle 9i版本之前是C语言写的,之后主要是java)
    最早期,mysql是按照3.x–4.x–5.x等来开发的,但是为了提高MySQL的竞争优势,以及提高性能、降低开发维护成本等原因,同时,更方便企业用户更精准的选择合适的版本产品用于自己的企业生产环境中,MySQL在发展到5.1系列版本之后,重新规划为三条产品线。
    第一条:5.0.xx 到 5.1.xx产品线系列介绍
    第二条:5.4.xx 到 5.7.xx产品线系列介绍(主流:5.5和5.6)
    第三条:6.0.xx 到 7.1.xx产品线系列介绍

MySQL数据库软件命名介绍

以mysql-5.6.42.tar.gz的版本号来解释:
      1.第一个数字5是主版本号,描述了文件格式。所有版本5发行都有相同的文件格式。
      2.第二个数字6是发行级别。主版本号和发行级别组合到一起便构成了发行序列号。
      3.第三个数据42是在此发行系列的版本号,随每个新发布版递增。通常你需要已经选择发行的最新版本,每次更新后,版本字符串的最后一个数字会递增。如果增加了一些新功能或者微小的不兼容性,版本字符串的第二个数字会递增。如果文件格式改变,第一个数字会递增。
    一般有的版本也会加上上面我们说的4个版本的后缀,beta、alpha、rc版、ga版等等,我们举得这个例子是不带后缀的,就相当于GA版

MySQL版本选择建议
    1.稳定版:选择开源的社区版的稳定版GA版本
    2.产品线:可以选择5.1、5.5、5.6,互联网公司主流5.5和5.6,其次是5.1.
    3.选择MySQL数据库GA版发布后6个月以上的GA版本。
    4.要选择前后几个月没有大的BUG修复的版本,而不是大量修复BUG的集中版本
    5.最好向后较长时间没有更新发布的版本。
    6.开发、测试、运维、DBA进行自己本地测试的时候,最好要和线上的版本一致,最差也要兼容,所以作为开发,你要清楚公司用的哪个版本的数据库
    7.作为内部开发测试数据库环境,跑大概3-6个月的时间。
    8.优先企业非核心业务采用新版本的数据库GA版本的软件。
    10.想DBA高手请教,或者在技术分为好的群里和大家一起交流,使用真正高手用过的好用的GA版本产品

经过上述工序后,若没有重要的功能BUG或者性能瓶颈,则可以开始考虑作为任何业务数据服务的后端数据库软件。

5.MySQL数据库的安装与启动

5.1MySQL的下载安装

mysql下载地址:https://dev.mysql/downloads,如果你在mysql官网下载的是zip压缩包,看操作:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZneQbkoN-1668513210132)(F:\newschool\笔记总结\MySQL笔记总结\MySQL笔记总结.assets\4812221F-5557-458D-BC7D-22F75E68024E.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AynvfkYr-1668513210133)(F:\newschool\笔记总结\MySQL笔记总结\MySQL笔记总结.assets\6C10F1C4-7C4A-4B3A-A0DB-20C6D5D7237F.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pifreDGo-1668513210133)(F:\newschool\笔记总结\MySQL笔记总结\MySQL笔记总结.assets\18336B99-B395-463A-9516-A1079E5094B2.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-J3GYoUEy-1668513210133)(F:\newschool\笔记总结\MySQL笔记总结\MySQL笔记总结.assets\DB777309-3800-4CF6-AA2C-AD69DB039230.png)]

下载完成以后进行安装:

    1.  解压MySQL压缩包,将以下载的MySQL压缩包解压到自定义目录下。我放在D:\MySQL

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-88UuLg9i-1668513210134)(F:\newschool\笔记总结\MySQL笔记总结\MySQL笔记总结.assets\810C4E58-D741-4D11-985A-6099284A38A4.png)]

  1. 解压完成之后其实就可以用了,但是为了方便使用,也就是在cmd启动mysql的时候,就不用加上全路径了,所以添加一下环境变量,将bin这个文件夹添加到环境变量,bin这个文件夹中有mysqld服务端,有mysql自带的一个客户端,所以添加了环境变量之后,在cmd中直接就可以使用了。

添加环境变量

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FYmMwiWp-1668513210134)(F:\newschool\笔记总结\MySQL笔记总结\MySQL笔记总结.assets\345E4BE5-3EA3-4F69-8432-FF85457D0427.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YU1KkaBt-1668513210135)(F:\newschool\笔记总结\MySQL笔记总结\MySQL笔记总结.assets\88FF1B81-0BCB-4CD7-81F5-DA4727CFED21.png)]

5.2 MySQL的配置启动

  • 方式一

    直接在cmd命令行进行启动

    先启动服务端:打开一个窗口,输入mysqld,回车,启动了MySQL的服务端。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nwo8dzFu-1668513210135)(F:\newschool\笔记总结\MySQL笔记总结\MySQL笔记总结.assets\BF5C8A8A-7CC8-4E58-836B-F0A8010F48F8.png)]

    然后我们再启动一个cmd窗口来使用一下mysql客户端,然后连接一下我们开启的服务端,就可以成功了。

    连接指令是:mysql -h 服务端IP地址 -P(大写) 3306(mysql服务端默认端口) -uroot(用户,这里我使用root用户来测了) -p密码 。注意:如果这样直接输入密码的话,密码和前面的-p中间不要有空格。

    taibaijingdeMBP:~ barry$ mysql -h127.0.0.1 -P3306 -uroot -p
    Enter password:
    
    # 如果你在本机进行测试,可以省略ip地址与端口
    mysql -uroot -p
    

    刚下载安装启动客户端,只有一个root账号,并且没有密码。 以root身份进入,密码为空。

  • 方式二

    将mysql添加到系统命令中,我们可以通过操作系统帮助我们开启或者关闭mysql服务端。

    以管理员的身份打开我们的终端:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-U9L9VmrB-1668513210136)(F:\newschool\笔记总结\MySQL笔记总结\MySQL笔记总结.assets\2EA9062E-16C9-419E-9E29-F13AC2CF6C50.png)]

    然后输入 mysqld --install 服务名(可以给这个服务一个名字,直接跟在这个指令的后面,不写也可以,会默认有一个,后面可以查看到)

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nePsBpFB-1668513210136)(F:\newschool\笔记总结\MySQL笔记总结\MySQL笔记总结.assets\9DB0AE6A-7EFC-4934-B9CC-F55E3CD686CF.png)]

    这样就添加系统服务了

    然后win键+r 打开运行窗口,里面输入services.msc来打开系统服务列表

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cEHXKChT-1668513210137)(F:\newschool\笔记总结\MySQL笔记总结\MySQL笔记总结.assets\D3FB6AD1-5F62-42FC-8D75-5345ED529951.png)]

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Q7oVvn9i-1668513210137)(F:\newschool\笔记总结\MySQL笔记总结\MySQL笔记总结.assets\4DC487B5-0757-4C13-88C4-9B000755E831.png)]

    选中MySQL服务这一项,我们就可以直接右键查看功能,功能里面有启动和关闭,就可以通过系统服务的形式来启动和关闭MySQL服务了,这个服务是默认开启的,我们也可以自行设置是否自动开机启动等等的行为,左键双击一下这个服务你就看到下面的窗口了。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-a6X5rTyy-1668513210138)(F:\newschool\笔记总结\MySQL笔记总结\MySQL笔记总结.assets\9F46E03A-2321-4AC3-BC97-2BF1F31C71E3.png)]

    添加了系统服务以后,我们在启动和关闭这个mysql服务,就可以在cmd窗口下使用两个指令就搞定了:

    启动指令:net start mysql

    关闭指令:net stop mysql

    并且不能再使用 mysqld指令直接启动了。

    还可以通过指令来移除刚才添加的系统服务:

    cmd下移除服务命令为:mysqld remove

总结

#1、下载:MySQL Community Server 5.7.16
http://dev.mysql/downloads/mysql/

#2、解压
如果想要让MySQL安装在指定目录,那么就将解压后的文件夹移动到指定目录,如:C:\mysql-5.7.16-winx64

#3、添加环境变量
【右键计算机】--》【属性】--》【高级系统设置】--》【高级】--》【环境变量】--》【在第二个内容框中找到 变量名为Path 的一行,双击】 --> 【将MySQL的bin目录路径追加到变值值中,用 ; 分割】
 
#4、初始化
mysqld --initialize-insecure

#5、启动MySQL服务
mysqld # 启动MySQL服务

#6、启动MySQL客户端并连接MySQL服务
mysql -u root -p # 连接MySQL服务器

#7、将mysql添加系统服务
    注意:--install前,必须用mysql启动命令的绝对路径
    # 制作MySQL的Windows服务,在终端执行此命令:
    "c:\mysql-5.7.16-winx64\bin\mysqld" --install
 
    # 移除MySQL的Windows服务,在终端执行此命令:
    "c:\mysql-5.7.16-winx64\bin\mysqld" --remove

    注册成服务之后,以后再启动和关闭MySQL服务时,仅需执行如下命令:
    # 启动MySQL服务
    net start mysql
 
    # 关闭MySQL服务
    net stop mysql

6.MySQL数据库的目录结构分析

其中,我们重点看一下data文件夹:如果你找不到自己建立的库或者表的文件,可能不在这个data文件夹下面,连接上mysql之后,输入show global variables like ``"%datadir%"``;来查看数据文件存储路径,找到路径之后,到对应路径下如果找不到这个文件夹,那么可能是隐藏的,把隐藏的文件显示一下就行了。

​ 其中mysql这个库我需要提一下:

​ mysql:这个是mysql整个服务的核心数据库,类似于sql server中的master表,主要负责存储数据库的用户、权限设置、关键字等mysql自己需要使用的控制和管理信息。不可以删除,如果对mysql不是很了解,也不要轻易修改这个数据库里面的表信息。

总结:其实这些库就是我们电脑上对应的文件夹,在mysql中显示为对应的库,来方便我们管理数据,而文件或者文件夹这种与硬盘打交道的事情就交给mysql了,我们只需要对mysql库中的数据进行操作就可以了,你可以看到,我们刚才简单使用的时候创建的一个crm库,也就是在data目录下的生成了一个crm文件夹。

2.MySQL配置文件

在MySQL的安装目录下面,创建一个my.ini自定制的配置文件:

[mysql]
default-character-set=utf8
user=root
password=123
[mysqld]
port=3306
# 服务端默认使用编码为latin1字符集 我们更改一下配置
character-set-server=utf8
# 存储引擎
default-storage-engine=INNODB

3.MySQL数据库的库的操作

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CFl2ZAD2-1668513210140)(F:\newschool\笔记总结\MySQL笔记总结\assets\AD5613C8-8083-4E92-AA04-0BBE7DBA7A84.png)]

1.创建数据库

语法

CREATE DATABASE 数据库名 charset utf8;

数据库命名规则

可以由字母、数字、下划线、@、#、$

区分大小写

唯一性

不能使用关键字如 create select

不能单独使用数字

最长128位

2.数据库相关操作

创建数据库
create database 库名 (charset='utf8');
1 查看数据库
show databases;   查看所有的数据库
show create database db1; 查看某个数据库的详细信息。
select database(); 查看当前使用的数据库

2 选择数据库
USE 数据库名

3 删除数据库
DROP DATABASE 数据库名;

4 修改数据库(用的少)
alter database db1 charset utf8;

以上所有的命令,一定要背过记住。

4.MySQL数据库数据引擎

1.存储引擎

存储引擎即表类型,mysql根据不同的表类型会有不同的处理机制,存储引擎的介绍:

首先确定一点,存储引擎的概念是MySQL里面才有的,不是所有的关系型数据库都有存储引擎这个概念,后面我们还会说,但是现在要确定这一点。

在讲清楚什么是存储引擎之前,我们先来个比喻,我们都知道录制一个视频文件,可以转换成不同的格式,例如mp4,avi,wmv等,而存在我们电脑的磁盘上也会存在于不同类型的文件系统中如windows里常见的ntfs、fat32,存在于linux里常见的ext3,ext4,xfs,但是,给我们或者用户看懂实际视频内容都是一样的。直观区别是,占用系统的空间大小与清晰程度可能不一样。
那么数据库表里的数据存储在数据库里及磁盘上和上述的视频格式及存储磁盘文件的系统格式特征类似,也有很多种存储方式。
  但是对于用户和应用程序来说同样一张表的数据,无论用什么引擎来存储,用户能够看到的数据是一样的。不同储引擎存取,引擎功能,占用空间大小,读取性能等可能有区别。说白了,存储引擎就是在如何存储数据、提取数据、更新数据等技术方法的实现上,底层的实现方式不同,那么就会呈现出不同存储引擎有着一些自己独有的特点和功能,对应着不同的存取机制。
  因为在关系数据库中数据的存储是以表的形式存储的,所以存储引擎也可以称为表类型(即:对表的存储、操作等的实现方法不同),表是什么,表本质上就是磁盘上的文件。

其实MySQL支持多种存储引擎,每种引擎有着一些自己独特的功能,用户在使用的时候,可以根据自己的业务场景来使用不同的存储引擎,其中MySQL最常用的存储引擎为:MyISAM和InnoDB。
在详细介绍这些存储引擎之前,我们先来看看MySQL的整个工作流程,看一下存储引擎在哪里,MySQL都做了哪些事情。

MySQL我们最少要记住四种存储引擎,我们主要是学习INNODB存储引擎。

英文版的看着很难受啊太白,好好好,给你来个中文的,看下图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZOtYGhk0-1668513210142)(F:\newschool\笔记总结\MySQL笔记总结\assets\151377B0-E02D-49A8-98CC-432B717D32FB.png)]

2.存储引擎的分类

前面我们对MySQL进行操作的时候并没有指定存储引擎,为什么还能用呢,没有指定还能用,说明什么,说明MySQL默认有一个存储引擎,我记得MySQL5.5之前,默认的存储引擎是MyISAM,之后都改为InnoDB了,我们的重点就是这个InnoDB,也是公司中最常用的、最好用的引擎,但是还是一些公司在用MyISAM引擎,除了历史原因之外,还有MyISAM的效率比InnoDB高一点,InnoDB在这方面做了很多优化,效率已经和MyISAM差不多了,但是InnoDB引擎还有好多其他的NB的功能,下面我们就介绍几种存储引擎。

首先看一下所有的存储引擎:

mysql> show engines;
+--------------------+---------+----------------------------------------------------------------+--------------+------+------------+
| Engine             | Support | Comment                                                        | Transactions | XA   | Savepoints |
+--------------------+---------+----------------------------------------------------------------+--------------+------+------------+
| InnoDB             | DEFAULT | Supports transactions, row-level locking, and foreign keys     | YES          | YES  | YES        |
| MRG_MYISAM         | YES     | Collection of identical MyISAM tables                          | NO           | NO   | NO         |
| PERFORMANCE_SCHEMA | YES     | Performance Schema                                             | NO           | NO   | NO         |
| BLACKHOLE          | YES     | /dev/null storage engine (anything you write to it disappears) | NO           | NO   | NO         |
| CSV                | YES     | CSV storage engine                                             | NO           | NO   | NO         |
| MyISAM             | YES     | MyISAM storage engine                                          | NO           | NO   | NO         |
| ARCHIVE            | YES     | Archive storage engine                                         | NO           | NO   | NO         |
| MEMORY             | YES     | Hash based, stored in memory, useful for temporary tables      | NO           | NO   | NO         |
| FEDERATED          | NO      | Federated MySQL storage engine                                 | NULL         | NULL | NULL       |
+--------------------+---------+----------------------------------------------------------------+--------------+------+------------+
9 rows in set (0.00 sec)

show variables like “%storage_engine%”;查看当前正在使用的引擎

+----------------------------------+--------+
| Variable_name                    | Value  |
+----------------------------------+--------+
| default_storage_engine           | InnoDB |

常见的两种存储引擎介绍

MyISAM引擎

MyISAM引擎特点:
        1.不支持事务
            事务是指逻辑上的一组操作,组成这组操作的各个单元,要么全成功要么全失败。
        2.表级锁定
            数据更新时锁定整个表:其锁定机制是表级锁定,也就是对表中的一个数据进行操作都会将这个表锁定,其他人不能操作这个表,这虽然可以让锁定的实现成本很小但是也同时大大降低了其并发性能。
        3.读写互相阻塞
            不仅会在写入的时候阻塞读取,MyISAM还会再读取的时候阻塞写入,但读本身并不会阻塞另外的读。
        4.只会缓存索引
            MyISAM可以通过key_buffer_size的值来提高缓存索引,以大大提高访问性能减少磁盘IO,但是这个缓存区只会缓存索引,而不会缓存数据。
        
        5.读取速度较快
            占用资源相对较少
        6.不支持外键约束,但只是全文索引
        7.MyISAM引擎是MySQL5.5版本之前的默认引擎,是对最初的ISAM引擎优化的产物。

InnoDB引擎

InnoDB引擎
        介绍:InnoDB引擎是MySQL数据库的另一个重要的存储引擎,正称为目前MySQL AB所发行新版的标准,被包含在所有二进制安装包里。和其他的存储引擎相比,InnoDB引擎的优点是支持兼容ACID的事务(类似于PostGreSQL),以及参数完整性(即对外键的支持)。Oracle公司与2005年10月收购了Innobase。Innobase采用双认证授权。它使用GNU发行,也允许其他想将InnoDB结合到商业软件的团体获得授权。

InnoDB引擎特点:
        1.支持事务:支持4个事务隔离界别,支持多版本读。
        2.行级锁定(更新时一般是锁定当前行):通过索引实现,全表扫描仍然会是表锁,注意间隙锁的影响。
        3.读写阻塞与事务隔离级别相关(有多个级别,这就不介绍啦~)。
        4.具体非常高效的缓存特性:能缓存索引,也能缓存数据。
        5.整个表和主键与Cluster方式存储,组成一颗平衡树。(了解)
        6.所有SecondaryIndex都会保存主键信息。(了解)
        7.支持分区,表空间,类似oracle数据库。
        8.支持外键约束,不支持全文索引(5.5之前),以后的都支持了。
        9.和MyISAM引擎比较,InnoDB对硬件资源要求还是比较高的。
        
        小结:三个重要功能:Supports transactions,row-level locking,and foreign keys

MyISAM 与 InnoDB的区别:

InnoDB: MySQL5.5之后,默认指定的存储引擎。支持事务,行级锁定,支持外键,支持全文索引。
MyISAM:MySQL5.5之前,默认指定的存储引擎。不支持事务,表级锁定,不支持外键,支持全文索引。

事务介绍

事务介绍:
        简单地说,事务就是指逻辑上的一组SQL语句操作,组成这组操作的各个SQL语句,执行时要么全成功要么全失败。
        
        例如:你给我转账5块钱,流程如下
            a.从你银行卡取出5块钱,剩余计算money-5
            b.把上面5块钱打入我的账户上,我收到5块,剩余计算money+5.
        上述转账的过程,对应的sql语句为:
                update 你_account set money=money-5 where name='你';
                update 我_account set money=money+5 where name='我';
        上述的两条SQL操作,在事务中的操作就是要么都执行,要么都不执行,不然钱就对不上了。
        这就是事务的原子性(Atomicity)。
        
        
    事务的四大特性:
        1.原子性(Atomicity)
            事务是一个不可分割的单位,事务中的所有SQL等操作要么都发生,要么都不发生。
        2.一致性(Consistency)
            事务发生前和发生后,数据的完整性必须保持一致。
        3.隔离性(Isolation)
            当并发访问数据库时,一个正在执行的事务在执行完毕前,对于其他的会话是不可见的,多个并发事务之间的数据是相互隔离的。也就是其他人的操作在这个事务的执行过程中是看不到这个事务的执行结果的,也就是他们拿到的是这个事务执行之前的内容,等这个事务执行完才能拿到新的数据。
        4.持久性(Durability)
            一个事务一旦被提交,它对数据库中的数据改变就是永久性的。如果出了错误,事务也不允撤销,只能通过'补偿性事务'。
        
        
    事务的开启:
        数据库默认事务是自动提交的,也就是发一条sql他就执行一条。如果想多条sql放在一个事务中执行,则需要使用事务进行处理。当我们开启一个事务,并且没有提交,mysql会自动回滚事务。或者我们使用rollback命令手动回滚事务。
        数据库开启事务的命令,我们后面会讲到~~~

​ 其他引擎介绍

#NDB 存储引擎
2003 年,MySQL AB 公司从 Sony Ericsson 公司收购了 NDB 存储引擎。 NDB 存储引擎是一个集群存储引擎,类似于 Oracle 的 RAC 集群,不过与 Oracle RAC 的 share everything 结构不同的是,其结构是 share nothing 的集群架构,因此能提供更高级别的 高可用性。NDB 存储引擎的特点是数据全部放在内存中(从 5.1 版本开始,可以将非索引数 据放在磁盘上),因此主键查找(primary key lookups)的速度极快,并且能够在线添加 NDB 数据存储节点(data node)以便线性地提高数据库性能。由此可见,NDB 存储引擎是高可用、 高性能、高可扩展性的数据库集群系统,其面向的也是 OLTP 的数据库应用类型。

#Memory 存储引擎
正如其名,Memory 存储引擎中的数据都存放在内存中,数据库重 启或发生崩溃,表中的数据都将消失。它非常适合于存储 OLTP 数据库应用中临时数据的临时表,也可以作为 OLAP 数据库应用中数据仓库的维度表。Memory 存储引擎默认使用哈希 索引,而不是通常熟悉的 B+ 树索引。

#Infobright 存储引擎
第三方的存储引擎。其特点是存储是按照列而非行的,因此非常 适合 OLAP 的数据库应用。其官方网站是 http://www.infobright/,上面有不少成功的数据 仓库案例可供分析。

#NTSE 存储引擎
网易公司开发的面向其内部使用的存储引擎。目前的版本不支持事务, 但提供压缩、行级缓存等特性,不久的将来会实现面向内存的事务支持。

#BLACKHOLE
黑洞存储引擎,可以应用于主备复制中的分发主库。

MySQL 数据库还有很多其他存储引擎,上述只是列举了最为常用的一些引擎。如果 你喜欢,完全可以编写专属于自己的引擎,这就是开源赋予我们的能力,也是开源的魅 力所在。

5.MySQL数据库的表的操作

5.1表的介绍

MySQL中的表就是一个文件,类比成一个excel文件:

mysql> select * from emp;
+----+--------------+--------+
| id | name         | dep_id |
+----+--------------+--------+
|  1 | 太白金星     |      3 |
|  2 | 吴老师       |      1 |
|  3 | 刘老师       |      2 |
+----+--------------+--------+
3 rows in set (0.00 sec)

Id,name,dep_id 在excel表中,我们称之为列名,在MySQL中称之为字段。

1 太白金星 3 在excel表中,我们称之为一行数据,在MySQL中称之为行记录。

5.2 创建表

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

#注意:
1. 在同一张表中,字段名是不能相同
2. 宽度和约束条件可选、非必须,宽度指的就是字段长度约束,例如:char(10)里面的10
3. 字段名和类型是必须的

测试:

mysql> create database db1;
Query OK, 1 row affected (0.00 sec)

mysql> use db1;
Database changed
mysql> create table t1(
    -> id int,
    -> name varchar(20),
    -> sex enum('male', 'female'),
    -> age int);
Query OK, 0 rows affected (0.04 sec)

mysql> show tables;  # 查看当前数据库中所有的表。
+---------------+
| Tables_in_db1 |
+---------------+
| t1            |
+---------------+
1 row in set (0.00 sec)

mysql> desc t1;  # 查看t1表的表结构
+-------+-----------------------+------+-----+---------+-------+
| Field | Type                  | Null | Key | Default | Extra |
+-------+-----------------------+------+-----+---------+-------+
| id    | int(11)               | YES  |     | NULL    |       |
| name  | varchar(20)           | YES  |     | NULL    |       |
| sex   | enum('male','female') | YES  |     | NULL    |       |
| age   | int(11)               | YES  |     | NULL    |       |
+-------+-----------------------+------+-----+---------+-------+
4 rows in set (0.00 sec)

# 给表插入数据(行记录的操作)

# 语法1
insert into 表名(字段名1,字段名2,...) values(值1, 值2, 值3...);

# 语法2
insert into 表名 values(值1,值2,值3), (值1,值2,值3), ....;

测试:
mysql> insert into t1(id,name,sex) values(1, '明航', 'male');
Query OK, 1 row affected (0.00 sec)

mysql> select * from t1;   (行记录的操作,查看此表中的所有的行记录的所有的字段)
+------+--------+------+------+
| id   | name   | sex  | age  |
+------+--------+------+------+
|    1 | 明航   | male | NULL |
+------+--------+------+------+
1 row in set (0.00 sec)

mysql> insert into t1 values
    -> (2, '菜籽', 'female', 20),
    -> (3, '葱烧', 'female', 21),
    -> (4, '大招', 'male', 19);
Query OK, 3 rows affected (0.01 sec)
Records: 3  Duplicates: 0  Warnings: 0

mysql> select * from t1; (行记录的操作,查看此表中的所有的行记录的所有的字段)
+------+--------+--------+------+
| id   | name   | sex    | age  |
+------+--------+--------+------+
|    1 | 明航   | male   | NULL |
|    2 | 菜籽   | female |   20 |
|    3 | 葱烧   | female |   21 |
|    4 | 大招   | male   |   19 |
+------+--------+--------+------+
4 rows in set (0.00 sec)

mysql> select id,name,sex from t1;  (行记录的操作,查看此表中的所有的行记录的指定字段)
+------+--------+--------+
| id   | name   | sex    |
+------+--------+--------+
|    1 | 明航   | male   |
|    2 | 菜籽   | female |
|    3 | 葱烧   | female |
|    4 | 大招   | male   |
+------+--------+--------+
4 rows in set (0.00 sec)

表的指令:

创建表
create table 表名(
字段名1 类型[(宽度) 约束条件],
字段名2 类型[(宽度) 约束条件],
字段名3 类型[(宽度) 约束条件]
);

查看所有的表
show tables;

查看某个表的表结构:
mysql> desc t1;  # 查看t1表的表结构

未完待续......

6.mysql数据库的基础数据类型

数值类型

1、整数类型

    整数类型:TINYINT,SMALLINT,MEDIUMINT,INT,BIGINT

    作用:存储年龄,等级,id,各种号码等

对于整型来说,数据类型后面的宽度并不是存储长度限制,而是显示限制,假如:int(8),那么显示时不够8位则用0来填充,够8位则正常显示,通过zerofill来测试,存储长度还是int的4个字节长度。默认的显示宽度就是能够存储的最大的数据的长度,比如:int无符号类型,那么默认的显示宽度就是int(10),有符号的就是int(11),因为多了一个符号,所以我们没有必要指定整数类型的数据,没必要指定宽度,因为默认的就能够将你存的原始数据完全显示

​  

int的存储宽度是4个Bytes,即32个bit,即2**32

无符号最大值为:4294967296-1

有符号最大值:2147483648-1

有符号和无符号的最大数字需要的显示宽度均为10,而针对有符号的最小值则需要11位才能显示完全,所以int类型默认的显示宽度为11是非常合理的

最后:整形类型,其实没有必要指定显示宽度,使用默认的就ok

数值测试

有符号和无符号tinyint
    1.tinyint默认为有符号
        mysql> create table t1(x tinyint); #默认为有符号,即数字前有正负号
        mysql> desc t1;
        mysql> insert into t1 values
            -> (-129),
            -> (-128),
            -> (127),
            -> (128);
        mysql> select * from t1;
        +------+
        | x    |
        +------+
        | -128 | #-129存成了-128
        | -128 | #有符号,最小值为-128
        |  127 | #有符号,最大值127
        |  127 | #128存成了127
        +------+
5.7.40版本改成如果超出范围报错


    2.设置无符号tinyint
        mysql> create table t2(x tinyint unsigned);
        mysql> insert into t2 values
            -> (-1),
            -> (0),
            -> (255),
            -> (256);
        mysql> select * from t2;
        +------+
        | x    |
        +------+
        |    0 | -1存成了0
        |    0 | #无符号,最小值为0
        |  255 | #无符号,最大值为255
        |  255 | #256存成了255
        +------+
5.7.40版本改成如果超出范围报错



	3. 设置有符号的int
          mysql> create table t2(id int);
      Query OK, 0 rows affected (0.03 sec)

      mysql> insert into t2 values
          -> (-100),
          -> (-3),
          -> (200);
      Query OK, 3 rows affected (0.00 sec)
      Records: 3  Duplicates: 0  Warnings: 0

      mysql> select * from t2;
      +------+
      | id   |
      +------+
      | -100 |
      |   -3 |
      |  200 |
      +------+
      3 rows in set (0.00 sec)
   4. 设置无符号的int
   mysql> create table t3(id int unsigned);
Query OK, 0 rows affected (0.03 sec)
mysql> select * from t3;
Empty set (0.00 sec)

mysql> insert into t3 values (-100);  # 负数不能增加
ERROR 1264 (22003): Out of range value for column 'id' at row 1
=
mysql> insert into t3 values(100);
Query OK, 1 row affected (0.01 sec)

mysql> insert into t3 values(1000);
Query OK, 1 row affected (0.00 sec)

mysql> select * from t3;
+------+
| id   |
+------+
|  100 |
| 1000 |
+------+
2 rows in set (0.00 sec)

有符号和无符号bigint
    1.有符号bigint
        mysql> create table t6(x bigint);
        mysql> insert into t5 values  
            -> (-9223372036854775809),
            -> (-9223372036854775808),
            -> (9223372036854775807),
            -> (9223372036854775808);

        mysql> select * from t5;
        +----------------------+
        | x                    |
        +----------------------+
        | -9223372036854775808 |
        | -9223372036854775808 |
        |  9223372036854775807 |
        |  9223372036854775807 |
        +----------------------+
5.7.40版本改成如果超出范围报错

    2.无符号bigint
        mysql> create table t6(x bigint unsigned);
        mysql> insert into t6 values  
            -> (-1),
            -> (0),
            -> (18446744073709551615),
            -> (18446744073709551616);

        mysql> select * from t6;
        +----------------------+
        | x                    |
        +----------------------+
        |                    0 |
        |                    0 |
        | 18446744073709551615 |
        | 18446744073709551615 |
        +----------------------+

5.7.40版本改成如果超出范围报错


用zerofill测试整数类型的显示宽度
mysql> create table t4(id int(3));
Query OK, 0 rows affected (0.03 sec)

mysql> insert into t4 values(1);
Query OK, 1 row affected (0.00 sec)

mysql> insert into t4 values(1000);
Query OK, 1 row affected (0.00 sec)

mysql> insert into t4 values(234563);
Query OK, 1 row affected (0.00 sec)

mysql> select * from t4;
+--------+
| id     |
+--------+
|      1 |
|   1000 |
| 234563 |
+--------+
3 rows in set (0.00 sec)


mysql> create table t5(id int(4) zerofill);
Query OK, 0 rows affected (0.03 sec)

mysql> insert into t5 values(1);
Query OK, 1 row affected (0.00 sec)

mysql> insert into t5 values(19);
Query OK, 1 row affected (0.00 sec)

mysql> insert into t5 values(187);
Query OK, 1 row affected (0.00 sec)

mysql> insert into t5 values(2000);
Query OK, 1 row affected (0.00 sec)

mysql> insert into t5 values(19800);
Query OK, 1 row affected (0.00 sec)

mysql> select * from t5;
+-------+
| id    |
+-------+
|  0001 |
|  0019 |
|  0187 |
|  2000 |
| 19800 |
+-------+
5 rows in set (0.00 sec)

Int(n)不会限制插入的数据的范围,只是不满足n个字符的数字补充到n个字符。

浮点型

定点数类型 DEC,等同于DECIMAL

浮点类型:FLOAT DOUBLE

作用:存储薪资、身高、温度、体重、体质参数等

1.FLOAT[(M,D)] [UNSIGNED] [ZEROFILL]

    定义:
            单精度浮点数(非准确小数值),m是整数部分+小数部分的总个数,d是小数点后个数。m最大值为255,d最大值为30,例如:float(255,30)

    有符号:
               -3.402823466E+38 to -1.175494351E-38,
               1.175494351E-38 to 3.402823466E+38
    无符号:
               1.175494351E-38 to 3.402823466E+38


    精确度: 
               **** 随着小数的增多,精度变得不准确 ****



2.DOUBLE[(M,D)] [UNSIGNED] [ZEROFILL]

    定义:
               双精度浮点数(非准确小数值),m是整数部分+小数部分的总个数,d是小数点后个数。m最大值也为255,d最大值也为30

    有符号:
               -1.7976931348623157E+308 to -2.2250738585072014E-308
               2.2250738585072014E-308 to 1.7976931348623157E+308

    无符号:
               2.2250738585072014E-308 to 1.7976931348623157E+308
                
    精确度:
               ****随着小数的增多,精度比float要高,但也会变得不准确 ****


3.decimal[(m[,d])] [unsigned] [zerofill]

    定义:
              准确的小数值,m是整数部分+小数部分的总个数(负号不算),d是小数点后个数。 m最大值为65,d最大值为30。比float和double的整数个数少,但是小数位数都是30位


    精确度:
               **** 随着小数的增多,精度始终准确 ****
               对于精确数值计算时需要用此类型
               decimal能够存储精确值的原因在于其内部按照字符串存储。
精度从高到低:decimal、double、float
    decimal精度高,但是整数位数少
    float和double精度低,但是整数位数多
float已经满足绝大多数的场景了,但是什么导弹、航线等要求精度非常高,所以还是需要按照业务场景自行选择,如果又要精度高又要整数位数多,那么你可以直接用字符串来存。

浮点型测试:

mysql> create table t6(x float(40,30));
Query OK, 0 rows affected (0.03 sec)

mysql> create table t7(x double(40,30));
Query OK, 0 rows affected (0.03 sec)

mysql> create table t8(x decimal(40,30));
Query OK, 0 rows affected (0.02 sec)

mysql> insert into t6 values(1.111111111111111111111111111111);
Query OK, 1 row affected (0.00 sec)

mysql> insert into t7 values(1.111111111111111111111111111111);
Query OK, 1 row affected (0.00 sec)

mysql> insert into t8 values(1.111111111111111111111111111111);
Query OK, 1 row affected (0.00 sec)

mysql> select * from t6;
+----------------------------------+
| x                                |
+----------------------------------+
| 1.111111164093017600000000000000 |
+----------------------------------+
1 row in set (0.00 sec)

mysql> select * from t7;
+----------------------------------+
| x                                |
+----------------------------------+
| 1.111111111111111200000000000000 |
+----------------------------------+
1 row in set (0.00 sec)

mysql> select * from t8;
+----------------------------------+
| x                                |
+----------------------------------+
| 1.111111111111111111111111111111 |
+----------------------------------+
1 row in set (0.00 sec)

字符串类型

​ 类型:char,varchar

作用:名字,信息等等

#官网:https://dev.mysql/doc/refman/5.7/en/char.html
#注意:char和varchar括号内的参数指的都是字符的长度

#char类型:定长,简单粗暴,浪费空间,存取速度快
    字符长度范围:0-255(一个中文是一个字符,是utf8编码的3个字节)
    存储:
        存储char类型的值时,会往右填充空格来满足长度
        例如:指定长度为10,存>10个字符则报错(严格模式下),存<10个字符则用空格填充直到凑够10个字符存储

    检索:
        在检索或者说查询时,查出的结果会自动删除尾部的空格,如果你想看到它补全空格之后的内容,除非我们打开pad_char_to_full_length SQL模式(SET sql_mode = 'strict_trans_tables,PAD_CHAR_TO_FULL_LENGTH';)

#varchar类型:变长,精准,节省空间,存取速度慢
    字符长度范围:0-65535(如果大于21845会提示用其他类型 。mysql行最大限制为65535字节,字符编码为utf-8:https://dev.mysql/doc/refman/5.7/en/column-count-limit.html)
    存储:
        varchar类型存储数据的真实内容,不会用空格填充,如果'ab  ',尾部的空格也会被存起来
        强调:varchar类型会在真实数据前加1-2Bytes的前缀,该前缀用来表示真实数据的bytes字节数(1-2Bytes最大表示65535个数字,正好符合mysql对row的最大字节限制,即已经足够使用)
        如果真实的数据<255bytes则需要1Bytes的前缀(1Bytes=8bit 2**8最大表示的数字为255)
        如果真实的数据>255bytes则需要2Bytes的前缀(2Bytes=16bit 2**16最大表示的数字为65535)
    
    检索:
        尾部有空格会保存下来,在检索或者说查询时,也会正常显示包含空格在内的内容

char和varchar性能对比:
    以char(5)和varchar(5)来比较,加入我要存三个人名:sb,ssb1,ssbb2
    char:
      优点:简单粗暴,不管你是多长的数据,我就按照规定的长度来存,5个5个的存,三个人名就会类似这种存储:sb ssb1 ssbb2,中间是空格补全,取数据的时候5个5个的取,简单粗暴速度快
      缺点:貌似浪费空间,并且我们将来存储的数据的长度可能会参差不齐

varchar:
      varchar类型不定长存储数据,更为精简和节省空间
      例如存上面三个人名的时候类似于是这样的:sbssb1ssbb2,连着的,如果这样存,请问这三个人名你还怎么取出来,你知道取多长能取出第一个吗?(太白,我能看出来啊,那我只想说:滚犊子!)
      不知道从哪开始从哪结束,遇到这样的问题,你会想到怎么解决呢?还记的吗?想想?socket?tcp?struct?把数据长度作为消息头。

所以,varchar在存数据的时候,会在每个数据前面加上一个头,这个头是1-2个bytes的数据,这个数据指的是后面跟着的这个数据的长度,1bytes能表示2**8=256,两个bytes表示2**16=65536,能表示0-65535的数字,所以varchar在存储的时候是这样的:1bytes+sb+1bytes+ssb1+1bytes+ssbb2,所以存的时候会比较麻烦,导致效率比char慢,取的时候也慢,先拿长度,再取数据。

优点:节省了一些硬盘空间,一个acsii码的字符用一个bytes长度就能表示,但是也并不一定比char省,看一下官网给出的一个表格对比数据,当你存的数据正好是你规定的字段长度的时候,varchar反而占用的空间比char要多。

ValueCHAR(4)Storage RequiredVARCHAR(4)Storage Required
''' '4 bytes''1 byte
'ab''ab '4 bytes'ab'3 bytes
'abcd''abcd'4 bytes'abcd'5 bytes
'abcdefgh''abcd'4 bytes'abcd'5 bytes

​ 缺点:存取速度都慢

总结:
    所以需要根据业务需求来选择用哪种类型来存
    其实在多数的用户量少的工作场景中char和varchar效率差别不是很大,最起码给用户的感知不是很大,并且其实软件级别的慢远比不上硬件级别的慢,所以你们公司的运维发现项目慢的时候会加内存、换nb的硬盘,项目的效率提升的会很多,但是我们作为专业人士,我们应该提出来这样的技术点来提高效率。

但是对于InnoDB数据表,内部的行存储格式没有区分固定长度和可变长度列(所有数据行都使用指向数据列值的头指针),因此在本质上,使用固定长度的CHAR列不一定比使用可变长度VARCHAR列性能要好。因而,主要的性能因素是数据行使用的存储总量。由于CHAR平均占用的空间多于VARCHAR,因此使用VARCHAR来最小化需要处理的数据行的存储总量和磁盘I/O是比较好的。

所以啊,两个选哪个都可以,如果是大型并发项目,追求高性能的时候,需要结合你们服务器的硬件环境来进行测试,看一下char和varchar哪个更好,这也能算一个优化的点吧~~~~

其他的字符串类型:BINARY、VARBINARY、BLOB、TEXT

测试:

mysql> create table t9(x char(4), y varchar(4));
Query OK, 0 rows affected (0.03 sec)

mysql> insert into t9 values
    -> ('中国万岁', '中国统一');
Query OK, 1 row affected (0.00 sec)

mysql> insert into t9 values ('中国万岁111', '中国统一111');
ERROR 1406 (22001): Data too long for column 'x' at row 1
mysql> 

日期类型

类型:DATE,TIME,DATETIME ,IMESTAMP,YEAR

作用:存储用户注册时间,文章发布时间,员工入职时间,出生时间,过期时间等

		YEAR
            YYYY(范围:1901/2155)2018

        DATE
            YYYY-MM-DD(范围:1000-01-01/9999-12-31)例:2018-01-01 

        TIME
            HH:MM:SS(范围:'-838:59:59'/'838:59:59')例:12:09:32

        DATETIME

            YYYY-MM-DD HH:MM:SS(范围:1000-01-01 00:00:00/9999-12-31 23:59:59    Y)例: 2018-01-01 12:09:32

        TIMESTAMP

            YYYYMMDD HHMMSS(范围:1970-01-01 00:00:00/2037 年某时)

测试:

mysql> create table t1(
    -> born_year year);
Query OK, 0 rows affected (0.05 sec)

mysql> insert into t1 values
    -> (2019),
    -> (2018),
    -> (2020);
Query OK, 3 rows affected (0.00 sec)
Records: 3  Duplicates: 0  Warnings: 0

mysql> select * from t1;
+-----------+
| born_year |
+-----------+
|      2019 |
|      2018 |
|      2020 |
+-----------+
3 rows in set (0.00 sec)

mysql> create table t2(
    -> d date,
    -> t time,
    -> dt datetime);
Query OK, 0 rows affected (0.03 sec)

mysql> desc t2;
+-------+----------+------+-----+---------+-------+
| Field | Type     | Null | Key | Default | Extra |
+-------+----------+------+-----+---------+-------+
| d     | date     | YES  |     | NULL    |       |
| t     | time     | YES  |     | NULL    |       |
| dt    | datetime | YES  |     | NULL    |       |
+-------+----------+------+-----+---------+-------+
3 rows in set (0.00 sec)

mysql> insert into t2 values
    -> ('2020-11-12', '09:16:34','2020-11-12 09:16:34');
Query OK, 1 row affected (0.00 sec)

mysql> select * from t2;
+------------+----------+---------------------+
| d          | t        | dt                  |
+------------+----------+---------------------+
| 2020-11-12 | 09:16:34 | 2020-11-12 09:16:34 |
+------------+----------+---------------------+
1 row in set (0.00 sec)

mysql> insert into t2 values (now(),now(),now());
Query OK, 1 row affected, 1 warning (0.00 sec)

mysql> select * from t2;
+------------+----------+---------------------+
| d          | t        | dt                  |
+------------+----------+---------------------+
| 2020-11-12 | 09:16:34 | 2020-11-12 09:16:34 |
| 2022-11-15 | 09:18:08 | 2022-11-15 09:18:08 |
+------------+----------+---------------------+
2 rows in set (0.00 sec)

# timestamp 可以自动获取当前时间并插入。
mysql> create table t3(
    -> name char(5),
    -> ts timestamp);
Query OK, 0 rows affected (0.03 sec)

mysql> insert into t3(name) values
    -> ('barry'),
    -> ('明航');
Query OK, 2 rows affected (0.00 sec)
Records: 2  Duplicates: 0  Warnings: 0

mysql> select * from t3;
+--------+---------------------+
| name   | ts                  |
+--------+---------------------+
| barry  | 2022-11-15 09:20:08 |
| 明航   | 2022-11-15 09:20:08 |
+--------+---------------------+
2 rows in set (0.00 sec)

mysql> insert into t3(name,ts) values ('阿珍',null);
Query OK, 1 row affected (0.00 sec)

mysql> select * from t3;
+--------+---------------------+
| name   | ts                  |
+--------+---------------------+
| barry  | 2022-11-15 09:20:08 |
| 明航   | 2022-11-15 09:20:08 |
| 阿珍   | 2022-11-15 09:21:32 |
+--------+---------------------+
3 rows in set (0.00 sec)

============注意啦,注意啦,注意啦===========
    1. 单独插入时间时,需要以字符串的形式,按照对应的格式插入
    2. 插入年份时,尽量使用4位值
    3. 插入两位年份时,<=69,以20开头,比如50,  结果2050      
                    >=70,以19开头,比如71,结果1971


mysql> create table t4(y year);
Query OK, 0 rows affected (0.03 sec)

mysql> insert into t4 values
    -> (23),
    -> (78),
    -> (70);
Query OK, 3 rows affected (0.00 sec)
Records: 3  Duplicates: 0  Warnings: 0

mysql> select * from t4;
+------+
| y    |
+------+
| 2023 |
| 1978 |
| 1970 |
+------+
3 rows in set (0.00 sec)



============综合练习===========
    mysql> create table student(
        -> id int,
        -> name varchar(20),
        -> born_year year,
        -> birth date,
        -> class_time time,
        -> reg_time datetime);

    mysql> insert into student values
        -> (1,'sb1',"1995","1995-11-11","11:11:11","2017-11-11 11:11:11"),
        -> (2,'sb2',"1997","1997-12-12","12:12:12","2017-12-12 12:12:12"),
        -> (3,'sb3',"1998","1998-01-01","13:13:13","2017-01-01 13:13:13");  

    mysql> select * from student;
    +------+------+-----------+------------+------------+---------------------+
    | id   | name | born_year | birth      | class_time | reg_time            |
    +------+------+-----------+------------+------------+---------------------+
    |    1 | sb1 |      1995 | 1995-11-11 | 11:11:11   | 2017-11-11 11:11:11 |
    |    2 | sb2 |      1997 | 1997-12-12 | 12:12:12   | 2017-12-12 12:12:12 |
    |    3 | sb3 |      1998 | 1998-01-01 | 13:13:13   | 2017-01-01 13:13:13 |
    +------+------+-----------+------------+------------+---------------------+

枚举与集合类型

字段的值只能在给定范围中选择,如单选框,多选框,如果你在应用程序或者前端不做选项限制,在MySQL的字段里面也能做限制

enum 单选 只能在给定的范围内选一个值,如性别 sex 男male/女female
  set 多选 在给定的范围内可以选择一个或一个以上的值(爱好1,爱好2,爱好3…)

		枚举类型(enum)
            An ENUM column can have a maximum of 65,535 distinct elements. (The practical limit is less than 3000.)
            示例:
                CREATE TABLE shirts (
                    name VARCHAR(40),
                    size ENUM('x-small', 'small', 'medium', 'large', 'x-large')
                );
                INSERT INTO shirts (name, size) VALUES ('dress shirt','large'), ('t-shirt','medium'),('polo shirt','small');

  

          集合类型(set)
            A SET column can have a maximum of 64 distinct members.
            示例:
                CREATE TABLE myset (col SET('a', 'b', 'c', 'd'));
                INSERT INTO myset (col) VALUES ('a,d'), ('d,a'), ('a,d,a'), ('a,d,d'), ('d,a,d');

测试:

mysql> create table consumer(
    -> name char(5),
    -> sex enum('male', 'female', 'laddyboy'),
    -> hobby set('play', 'music', 'read', 'study', 'girl'));
Query OK, 0 rows affected (0.03 sec)

mysql> insert into consumer values
    -> ('荣轩', 'male', 'play,girl,study'),
    -> ('旋哥', 'laddyboy', 'music,play');
Query OK, 2 rows affected (0.00 sec)
Records: 2  Duplicates: 0  Warnings: 0

mysql> select * from consumer;
+--------+----------+-----------------+
| name   | sex      | hobby           |
+--------+----------+-----------------+
| 荣轩   | male     | play,study,girl |
| 旋哥   | laddyboy | play,music      |
+--------+----------+-----------------+
2 rows in set (0.01 sec)

mysql> insert into consumer values ('荣轩', '男', 'play,girl,study');
ERROR 1265 (01000): Data truncated for column 'sex' at row 1
# set集合多选时,选项通过逗号隔开,但是不能有空格。
mysql> insert into consumer values ('天伦', 'male', 'play, girl, read');
ERROR 1265 (01000): Data truncated for column 'hobby' at row 1
mysql> insert into consumer values ('天伦', 'male', 'play,run');
ERROR 1265 (01000): Data truncated for column 'hobby' at row 1

3.表的约束

介绍

约束条件与数据类型的宽度一样,都是可选参数

作用:用于保证数据的完整性和一致性

主要分为

PRIMARY KEY (PK)    标识该字段为该表的主键,可以唯一的标识记录
FOREIGN KEY (FK)    标识该字段为该表的外键
NOT NULL    标识该字段不能为空
UNIQUE KEY (UK)    标识该字段的值是唯一的
AUTO_INCREMENT    标识该字段的值自动增长(整数类型,而且为主键)
DEFAULT    为该字段设置默认值

UNSIGNED 无符号
ZEROFILL 使用0填充

说明

1. 是否允许为空,默认NULL,可设置NOT NULL,字段不允许为空,必须赋值
2. 字段是否有默认值,缺省的默认值是NULL,如果插入记录时不给字段赋值,此字段使用默认值
sex enum('male','female') not null default 'male'
age int unsigned NOT NULL default 20 必须为正值(无符号) 不允许为空 默认是20
3. 是否是key
主键 primary key
外键 foreign key
索引 (index,unique...)

not null 与 default

是否可空,null表示空,非字符串,我们创建表时,如果不指定null还是not null,默认该字段是null可以为空。
not null - 不可空
null - 可空

default 默认值。一般如果一个字段设置为不为空都会给其配合一个默认值。

测试:

# 创建表时,不指定null还是not null,默认该字段是null可以为空。
mysql> create table t5(
    -> id int);
Query OK, 0 rows affected (0.03 sec)

mysql> insert into t5 values (1), (null);
Query OK, 2 rows affected (0.00 sec)
Records: 2  Duplicates: 0  Warnings: 0

mysql> select * from t5;
+------+
| id   |
+------+
|    1 |
| NULL |
+------+
2 rows in set (0.00 sec)
mysql> desc t5;
+-------+---------+------+-----+---------+-------+
| Field | Type    | Null | Key | Default | Extra |
+-------+---------+------+-----+---------+-------+
| id    | int(11) | YES  |     | NULL    |       |
+-------+---------+------+-----+---------+-------+
1 row in set (0.00 sec)

# 设置不为空,则不能插入空数据
mysql> create table t6(
    -> id int,
    -> name char(5) not null);
Query OK, 0 rows affected (0.03 sec)
	# 正常插入数据没有问题
mysql> insert into t6 values
    -> (1, '姚聪');
    # 如果我们插入null,或者什么都不加,都会报错
mysql> insert into t6(id) values (3);
ERROR 1364 (HY000): Field 'name' doesn't have a default value
mysql> insert into t6(id,name) values (3,null);
ERROR 1048 (23000): Column 'name' cannot be null

==================default====================

mysql> create table t7(
    -> id int not null default 1,
    -> name char(5) default '小明');
Query OK, 0 rows affected (0.03 sec)

mysql> insert into t7 values
    -> (1, 'barry');
Query OK, 1 row affected (0.01 sec)

# id字段不能插入null数据
mysql> insert into t7 values
    -> (null, '改博');
ERROR 1048 (23000): Column 'id' cannot be null

# 如果不给id字段插入数据,则默认为1.
mysql> insert into t7(name) values ('改博');
Query OK, 1 row affected (0.00 sec)

mysql> select * from t7;
+----+--------+
| id | name   |
+----+--------+
|  1 | barry  |
|  1 | 改博   |
+----+--------+
2 rows in set (0.00 sec)
# 如果不给name字段插入数据,则默认使用default指定的小明数据。
mysql> insert into t7(id) values (3);
Query OK, 1 row affected (0.00 sec)

==================综合练习====================
mysql> create table student(
    -> name varchar(20) not null,
    -> age int(3) unsigned not null default 18,
    -> sex enum('male','female') default 'male',
    -> hobby set('play','study','read','music') default 'play,music'
    -> );
mysql> desc student;
+-------+------------------------------------+------+-----+------------+-------+
| Field | Type                               | Null | Key | Default    | Extra |
+-------+------------------------------------+------+-----+------------+-------+
| name  | varchar(20)                        | NO   |     | NULL       |       |
| age   | int(3) unsigned                    | NO   |     | 18         |       |
| sex   | enum('male','female')              | YES  |     | male       |       |
| hobby | set('play','study','read','music') | YES  |     | play,music |       |
+-------+------------------------------------+------+-----+------------+-------+
mysql> insert into student(name) values('chao');
mysql> select * from student;
+------+-----+------+------------+
| name | age | sex  | hobby      |
+------+-----+------+------------+
| chao|  18 | male | play,music |
+------+-----+------+------------+

unique

独一无二,唯一属性:id,身份证号,电话号码,邮箱等等。

是一种key,唯一键,是在数据类型之外的附加属性,其实还有加速查询的作用。

单独字段创建唯一约束

# 方式一
mysql> create table t8(
    -> id int,
    -> name varchar(5) unique);
Query OK, 0 rows affected (0.03 sec)

mysql> insert into t8 values
    -> (1, 'barry');
Query OK, 1 row affected (0.00 sec)

mysql> insert into t8 values (1,'明航'),(2, 'barry');
ERROR 1062 (23000): Duplicate entry 'barry' for key 'name'
mysql> desc t8;
+-------+------------+------+-----+---------+-------+
| Field | Type       | Null | Key | Default | Extra |
+-------+------------+------+-----+---------+-------+
| id    | int(11)    | YES  |     | NULL    |       |
| name  | varchar(5) | YES  | UNI | NULL    |       |  设置唯一key
+-------+------------+------+-----+---------+-------+

# 方式二
mysql> create table t9(
    -> id int,
    -> name varchar(5),
    -> constraint ukname unique(name));
Query OK, 0 rows affected (0.03 sec)

mysql> desc t9;
+-------+------------+------+-----+---------+-------+
| Field | Type       | Null | Key | Default | Extra |
+-------+------------+------+-----+---------+-------+
| id    | int(11)    | YES  |     | NULL    |       |
| name  | varchar(5) | YES  | UNI | NULL    |       |
+-------+------------+------+-----+---------+-------+
2 rows in set (0.00 sec)

联合字段创建唯一约束

mysql> create table t10(
    -> id int,
    -> host varchar(15) not null,
    -> port int not null,
    -> unique(host, port));
Query OK, 0 rows affected (0.03 sec)

mysql> insert into t10 values
    -> (1, '127.0.0.1', 3306);
Query OK, 1 row affected (0.01 sec)

mysql> insert into t10 values (1, '127.0.0.1', 3316);
Query OK, 1 row affected (0.00 sec)

mysql> insert into t10 values (1, '127.0.1.1', 3316);
Query OK, 1 row affected (0.00 sec)

# 两个字段全部都重复才会报错,只有按照两个字段联合查询时,才会加速查询
mysql> insert into t10 values (1, '127.0.0.1', 3306);
ERROR 1062 (23000): Duplicate entry '127.0.0.1-3306' for key 'host'

primary key

从约束角度看primary key字段的值不为空且唯一,那我们直接使用not null+unique不就可以了吗,要它干什么?

主键primary key是innodb存储引擎组织数据的依据,innodb称之为索引组织表,一张表中必须有且只有一个主键。

一个表中可以:

单列做主键
    多列做主键(复合主键或者叫做联合主键)

通俗解释:
unique key和primary key都是MySQL的特殊类型,不仅仅是个字段约束条件,还称为索引,可以加快查询速度,这个索引功能我们后面再讲,现在只讲一下这些key作为约束条件的效果。

    关于主键的强调内容:
        1.一张表中必须有,并且只能由一个主键字段:innodb引擎下存储表数据的时候,会通过你的主键字段的数据来组织管理所有的数据,将数据做成一种树形结构的数据结构,帮你较少IO次数,提高获取定位数据、获取数据的速度,优化查询。
            解释:如果我们在一张表中没有设置primary key,那么mysql在创建表的时候,会按照顺序从上到下遍历你设置的字段,直到找到一个not null unique的字段,自动识别成主键pri,通过desc可以看到,这样是不是不好啊,所以我们在创建表的时候,要给他一个主键,让他优化的时候用,如果没有pri也没有not null unique字段,那么innodb引擎下的mysql被逼无奈,你没有设置主键字段,主键又有不为空且唯一的约束,又不能擅自给你的字段加上这些约束,那么没办法,它只能给你添加一个隐藏字段来帮你组织数据,如果是这样,你想想,主键是不是帮我们做优化查询用的啊,这个优化是我们可以通过主键来查询数据:例如:如果我们将id设置为主键,当我们查一个id为30的数据的时候,也就是select * from tb1 where id=30;这个查询语句的速度非常快,不需要遍历前面三十条数据,就好像我们使用的字典似的,找一个字,不需要一页一页的翻书,可以首先看目录,然后看在哪一节,然后看在哪一页,一步步的范围,然后很快就找到了,这就像我们说的mysql的索引(主键、唯一键)的工作方式,一步一步的缩小范围来查找,几步就搞定了,所以通过主键你能够快速的查询到你所需要的数据,所以,如果你的主键是mysql帮你加的隐藏的字段,你查询数据的时候,就不能将这个隐藏字段作为条件来查询数据了,就不能享受到优化后的查询速度了,对么
        
        2.一张表里面,通常都应该有一个id字段,而且通常把这个id字段作为主键,当然你非要让其他的字段作为主键也是可以的,看你自己的设计,创建表的时候,一般都会写create table t1(id int primary key);id int primary key这个东西在建表的时候直接就写上

核心解释:

unique key和primary key都是MySQL的特殊类型,不仅仅是个字段约束条件,还称为索引,可以加快查询速度,这个索引功能我们后面再讲,现在只讲一下这些key作为约束条件的效果。
1. 一张表中有且只有一个主键字段。组织数据结构,减少IO次数,提高定位数据,优化查询。
2. 一般我们会将id字典设置为主键字段。

测试:

# 没有设置主键,MySQL会将not null and unique的字段当成主键字段
mysql> create table t11(
    -> id int not null unique,
    -> name char(16));
Query OK, 0 rows affected (0.03 sec)

mysql> desc t11;
+-------+----------+------+-----+---------+-------+
| Field | Type     | Null | Key | Default | Extra |
+-------+----------+------+-----+---------+-------+
| id    | int(11)  | NO   | PRI | NULL    |       |
| name  | char(16) | YES  |     | NULL    |       |
+-------+----------+------+-----+---------+-------+
2 rows in set (0.00 sec)

# 创建主键的方式一
mysql> create table t12( id int primary key, name char(16));
Query OK, 0 rows affected (0.02 sec)

mysql> desc t12;
+-------+----------+------+-----+---------+-------+
| Field | Type     | Null | Key | Default | Extra |
+-------+----------+------+-----+---------+-------+
| id    | int(11)  | NO   | PRI | NULL    |       |
| name  | char(16) | YES  |     | NULL    |       |
+-------+----------+------+-----+---------+-------+
2 rows in set (0.00 sec)

# 创建主键的方式二
mysql> create table t13(
    -> id int,
    -> name char(5),
    -> constraint pkname primary key(id));
Query OK, 0 rows affected (0.03 sec)

mysql> desc t13;
+-------+---------+------+-----+---------+-------+
| Field | Type    | Null | Key | Default | Extra |
+-------+---------+------+-----+---------+-------+
| id    | int(11) | NO   | PRI | NULL    |       |
| name  | char(5) | YES  |     | NULL    |       |
+-------+---------+------+-----+---------+-------+
2 rows in set (0.00 sec)

# 联合主键

mysql> create table t14(
    -> id int,
    -> host char(15),
    -> port int,
    -> primary key(host,port));
Query OK, 0 rows affected (0.03 sec)

mysql> desc t14;
# 当一张表中有多个字段都是PRI key时,一定是联合主键。
+-------+----------+------+-----+---------+-------+
| Field | Type     | Null | Key | Default | Extra |
+-------+----------+------+-----+---------+-------+
| id    | int(11)  | YES  |     | NULL    |       |
| host  | char(15) | NO   | PRI | NULL    |       |
| port  | int(11)  | NO   | PRI | NULL    |       |
+-------+----------+------+-----+---------+-------+
3 rows in set (0.01 sec)

auto_increment

之前我们插入数据的时候,id也需要自己来写,是不是很麻烦啊,我们是不是想,只要有一条记录就直接插入进去啊,不需要考虑说,你现在存储到第多少条数据了,对不对,所以出现了一个叫做auto_increment的属性

约束字段为自动增长,被约束的字段必须同时被key约束,也就是说只能给约束成key的字段加自增属性,默认起始位置为1,步长也为1.

这个约束条件一般用于id字段。

测试:

mysql> create table t15(
    -> id int primary key auto_increment,
    -> name char(15));
Query OK, 0 rows affected (0.04 sec)

mysql> insert into t15 values
    -> (1, 'barry');
Query OK, 1 row affected (0.00 sec)

mysql> select * from t15;
+----+-------+
| id | name  |
+----+-------+
|  1 | barry |
+----+-------+
1 row in set (0.00 sec)

#按照之前的id进行自增
mysql> insert into t15(name) values ( '虫子');
Query OK, 1 row affected (0.00 sec)

mysql> select * from t15;
+----+--------+
| id | name   |
+----+--------+
|  1 | barry  |
|  2 | 虫子   |
+----+--------+
2 rows in set (0.00 sec)
# 可以指定id插入数据
mysql> insert into t15 values (10, '大雨');
Query OK, 1 row affected (0.00 sec)

# 按照上一个id进行自增
mysql> insert into t15(name) values ( '小雨');
Query OK, 1 row affected (0.00 sec)

mysql> select * from t15;
+----+--------+
| id | name   |
+----+--------+
|  1 | barry  |
|  2 | 虫子   |
| 10 | 大雨   |
| 11 | 小雨   |
+----+--------+
4 rows in set (0.00 sec)

# 删除最后一条数据
mysql> delete from t15 where id=11;
Query OK, 1 row affected (0.01 sec)

# id会按照删除的那条数据的下一个数字自增
mysql> insert into t15(name) values ('嘉豪');
Query OK, 1 row affected (0.00 sec)

mysql> select * from t15;
+----+--------+
| id | name   |
+----+--------+
|  1 | barry  |
|  2 | 虫子   |
| 10 | 大雨   |
| 12 | 嘉豪   |
+----+--------+
4 rows in set (0.00 sec)


# 清空表记录
# 使用delete方法清空表,原id还保存
mysql> delete from t15;
Query OK, 4 rows affected (0.00 sec)

mysql> select * from t15;
Empty set (0.00 sec)

mysql> insert into t15(name) values
    -> ('兴涛');
Query OK, 1 row affected (0.00 sec)

mysql> select * from t15;
+----+--------+
| id | name   |
+----+--------+
| 13 | 兴涛   |
+----+--------+
1 row in set (0.00 sec)

# trancate清空表,原id不保存
mysql> insert into t15(name) values
    -> ('菜籽'),
    -> ('明天'),
    -> ('高宁');
Query OK, 3 rows affected (0.00 sec)
Records: 3  Duplicates: 0  Warnings: 0

mysql> select * from t15;
+----+--------+
| id | name   |
+----+--------+
| 13 | 兴涛   |
| 14 | 菜籽   |
| 15 | 明天   |
| 16 | 高宁   |
+----+--------+
4 rows in set (0.00 sec)

mysql> truncate t15;
Query OK, 0 rows affected (0.01 sec)

mysql> select * from t15;
Empty set (0.00 sec)

mysql> insert into t15(name) values ('菜籽'), ('明天'), ('高宁');
Query OK, 3 rows affected (0.00 sec)
Records: 3  Duplicates: 0  Warnings: 0

mysql> select * from t15;
+----+--------+
| id | name   |
+----+--------+
|  1 | 菜籽   |
|  2 | 明天   |
|  3 | 高宁   |
+----+--------+
3 rows in set (0.00 sec)

foreign key

表与表的关系

外键,这个字段是通过另一张表的某个字段来约束本表的某个字段。体现表与表之间的关系的键。

快速理解foreign key(外键其实就是标明表和表之间的关系,表和表之间如果有关系的话就三种:一对一,多对一,多对多,我们挨个看看~)

员工信息表有三个字段:工号 姓名 部门

公司有3个部门,但是有1个亿的员工,那意味着部门这个字段需要重复存储,部门名字越长,越浪费

那这就体现出来了三个缺点:

  1. 表的组织结构不清晰:员工的信息、部门的信息等等都掺在一张表里面。
  2. 浪费空间,每一条信息都包含员工和部门,多个员工从属一个部门,也需要每个员工的信息里都包含着部门的信息,浪费硬盘空间。
  3. 扩展性极差:如果想修改一个部门的信息,比如修改部门名称,那么这个包含员工和部门信息的表中的所有的包含这个部门信息的数据都需要进行修改,那么修改起来就非常麻烦,这是非常致命的缺点。

解决方法:(画一个excel表格来表示一下效果~~)

我们完全可以定义一个部门表,解耦和

我们虽然将部门表提出来了,但是员工表本身是和部门有联系的,你光把部门信息提出来还是不够的,还需要建立关联

然后让员工信息表关联该表,如何关联,即foreign key 

如何判断表与表的关系:

如果:左表的多条记录可以对应右表的一条记录,foreign key 设置在左边,这个就是左表对右表的多对一。
如果:右表的多条记录可以对应左表的一条记录,foreign key 设置在右边,这个就是右表对左表的多对一。

# 多对一
单向的多对一,就是多对一。
# 多对多
双向的多对一,就是多对多。
# 一对一
左表的一条记录唯一对应右表的一条记录,左表的某个字段设置foreign key + unique.

多对一

我们以上述的表为例,举例说明研究。

employee员工表,关联表。

department部门表:被关联的表。

  • 先创建哪张表?关联表,还是被关联表。

    一定是先创建被关联表,在创建关联表。

    mysql> create table emp(
    id int primary key auto_increment,
    name char(5),
    gender enum('male', 'female') not null default 'male',
    dep_id int,
    foreign key(dep_id) references dep(id));
        
    #因为你还没有建立dep表,所以报错。
    ERROR 1215 (HY000): Cannot add foreign key constraint
    
    # 先建dep表,在建emp表。
    create table dep(
     id int primary key auto_increment,
    dep_name char(10),
    dep_comment varchar(25));
    Query OK, 0 rows affected (0.02 sec)
    
    mysql> create table emp(
        -> id int primary key auto_increment,
        -> name char(5),
        -> gender enum('male', 'female') not null default 'male',
        -> dep_id int,
        -> foreign key(dep_id) references dep(id));
    Query OK, 0 rows affected (0.03 sec)
    
    mysql> desc dep;
    +-------------+-------------+------+-----+---------+----------------+
    | Field       | Type        | Null | Key | Default | Extra          |
    +-------------+-------------+------+-----+---------+----------------+
    | id          | int(11)     | NO   | PRI | NULL    | auto_increment |
    | dep_name    | char(10)    | YES  |     | NULL    |                |
    | dep_comment | varchar(25) | YES  |     | NULL    |                |
    +-------------+-------------+------+-----+---------+----------------+
    3 rows in set (0.00 sec)
    
    mysql> desc emp;
    +--------+-----------------------+------+-----+---------+----------------+
    | Field  | Type                  | Null | Key | Default | Extra          |
    +--------+-----------------------+------+-----+---------+----------------+
    | id     | int(11)               | NO   | PRI | NULL    | auto_increment |
    | name   | char(5)               | YES  |     | NULL    |                |
    | gender | enum('male','female') | NO   |     | male    |                |
    | dep_id | int(11)               | YES  | MUL | NULL    |                |
    +--------+-----------------------+------+-----+---------+----------------+
    4 rows in set (0.00 sec)
    
    
  • 插入数据,先给dep表插入数据,再给emp表插入数据。

    insert into dep(dep_name, dep_comment) values
    ('教学部', '日常教学,讲课等'),
    ('开发部', '开发项目,测试项目'),
    ('人事部', '招人,画饼,处理办公事务');
    
    insert into emp(name, dep_id) values
    ('明航', 1),
    ('阿珍', 2),
    ('小蒙', 3),
    ('小丽', 3),
    ('老李', 1),
    ('新月', 2);
    
     mysql> select * from emp;
      +----+--------+--------+--------+
      | id | name   | gender | dep_id |
      +----+--------+--------+--------+
      |  1 | 明航   | male   |      1 |
      |  2 | 阿珍   | male   |      2 |
      |  3 | 小蒙   | male   |      3 |
      |  4 | 小丽   | male   |      3 |
      |  5 | 老李   | male   |      1 |
      |  6 | 新月   | male   |      2 |
      +----+--------+--------+--------+
      6 rows in set (0.00 sec)
    
      mysql> select * from dep;
      +----+-----------+--------------------------------------+
      | id | dep_name  | dep_comment                          |
      +----+-----------+--------------------------------------+
      |  1 | 教学部    | 日常教学,讲课等                     |
      |  2 | 开发部    | 开发项目,测试项目                   |
      |  3 | 人事部    | 招人,画饼,处理办公事务             |
      +----+-----------+--------------------------------------+
      3 rows in set (0.00 sec)
    
  • 如果我们删除这两张表的所有数据,先删除员工表,再删除部门表。

    # 错误演示
    mysql> truncate dep;
    ERROR 1701 (42000): Cannot truncate a table referenced in a foreign key constraint (`db1`.`emp`, CONSTRAINT `emp_ibfk_1` FOREIGN KEY (`dep_id`) REFERENCES `db1`.`dep` (`id`))
    
    # 先删除员工表,再删除部门表。(课下测试)
    
  • 我们修改部门表的id字段,可以么?目前不可以。

    # 将部门表的id=1的字段,改成id=100报错
    mysql> update dep set id=100 where id=1;
    ERROR 1451 (23000): Cannot delete or update a parent row: a foreign key constraint fails (`db1`.`emp`, CONSTRAINT `emp_ibfk_1` FOREIGN KEY (`dep_id`) REFERENCES `dep` (`id`))
    
  • 我们将部门表的id=2的字段删除,可以么?目前不可以。

    mysql> delete from dep where id=2;
    ERROR 1451 (23000): Cannot delete or update a parent row: a foreign key constraint fails (`db1`.`emp`, CONSTRAINT `emp_ibfk_1` FOREIGN KEY (`dep_id`) REFERENCES `dep` (`id`))
    
    # 如果我们硬要部门表id=2的部门删除,我们应该先将员工表dep_id=2全部删除,然后在删除部门表的对应的数据。
    mysql> delete from emp where dep_id=2;
    Query OK, 2 rows affected (0.00 sec)
    
    mysql> select * from emp;
    +----+--------+--------+--------+
    | id | name   | gender | dep_id |
    +----+--------+--------+--------+
    |  1 | 明航   | male   |      1 |
    |  3 | 小蒙   | male   |      3 |
    |  4 | 小丽   | male   |      3 |
    |  5 | 老李   | male   |      1 |
    +----+--------+--------+--------+
    4 rows in set (0.00 sec)
    
    mysql> delete from dep where id=2;
    Query OK, 1 row affected (0.00 sec)
    
    mysql> select * from dep;
    +----+-----------+--------------------------------------+
    | id | dep_name  | dep_comment                          |
    +----+-----------+--------------------------------------+
    |  1 | 教学部    | 日常教学,讲课等                     |
    |  3 | 人事部    | 招人,画饼,处理办公事务             |
    +----+-----------+--------------------------------------+
    2 rows in set (0.00 sec)
    
    

我们如果这样设立外键字段,对于被关联表的数据的修改或者删除,收到了很多限制,所以我们想要实现什么?当部门表的数据修改或者删除时,员工表的数据随之修改或者删除。

on update cascade

on delete cascade

删表,建表,重新测试:

# 删表
mysql> drop table emp,dep;
Query OK, 0 rows affected (0.00 sec)

mysql> show tables;
Empty set (0.00 sec)

# 重新建表
create table dep(
 id int primary key auto_increment,
dep_name char(10),
dep_comment varchar(25));


create table emp(
id int primary key auto_increment,
name char(5),
gender enum('male', 'female') not null default 'male',
dep_id int,
foreign key(dep_id) references dep(id)
on update cascade
on delete cascade
);

# 插入数据
insert into dep(dep_name, dep_comment) values
('教学部', '日常教学,讲课等'),
('开发部', '开发项目,测试项目'),
('人事部', '招人,画饼,处理办公事务');

insert into emp(name, dep_id) values
('明航', 1),
('阿珍', 2),
('小蒙', 3),
('小丽', 3),
('老李', 1),
('新月', 2);

mysql> select * from emp;
+----+--------+--------+--------+
| id | name   | gender | dep_id |
+----+--------+--------+--------+
|  1 | 明航   | male   |      1 |
|  2 | 阿珍   | male   |      2 |
|  3 | 小蒙   | male   |      3 |
|  4 | 小丽   | male   |      3 |
|  5 | 老李   | male   |      1 |
|  6 | 新月   | male   |      2 |
+----+--------+--------+--------+
6 rows in set (0.00 sec)

mysql> select * from dep;
+----+-----------+--------------------------------------+
| id | dep_name  | dep_comment                          |
+----+-----------+--------------------------------------+
|  1 | 教学部    | 日常教学,讲课等                     |
|  2 | 开发部    | 开发项目,测试项目                   |
|  3 | 人事部    | 招人,画饼,处理办公事务             |
+----+-----------+--------------------------------------+
3 rows in set (0.00 sec)

# 级联修改,修改部门表的id ,员工表的dep_id随之修改。
mysql> update dep set id=100 where id=1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> select * from emp;
+----+--------+--------+--------+
| id | name   | gender | dep_id |
+----+--------+--------+--------+
|  1 | 明航   | male   |    100 |
|  2 | 阿珍   | male   |      2 |
|  3 | 小蒙   | male   |      3 |
|  4 | 小丽   | male   |      3 |
|  5 | 老李   | male   |    100 |
|  6 | 新月   | male   |      2 |
+----+--------+--------+--------+
6 rows in set (0.00 sec)

# 不能从修改员工表的dep_id让部门表的id修改。
mysql> update emp set dep_id=200 where dep_id=2;
ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint fails (`db1`.`emp`, CONSTRAINT `emp_ibfk_1` FOREIGN KEY (`dep_id`) REFERENCES `dep` (`id`) ON DELETE CASCADE ON UPDATE CASCADE)

mysql> delete from dep where id=100;
Query OK, 1 row affected (0.00 sec)

mysql> select * from dep;
+----+-----------+--------------------------------------+
| id | dep_name  | dep_comment                          |
+----+-----------+--------------------------------------+
|  2 | 开发部    | 开发项目,测试项目                   |
|  3 | 人事部    | 招人,画饼,处理办公事务             |
+----+-----------+--------------------------------------+
2 rows in set (0.00 sec)

mysql> select * from emp;
+----+--------+--------+--------+
| id | name   | gender | dep_id |
+----+--------+--------+--------+
|  2 | 阿珍   | male   |      2 |
|  3 | 小蒙   | male   |      3 |
|  4 | 小丽   | male   |      3 |
|  6 | 新月   | male   |      2 |
+----+--------+--------+--------+
4 rows in set (0.00 sec)

我们总结一下foreign key的下面几个约束作用:

1、先要建立被关联的表才能建立关联表

2、在插入数据记录的时候,要先想被关联表中插入数据,才能往关联表里面插入数据

3、更新或者删除数据的时候,都需要考虑关联表和被关联表的关系

解决方案:重建表的时候,在加外键关联的时候加上这两句:on delete cascade 和 on update cascade

多对多

一本书可以有多个作者,一个作者可不可以写多本书,两者之间是不是站在谁的角度去看都是一个一对多的关系啊,那这就是多对多的关系,那我们创建表的时候,需要将两个表都加一个foreign key的字段,但是你添加字段的时候,你想想,能直接给两个表都这一个foreign key字段吗,两个谁先创建,谁后创建,是不是都不行啊,两个表的创建是不是都依赖着另外一张表啊,所以我们之前的加外键字段的方式对于这种多对多的关系是不是就不好用啦,怎么办,我们需要通过第三张表来缓和一下两者的关系,通过第三张表来创建双方的关系

我们先创建书表和作者表,然后创建第三张表,第三张表就需要有一个字段外键关联书表,还有一个字段外键关联作者表

  • 先创建被关联表 book,author,最后创建第三张表关联表 book_author表。

    create table book(
    id int primary key auto_increment,
    book_name varchar(20),
    price float(6,2));
    
    create table author(
    id int primary key auto_increment,
    author_name varchar(20));
    
    create table author_book(
    id int primary key auto_increment,
    book_id int,
    author_id int,
    foreign key(book_id) references book(id)
    on update cascade
    on delete cascade,
    foreign key(author_id) references author(id)
    on update cascade
    on delete cascade
    );
    
    # 课下插入数据,删除数据,更新数据测试。
    

一对一

我们来以咱们学校的学生来举例:

最开始你只是一个客户,可能还处于咨询考虑的阶段,还没有转化为学生,也有的客户已经转换为学生了,说白了就是你交钱了,哈哈

那我们来建两个表:客户表和学生表

create table customer(
id int primary key auto_increment,
name varchar(20) not null,
qq varchar(10) not null,
phone char(16) not null
);


create table student(
id int primary key auto_increment,
class_name varchar(20) not null,
customer_id int unique, #该字段一定要是唯一的
foreign key(customer_id) references customer(id) #外键的字段一定要保证unique
on delete cascade
on update cascade
);


5.表的操作

​ 下面的内容就不带着大家演示了,简单带大家看一下,都是固定的语法格式,按照这个写就行了,毫无逻辑可言,所以不做太多的演示,大家自己回去练一下:

语法:
1. 修改表名
      ALTER TABLE 表名 
                          RENAME 新表名;

2. 增加字段
      ALTER TABLE 表名
                          ADD 字段名  数据类型 [完整性约束条件…], #注意这里可以通过逗号来分割,一下添加多个约束条件
                          ADD 字段名  数据类型 [完整性约束条件…];
      ALTER TABLE 表名
                          ADD 字段名  数据类型 [完整性约束条件…]  FIRST; #添加这个字段的时候,把它放到第一个字段位置去。
      ALTER TABLE 表名
                          ADD 字段名  数据类型 [完整性约束条件…]  AFTER 字段名;#after是放到后的这个字段的后面去了,我们通过一个first和一个after就可以将新添加的字段放到表的任意字段位置了。
      
3. 删除字段
      ALTER TABLE 表名 
                          DROP 字段名;

4. 修改字段
      ALTER TABLE 表名 
                          MODIFY  字段名 数据类型 [完整性约束条件…];
      ALTER TABLE 表名 
                          CHANGE 旧字段名 新字段名 旧数据类型 [完整性约束条件…];  #change比modify还多了个改名字的功能,这一句是只改了一个字段名
                          
      ALTER TABLE 表名 
                          CHANGE 旧字段名 新字段名 新数据类型 [完整性约束条件…];#这一句除了改了字段名,还改了数据类型、完整性约束等等的内容
                          

给一个字段添加外键属性的语句:alter table 表2名 add foreign key(表2的一个字段) references 表1名(表1的一个字段);

	注意一点:在mysql里面表名是不区分大小写的,如果你将一个名为t1的(小写的t1)改名为一个T1(大写的T1),是完全没用的,因为在数据库里面表名都是小写的。

简单示例:

示例:
1. 修改存储引擎
mysql> alter table service 
    -> engine=innodb;

2. 添加字段
mysql> alter table student10
    -> add name varchar(20) not null,
    -> add age int(3) not null default 22;
   

mysql> alter table student10
    -> add stu_num varchar(10) not null after name;                //添加name字段之后

mysql> alter table student10                        
    -> add sex enum('male','female') default 'male' first;          //添加到最前面

3. 删除字段
mysql> alter table student10
    -> drop sex;

mysql> alter table service
    -> drop mac;

4. 修改字段类型modify
mysql> alter table student10
    -> modify age int(3);
mysql> alter table student10
    -> modify id int(11) not null primary key auto_increment;    //修改为主键

5. 增加约束(针对已有的主键增加auto_increment)
mysql> alter table student10 modify id int(11) not null primary key auto_increment;
ERROR 1068 (42000): Multiple primary key defined

mysql> alter table student10 modify id int(11) not null auto_increment;
Query OK, 0 rows affected (0.01 sec)
Records: 0  Duplicates: 0  Warnings: 0

6. 对已经存在的表增加复合主键
mysql> alter table service2
    -> add primary key(host_ip,port);        

7. 增加主键
mysql> alter table student1
    -> modify name varchar(10) not null primary key;

8. 增加主键和自动增长
mysql> alter table student1
    -> modify id int not null primary key auto_increment;

9. 删除主键
a. 删除自增约束
mysql> alter table student10 modify id int(11) not null; 

b. 删除主键
mysql> alter table student10                                 
    -> drop primary key;

表的相关操作:(自己补全语法)

  • 创建表

  • 查看表结构

  • 查看创建表时的语句

  • 删除表

  • 重命名表。

  • 增加字段

  • 删除字段

  • 修改字段

  • 重命名字段

  • 修改表的存储引擎

  • 对表的主键的增删改查

据类型 [完整性约束条件…] AFTER 字段名;#after是放到后的这个字段的后面去了,我们通过一个first和一个after就可以将新添加的字段放到表的任意字段位置了。

  1. 删除字段
    ALTER TABLE 表名
    DROP 字段名;

  2. 修改字段
    ALTER TABLE 表名
    MODIFY 字段名 数据类型 [完整性约束条件…];
    ALTER TABLE 表名
    CHANGE 旧字段名 新字段名 旧数据类型 [完整性约束条件…]; #change比modify还多了个改名字的功能,这一句是只改了一个字段名

    ALTER TABLE 表名
    CHANGE 旧字段名 新字段名 新数据类型 [完整性约束条件…];#这一句除了改了字段名,还改了数据类型、完整性约束等等的内容

给一个字段添加外键属性的语句:alter table 表2名 add foreign key(表2的一个字段) references 表1名(表1的一个字段);

注意一点:在mysql里面表名是不区分大小写的,如果你将一个名为t1的(小写的t1)改名为一个T1(大写的T1),是完全没用的,因为在数据库里面表名都是小写的。

简单示例:

示例:
1. 修改存储引擎
mysql> alter table service 
    -> engine=innodb;

2. 添加字段
mysql> alter table student10
    -> add name varchar(20) not null,
    -> add age int(3) not null default 22;
   

mysql> alter table student10
    -> add stu_num varchar(10) not null after name;                //添加name字段之后

mysql> alter table student10                        
    -> add sex enum('male','female') default 'male' first;          //添加到最前面

3. 删除字段
mysql> alter table student10
    -> drop sex;

mysql> alter table service
    -> drop mac;

4. 修改字段类型modify
mysql> alter table student10
    -> modify age int(3);
mysql> alter table student10
    -> modify id int(11) not null primary key auto_increment;    //修改为主键

5. 增加约束(针对已有的主键增加auto_increment)
mysql> alter table student10 modify id int(11) not null primary key auto_increment;
ERROR 1068 (42000): Multiple primary key defined

mysql> alter table student10 modify id int(11) not null auto_increment;
Query OK, 0 rows affected (0.01 sec)
Records: 0  Duplicates: 0  Warnings: 0

6. 对已经存在的表增加复合主键
mysql> alter table service2
    -> add primary key(host_ip,port);        

7. 增加主键
mysql> alter table student1
    -> modify name varchar(10) not null primary key;

8. 增加主键和自动增长
mysql> alter table student1
    -> modify id int not null primary key auto_increment;

9. 删除主键
a. 删除自增约束
mysql> alter table student10 modify id int(11) not null; 

b. 删除主键
mysql> alter table student10                                 
    -> drop primary key;

表的相关操作:(自己补全语法)

  • 创建表

  • 查看表结构

  • 查看创建表时的语句

  • 删除表

  • 重命名表。

  • 增加字段

  • 删除字段

  • 修改字段

  • 重命名字段

  • 修改表的存储引擎

  • 对表的主键的增删改查

    2.MySQL行记录的操作

2.1 插入数据 insert

语法

1. 插入完整数据(顺序插入)
    语法一:
    INSERT INTO 表名(字段1,字段2,字段3…字段n) VALUES(值1,值2,值3…值n); #指定字段来插入数据,插入的值要和你前面的字段相匹配

    语法二:
    INSERT INTO 表名 VALUES (值1,值2,值3…值n); #不指定字段的话,就按照默认的几个字段来插入数据

2. 指定字段插入数据
    语法:
    INSERT INTO 表名(字段1,字段2,字段3…) VALUES (值1,值2,值3…);

3. 插入多条记录
    语法:#插入多条记录用逗号来分隔
    INSERT INTO 表名 VALUES
        (值1,值2,值3…值n),
        (值1,值2,值3…值n),
        (值1,值2,值3…值n);
        
4. 插入查询结果
    语法:
    INSERT INTO 表名(字段1,字段2,字段3…字段n) 
                    SELECT (字段1,字段2,字段3…字段n) FROM 表2
                    WHERE …; #将从表2里面查询出来的结果来插入到我们的表中,但是注意查询出来的数据要和我们前面指定的字段要对应好

测试:

mysql> insert into dep(id,dep_name,dep_comment) values(4, '后勤部', '检查宿舍'); s
Query OK, 1 row affected (0.00 sec)

mysql> insert into dep values(5, '安保部', '维护秩序');
Query OK, 1 row affected (0.00 sec)
# 不指定字段,必须按照所有的字段全部添加,包含id字段
mysql> insert into dep values('安保部1', '维护秩序');
ERROR 1136 (21S01): Column count doesn't match value count at row 1

mysql> insert into dep(dep_name, dep_comment) values('安保部1', '维护秩序');
Query OK, 1 row affected (0.00 sec)

mysql> insert into dep(dep_name, dep_comment) values
    -> ('销售部', '卖课'),
    -> ('法务部', '卖课'),
    -> ('财务部', '结缘开溜');
Query OK, 3 rows affected (0.00 sec)
Records: 3  Duplicates: 0  Warnings: 0

2.2 更新数据 update

语法

	UPDATE 表名 SET 
        字段1=值1,  #注意语法,可以同时来修改多个值,用逗号分隔
        字段2=值2
        WHERE CONDITION; #更改哪些数据,通过where条件来定位到符合条件的数据

测试:

mysql> update dep set dep_name='公关部', dep_comment='业务公关' where id=6;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> select * from dep;                                                       +----+-----------+--------------------------------------+
| id | dep_name  | dep_comment                          |
+----+-----------+--------------------------------------+
|  2 | 开发部    | 开发项目,测试项目                   |
|  3 | 人事部    | 招人,画饼,处理办公事务             |
|  4 | 后勤部    | 检查宿舍                             |
|  5 | 安保部    | 维护秩序                             |
|  6 | 公关部    | 业务公关                             |
|  7 | 销售部    | 卖课                                 |
|  8 | 法务部    | 卖课                                 |
|  9 | 财务部    | 结缘开溜                             |
+----+-----------+--------------------------------------+
8 rows in set (0.00 sec)

2.3 删除数据delete

语法:
    DELETE FROM 表名 
        WHERE CONITION; #删除符合条件的一些记录
    DELETE FROM 表名;如果不加where条件,意思是将表里面所有的内容都删掉,但是清空所有的内容,一般
   
我们用truncate ,能够将id置为零,delete不能将id置零,再插入数据的时候,会按照之前的数据记录的id数继续递增

测试:

mysql> delete from dep where id=8;
Query OK, 1 row affected (0.00 sec)

2.4 查询数据 select(单表)

​ 我们在工作中,多数的场景都是对数据的增删改操作少,读数据的操作多,所以我们的重点就在读取数据这里了。

之前我们说,我们是不是将数据分到多个表中进行保存,而不是将所有的数据都放到一个表里面,例如我们前面所说的部门表和员工表,员工的信息放到一个表里面,部门的信息放到一个部门表里面。

虽然我们把数据分到了不同的表里面,但是这些数据是不是属于我们同一个项目的,也就是说,你将来查询数据的时候,可能不单单的从一张表里面来查询数据,可能涉及到一下查询出来多个表中的数据,也就是多表关联查询,对不对,那么我们先来把单表查询学习一下,再来进行多表查询的学习。

单表查询简单语法

#查询数据的本质:mysql会到你本地的硬盘上找到对应的文件,然后打开文件,按照你的查询条件来找出你需要的数据。下面是完整的一个单表查询的语法

select * from,这个select * 指的是要查询所有字段的数据。

SELECT distinct 字段1,字段2... FROM 库名.表名 #from后面是说从库的某个表中去找数据,mysql会去找到这个库对应的文件夹下去找到你表名对应的那个数据文件,找不到就直接报错了,找到了就继续后面的操作
                  WHERE 条件       #从表中找符合条件的数据记录,where后面跟的是你的查询条件
                  GROUP BY field(字段)   #分组
                  HAVING 筛选      #过滤,过滤之后执行select后面的字段筛选,就是说我要确定一下需要哪个字段的数据,你查询的字段数据进行去重,然后在进行下面的操作
                  ORDER BY field(字段)   #将结果按照后面的字段进行排序
                  LIMIT 限制条数    #将最后的结果加一个限制条数,就是说我要过滤或者说限制查询出来的数据记录的条数

关于上面这些内容,我们在下面一个一个的来详细解释


关键字的执行优先级(重点)

   1.找到表:from

  2.拿着where指定的约束条件,去文件/表中取出一条条记录

  3.将取出的一条条记录进行分组group by,如果没有group by,则整体作为一组

  4.将分组的结果进行having过滤

  5.执行select

  6.去重

  7.将结果按条件排序:order by

  8.限制结果的查询条数

准备表,插入一定量的数据,用于我们接下来的查询:

#我们来创建一个员工表,然后对员工表进行一个简单的查询,来看一下效果,下面是员工表的字段
company.employee
    员工id      id                  int             
    姓名        emp_name            varchar
    性别        sex                 enum
    年龄        age                 int
    入职日期     hire_date           date
    岗位        post                varchar
    岗位描述     post_comment        varchar
    薪水        salary              double
    办公室       office              int
    部门编号     depart_id           int



#创建表
create table employee(
    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
);


#查看表结构
mysql> desc employee;
+--------------+-----------------------+------+-----+---------+----------------+
| Field        | Type                  | Null | Key | Default | Extra          |
+--------------+-----------------------+------+-----+---------+----------------+
| id           | int(11)               | NO   | PRI | NULL    | auto_increment |
| name         | varchar(20)           | NO   |     | NULL    |                |
| sex          | enum('male','female') | NO   |     | male    |                |
| age          | int(3) unsigned       | NO   |     | 28      |                |
| hire_date    | date                  | NO   |     | NULL    |                |
| post         | varchar(50)           | YES  |     | NULL    |                |
| post_comment | varchar(100)          | YES  |     | NULL    |                |
| salary       | double(15,2)          | YES  |     | NULL    |                |
| office       | int(11)               | YES  |     | NULL    |                |
| depart_id    | int(11)               | YES  |     | NULL    |                |
+--------------+-----------------------+------+-----+---------+----------------+

#插入记录
#三个部门:教学,销售,运营
insert into employee(name,sex,age,hire_date,post,salary,office,depart_id) values
('太白','male',18,'20170301','CEO',7300.33,401,1), #以下是教学部,全都是老师
('alex','male',78,'20150302','teacher',1000000.31,401,1),
('wupeiqi','male',81,'20130305','teacher',8300,401,1),
('yuanhao','male',73,'20140701','teacher',3500,401,1),
('liwenzhou','male',28,'20121101','teacher',2100,401,1),
('jingliyang','female',18,'20110211','teacher',9000,401,1),
('元宝','male',18,'19000301','teacher',30000,401,1),
('成龙','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);

#ps:如果在windows系统中,插入中文字符,select的结果为空白,可以将所有字符编码统一设置成gbk

简单查询

工作中一般不用*,使用*效率低,一般我们都会指定字段进行查询。
mysql> select name,sex,post from employee;
+------------+--------+-----------+
| name       | sex    | post      |
+------------+--------+-----------+
| 太白       | male   | CEO       |
| alex       | male   | teacher   |
| wupeiqi    | male   | teacher   |
| yuanhao    | male   | teacher   |
| liwenzhou  | male   | teacher   |
| jingliyang | female | teacher   |

Distinct去重

mysql> select post from employee;
+-----------+
| post      |
+-----------+
| CEO       |
| teacher   |
| teacher   |
| teacher   |
| teacher   |
| teacher   |
| teacher   |
| teacher   |
| sale      |
| sale      |
| sale      |
| sale      |
| sale      |
| operation |
| operation |
| operation |
| operation |
| operation |
+-----------+
18 rows in set (0.00 sec)

# 我们只想知道岗位名,将重复的去掉
mysql> select distinct post from employee;
+-----------+
| post      |
+-----------+
| CEO       |
| teacher   |
| sale      |
| operation |
+-----------+
4 rows in set (0.00 sec)

# 使用distinct对多个字段进行去重,只能是这些字段联合起来一样的才可以去重
mysql> select distinct post,sex from employee;
+-----------+--------+
| post      | sex    |
+-----------+--------+
| CEO       | male   |
| teacher   | male   |
| teacher   | female |
| sale      | female |
| operation | male   |
| operation | female |
+-----------+--------+
6 rows in set (0.00 sec)

mysql> select name,distinct post from employee;
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'distinct post from employee' at line 1

四则运算查询

可以对字段进行+ - * / % 等计算

mysql> select name,salary*12 from employee;
+------------+-------------+
| name       | salary*12   |
+------------+-------------+
| 太白       |    87603.96 |
| alex       | 12000003.72 |
| wupeiqi    |    99600.00 |
| yuanhao    |    42000.00 |
| liwenzhou  |    25200.00 |
| jingliyang |   108000.00 |
| 元宝       |   360000.00 |
| 成龙       |   120000.00 |
| 歪歪       |    36001.56 |
| 丫丫       |    24004.20 |
| 丁丁       |    12004.44 |
| 星星       |    36003.48 |
| 格格       |    48003.96 |
| 张野       |   120001.56 |
| 程咬金     |   240000.00 |
| 程咬银     |   228000.00 |
| 程咬铜     |   216000.00 |
| 程咬铁     |   204000.00 |
+------------+-------------+
18 rows in set (0.00 sec)

给字段起别名as

mysql> select name,salary*12 as annual_salary from employee;
+------------+---------------+
| name       | annual_salary |
+------------+---------------+
| 太白       |      87603.96 |
| alex       |   12000003.72 |
| wupeiqi    |      99600.00 |
| yuanhao    |      42000.00 |
| liwenzhou  |      25200.00 |
| jingliyang |     108000.00 |
| 元宝       |     360000.00 |
| 成龙       |     120000.00 |

自定制显示格式(聚合函数)concat concat_ws

类似于python中的字符串拼接。

mysql> select concat('姓名:' ,name, '年薪:',salary*12) as annual_salary from employee;
+-------------------------------------+
| annual_salary                       |
+-------------------------------------+
| 姓名:太白年薪:87603.96             |
| 姓名:alex年薪:12000003.72          |
| 姓名:wupeiqi年薪:99600.00          |
| 姓名:yuanhao年薪:42000.00          |
| 姓名:liwenzhou年薪:25200.00        |
| 姓名:jingliyang年薪:108000.00      |
| 姓名:元宝年薪:360000.00            |
| 姓名:成龙年薪:120000.00            |
| 姓名:歪歪年薪:36001.56             |
| 姓名:丫丫年薪:24004.20             |
| 姓名:丁丁年薪:12004.44             |
| 姓名:星星年薪:36003.48             |
| 姓名:格格年薪:48003.96             |
| 姓名:张野年薪:120001.56            |
| 姓名:程咬金年薪:240000.00          |
| 姓名:程咬银年薪:228000.00          |
| 姓名:程咬铜年薪:216000.00          |
| 姓名:程咬铁年薪:204000.00          |
+-------------------------------------+
18 rows in set (0.00 sec)

concat_ws 也是用于字符串拼接,只不过写法与concat略有不同
mysql> select concat_ws('--->', name, salary*12) as annual_salary from employee;
+-------------------------+
| annual_salary           |
+-------------------------+
| 太白--->87603.96        |
| alex--->12000003.72     |
| wupeiqi--->99600.00     |
| yuanhao--->42000.00     |
| liwenzhou--->25200.00   |
| jingliyang--->108000.00 |
| 元宝--->360000.00       |
| 成龙--->120000.00       |
| 歪歪--->36001.56        |
| 丫丫--->24004.20        |
| 丁丁--->12004.44        |
| 星星--->36003.48        |
| 格格--->48003.96        |
| 张野--->120001.56       |
| 程咬金--->240000.00     |
| 程咬银--->228000.00     |
| 程咬铜--->216000.00     |
| 程咬铁--->204000.00     |
+-------------------------+
18 rows in set (0.00 sec)

mysql> select concat_ws('--->', name, salary*12,age) as annual_salary from employee;
+-------------------------------+
| annual_salary                 |
+-------------------------------+
| 太白--->87603.96--->18        |
| alex--->12000003.72--->78     |
| wupeiqi--->99600.00--->81     |
| yuanhao--->42000.00--->73     |
| liwenzhou--->25200.00--->28   |
| jingliyang--->108000.00--->18 |
| 元宝--->360000.00--->18       |
| 成龙--->120000.00--->48       |
| 歪歪--->36001.56--->48        |
| 丫丫--->24004.20--->38        |
| 丁丁--->12004.44--->18        |
| 星星--->36003.48--->18        |
| 格格--->48003.96--->28        |
| 张野--->120001.56--->28       |
| 程咬金--->240000.00--->18     |
| 程咬银--->228000.00--->18     |
| 程咬铜--->216000.00--->18     |
| 程咬铁--->204000.00--->18     |
+-------------------------------+

where 条件约束

我们在查询数据时,有时是需要按照条件进行查询的。

​ where语句中可以使用:

之前我们用where 后面跟的语句是不是id=1这种类型的啊,用=号连接的,除了=号外,还能使用其他的,看下面:

  1. 比较运算符:> < >= <= <> !=

  2. between 80 and 100 值在80到100之间

  3. in (80,90,100) 值是80或90或100

  4. like '太白%'模糊匹配

         pattern可以是%或_,
             %表示任意多字符
             _表示一个字符 
    
    
  5. 逻辑运算符:在多个条件直接可以使用逻辑运算符 and or not

测试:

单条件查询
mysql> mysql> select id,name,sex,age from employee where sex='male';
+----+-----------+------+-----+
| id | name      | sex  | age |
+----+-----------+------+-----+
|  1 | 太白      | male |  18 |
|  2 | alex      | male |  78 |
|  3 | wupeiqi   | male |  81 |
|  4 | yuanhao   | male |  73 |
|  5 | liwenzhou | male |  28 |
|  7 | 元宝      | male |  18 |
|  8 | 成龙      | male |  48 |
| 14 | 张野      | male |  28 |
| 15 | 程咬金    | male |  18 |
| 17 | 程咬铜    | male |  18 |
+----+-----------+------+-----+

多条件查询
mysql> select id,name,sex,age from employee where sex='female' or age <= 22;
+----+------------+--------+-----+
| id | name       | sex    | age |
+----+------------+--------+-----+
|  1 | 太白       | male   |  18 |
|  6 | jingliyang | female |  18 |
|  7 | 元宝       | male   |  18 |
|  9 | 歪歪       | female |  48 |
| 10 | 丫丫       | female |  38 |
| 11 | 丁丁       | female |  18 |
| 12 | 星星       | female |  18 |
| 13 | 格格       | female |  28 |
| 15 | 程咬金     | male   |  18 |
| 16 | 程咬银     | female |  18 |
| 17 | 程咬铜     | male   |  18 |
| 18 | 程咬铁     | female |  18 |
+----+------------+--------+-----+
12 rows in set (0.00 sec)

mysql> select id,name,sex,age from employee where sex='female' and age <= 22;
+----+------------+--------+-----+
| id | name       | sex    | age |
+----+------------+--------+-----+
|  6 | jingliyang | female |  18 |
| 11 | 丁丁       | female |  18 |
| 12 | 星星       | female |  18 |
| 16 | 程咬银     | female |  18 |
| 18 | 程咬铁     | female |  18 |
+----+------------+--------+-----+
5 rows in set (0.01 sec)

# between and
mysql> select id,name,sex,salary from employee where salary between 5000 and 10000;
+----+------------+--------+----------+
| id | name       | sex    | salary   |
+----+------------+--------+----------+
|  1 | 太白       | male   |  7300.33 |
|  3 | wupeiqi    | male   |  8300.00 |
|  6 | jingliyang | female |  9000.00 |
|  8 | 成龙       | male   | 10000.00 |
+----+------------+--------+----------+
4 rows in set (0.00 sec)

# is null / is not null
mysql> select id,name from employee where post_comment is null;
+----+------------+
| id | name       |
+----+------------+
|  1 | 太白       |
|  2 | alex       |
|  3 | wupeiqi    |
|  4 | yuanhao    |
|  5 | liwenzhou  |
|  6 | jingliyang |
|  7 | 元宝       |
.....

# in 查询
mysql> select id,name,age from employee where age in (18,28,30);
+----+------------+-----+
| id | name       | age |
+----+------------+-----+
|  1 | 太白       |  18 |
|  5 | liwenzhou  |  28 |
|  6 | jingliyang |  18 |
|  7 | 元宝       |  18 |
| 11 | 丁丁       |  18 |
| 12 | 星星       |  18 |
......

# like  % 零个或者多个任意字符 _ 任意一个字符
# 匹配名字中含有程的所有的员工信息。
mysql> select name,age from employee where name like '%程%';
+-----------+-----+
| name      | age |
+-----------+-----+
| 程咬金    |  18 |
| 程咬银    |  18 |
| 程咬铜    |  18 |
| 程咬铁    |  18 |
+-----------+-----+
4 rows in set (0.00 sec)
# 匹配名字为两个字的所有员工信息
mysql> select name,age from employee where name like '__';
+--------+-----+
| name   | age |
+--------+-----+
| 太白   |  18 |
| 元宝   |  18 |
| 成龙   |  48 |
| 歪歪   |  48 |
| 丫丫   |  38 |
| 丁丁   |  18 |
| 星星   |  18 |
| 格格   |  28 |
| 张野   |  28 |
+--------+-----+
9 rows in set (0.00 sec)

# 查询 姓名含有a或者含有y 并且 年龄在20以下或者30以上。
select id,name,sex,age from employee where (name like '%w%' or name like '%y%') and (age<=17 or age>=30);
+----+---------+------+-----+
| id | name    | sex  | age |
+----+---------+------+-----+
|  3 | wupeiqi | male |  81 |
|  4 | yuanhao | male |  73 |
+----+---------+------+-----+
3 rows in set (0.00 sec)

where条件咱们就说完了,这个where条件到底怎么运作的,我们来说一下:我们以select id,name,age from employee where id>7;这个语句来说一下

先执行from找到表,然后在从我们的表中,一条一条的按照where条件进行筛选,筛选出满足条件的一条记录,交给select筛选具体的字段,然后加载到临时的结果表中,where在按照条件进行下一次筛选。

group by 查询

分组查询,我们查询时为什么要进行分组?

#1、首先明确一点:分组发生在where之后,即分组是基于where之后得到的记录而进行的

#2、分组指的是:将所有记录按照某个相同字段进行归类,比如针对员工信息表的职位分组,或者按照性别进行分组等

#3、为何要分组呢?是因为我们有时候会需要以组为单位来统计一些数据或者进行一些计算的,对不对,比方说下面的几个例子
    取每个部门的最高工资  
    取每个部门的员工数
    取男人数和女人数  

    小窍门:‘每’这个字后面的字段,就是我们分组的依据,只是个小窍门,但是不能表示所有的情况,看上面第三个分组,没有'每'字,这个就需要我们通过语句来自行判断分组依据了
    我们能用id进行分组吗,能,但是id是不是重复度很低啊,基本没有重复啊,对不对,这样的字段适合做分组的依据吗?不适合,对不对,依据性别分组行不行,当然行,因为性别我们知道,是不是就两种啊,也可能有三种是吧,这个重复度很高,对不对,分组来查的时候才有更好的意义
  • 分组在where执行之后,在执行。
  • 分组的字段重复度要高。
  • 我们对数据进行分组,分完组之后,一般都会使用聚合函数进行组后的数据处理。
  • 如果我们对数据进行分组,那么select选取的字段只能是分组的字段或者通过聚合函数构成的字段。

聚合函数

max min avg sum count  concat concat_ws
#强调:聚合函数聚合的是组的内容,若是没有分组,则默认一组

示例:
# 计算表中的所有的员工的总数。
select count(id) from employee;
# 计算所有员工的最高薪资
select max(salary) from employee;
# 计算所有员工的最低的年龄
select min(age) from employee;
# 计算所有员工的平均薪资
select avg(salary) from employee;
# 计算所有员工的薪资综合。
select sum(salary) from employee;

分组测试:

# 获取每个部门的平均薪资以及部门名称。
select post, avg(salary) from employee group by post;

# 获取男性与女性的人数。
select sex,count(id) from employee group by sex;

# 获取年龄在30及以下的男性与女性的人数。
select sex,count(id) from employee where age <=30 group by sex;

# 获取年龄在20~30岁的各个部门的最高薪资。
select post,max(salary) from employee where age between 20 and 30 group by post;

# concat也是聚合函数,但是使用这个意义不大
# 获取每个部门的每个员工的年薪  (修改严格模式之后,才可以尝试)
mysql> select post,concat(name,':',salary*12) as a from employee group by post;

having 过滤

在分组之后,对数据进行再次筛选。

having注意事项:

  • having只能在分组后使用,不能单独使用(脱离分组)。
  • having 过滤的字段只能是分组字段或者聚合函数构建的字段。

where与having的区别?

where 发生在分组之前,having发生在分组之后。
where 可以独立使用不依赖于任何条件,而having必须依赖于分组,不能单独使用
where 可以使用任何字段,having 过滤的字段只能是分组字段或者聚合函数构建的字段。

测试:

获取部门平均工资大于5000的部门:
select post, avg(salary) from employee group by post having avg(salary) > 5000;

# 按照性别进行分组,保留平均年龄大于25的性别。
select sex, avg(age) from employee group by sex having avg(age) > 26;

统计各部门年龄在30岁及以上的员工的平均薪资,并且保留平均工资大于10000的部门
select
	post, 
	avg(salary) 
from employee 
	where 
	age >=30 
	group by 
	post 
	having avg(salary) >= 10000;

distinct去重可以与聚合函数配合使用

mysql> select count(distinct post) from employee;
+----------------------+
| count(distinct post) |
+----------------------+
|                    4 |
+----------------------+
1 row in set (0.00 sec)

order by排序

# 按照年龄从小到大的顺序查询
mysql> select * from employee order by age;
+----+------------+--------+-----+------------+-----------+--------------+------------+--------+-----------+
| id | name       | sex    | age | hire_date  | post      | post_comment | salary     | office | depart_id |
+----+------------+--------+-----+------------+-----------+--------------+------------+--------+-----------+
|  1 | 太白       | male   |  18 | 2017-03-01 | CEO       | NULL         |    7300.33 |    401 |         1 |
|  6 | jingliyang | female |  18 | 2011-02-11 | teacher   | NULL         |    9000.00 |    401 |         1 |
|  7 | 元宝       | male   |  18 | 1900-03-01 | teacher   | NULL         |   30000.00 |    401 |         1 |
| 11 | 丁丁       | female |  18 | 2011-03-12 | sale      | NULL         |    1000.37 |    402 |         2 |
| 12 | 星星       | female |  18 | 2016-05-13 | sale      | NULL         |    3000.29 |    402 |         2 |
| 15 | 程咬金     | male   |  18 | 1997-03-12 | operation | NULL         |   20000.00 |    403 |         3 |
| 16 | 程咬银     | female |  18 | 2013-03-11 | operation | NULL         |   19000.00 |    403 |         3 |
| 17 | 程咬铜     | male   |  18 | 2015-04-11 | operation | NULL         |   18000.00 |    403 |         3 |
| 18 | 程咬铁     | female |  18 | 2014-05-12 | operation | NULL         |   17000.00 |    403 |         3 |
|  5 | liwenzhou  | male   |  28 | 2012-11-01 | teacher   | NULL         |    2100.00 |    401 |         1 |
| 13 | 格格       | female |  28 | 2017-01-27 | sale      | NULL         |    4000.33 |    402 |         2 |
| 14 | 张野       | male   |  28 | 2016-03-11 | operation | NULL         |   10000.13 |    403 |         3 |
| 10 | 丫丫       | female |  38 | 2010-11-01 | sale      | NULL         |    2000.35 |    402 |         2 |
|  8 | 成龙       | male   |  48 | 2010-11-11 | teacher   | NULL         |   10000.00 |    401 |         1 |
|  9 | 歪歪       | female |  48 | 2015-03-11 | sale      | NULL         |    3000.13 |    402 |         2 |
|  4 | yuanhao    | male   |  73 | 2014-07-01 | teacher   | NULL         |    3500.00 |    401 |         1 |
|  2 | alex       | male   |  78 | 2015-03-02 | teacher   | NULL         | 1000000.31 |    401 |         1 |
|  3 | wupeiqi    | male   |  81 | 2013-03-05 | teacher   | NULL         |    8300.00 |    401 |         1 |
+----+------------+--------+-----+------------+-----------+--------------+------------+--------+-----------+
18 rows in set (0.00 sec)

mysql> mysql> select * from employee order by salary desc;
+----+------------+--------+-----+------------+-----------+--------------+------------+--------+-----------+
| id | name       | sex    | age | hire_date  | post      | post_comment | salary     | office | depart_id |
+----+------------+--------+-----+------------+-----------+--------------+------------+--------+-----------+
|  2 | alex       | male   |  78 | 2015-03-02 | teacher   | NULL         | 1000000.31 |    401 |         1 |
|  7 | 元宝       | male   |  18 | 1900-03-01 | teacher   | NULL         |   30000.00 |    401 |         1 |
| 15 | 程咬金     | male   |  18 | 1997-03-12 | operation | NULL         |   20000.00 |    403 |         3 |
| 16 | 程咬银     | female |  18 | 2013-03-11 | operation | NULL         |   19000.00 |    403 |         3 |
| 17 | 程咬铜     | male   |  18 | 2015-04-11 | operation | NULL         |   18000.00 |    403 |         3 |
| 18 | 程咬铁     | female |  18 | 2014-05-12 | operation | NULL         |   17000.00 |    403 |         3 |
| 14 | 张野       | male   |  28 | 2016-03-11 | operation | NULL         |   10000.13 |    403 |         3 |
|  8 | 成龙       | male   |  48 | 2010-11-11 | teacher   | NULL         |   10000.00 |    401 |         1 |
|  6 | jingliyang | female |  18 | 2011-02-11 | teacher   | NULL         |    9000.00 |    401 |         1 |
|  3 | wupeiqi    | male   |  81 | 2013-03-05 | teacher   | NULL         |    8300.00 |    401 |         1 |
|  1 | 太白       | male   |  18 | 2017-03-01 | CEO       | NULL         |    7300.33 |    401 |         1 |
| 13 | 格格       | female |  28 | 2017-01-27 | sale      | NULL         |    4000.33 |    402 |         2 |
|  4 | yuanhao    | male   |  73 | 2014-07-01 | teacher   | NULL         |    3500.00 |    401 |         1 |
| 12 | 星星       | female |  18 | 2016-05-13 | sale      | NULL         |    3000.29 |    402 |         2 |
|  9 | 歪歪       | female |  48 | 2015-03-11 | sale      | NULL         |    3000.13 |    402 |         2 |
|  5 | liwenzhou  | male   |  28 | 2012-11-01 | teacher   | NULL         |    2100.00 |    401 |         1 |
| 10 | 丫丫       | female |  38 | 2010-11-01 | sale      | NULL         |    2000.35 |    402 |         2 |
| 11 | 丁丁       | female |  18 | 2011-03-12 | sale      | NULL         |    1000.37 |    402 |         2 |
+----+------------+--------+-----+------------+-----------+--------------+------------+--------+-----------+
18 rows in set (0.01 sec)

# 多条件排序
# 按照年龄从小到大排序,相同年龄的工资从大到小排序
mysql> select id,name,age,salary from employee order by age, salary desc;
+----+------------+-----+------------+
| id | name       | age | salary     |
+----+------------+-----+------------+
|  7 | 元宝       |  18 |   30000.00 |
| 15 | 程咬金     |  18 |   20000.00 |
| 16 | 程咬银     |  18 |   19000.00 |
| 17 | 程咬铜     |  18 |   18000.00 |
| 18 | 程咬铁     |  18 |   17000.00 |
|  6 | jingliyang |  18 |    9000.00 |
|  1 | 太白       |  18 |    7300.33 |
| 12 | 星星       |  18 |    3000.29 |
| 11 | 丁丁       |  18 |    1000.37 |
| 14 | 张野       |  28 |   10000.13 |
| 13 | 格格       |  28 |    4000.33 |
|  5 | liwenzhou  |  28 |    2100.00 |
| 10 | 丫丫       |  38 |    2000.35 |
|  8 | 成龙       |  48 |   10000.00 |
|  9 | 歪歪       |  48 |    3000.13 |
|  4 | yuanhao    |  73 |    3500.00 |
|  2 | alex       |  78 | 1000000.31 |
|  3 | wupeiqi    |  81 |    8300.00 |
+----+------------+-----+------------+
18 rows in set (0.00 sec)

limit 限制查询条数

limit具有优化查询的效果,一般用于分页,或者数据量非常大的结果,我们可以使用limit关键字优化查询。

取出工资最高的前三位
mysql> select id,name,age,salary from employee order by salary desc limit 3;
+----+-----------+-----+------------+
| id | name      | age | salary     |
+----+-----------+-----+------------+
|  2 | alex      |  78 | 1000000.31 |
|  7 | 元宝      |  18 |   30000.00 |
| 15 | 程咬金    |  18 |   20000.00 |
+----+-----------+-----+------------+
3 rows in set (0.00 sec)

    SELECT * FROM employee ORDER BY salary DESC
        LIMIT 0,5; #从第0开始,即先查询出第一条,然后包含这一条在内往后查5条 

    SELECT * FROM employee ORDER BY salary DESC
        LIMIT 5,5; #从第5开始,即先查询出第6条,然后包含这一条在内往后查5条


正则表达式

#之前我们用like做模糊匹配,只有%和_,局限性比较强,所以我们说一个正则,之前我们是不是学过正则匹配,你之前学的正则表达式都可以用,正则是通用的
SELECT * FROM employee WHERE name REGEXP '^ale';

SELECT * FROM employee WHERE name REGEXP 'on$';

SELECT * FROM employee WHERE name REGEXP 'm{2}';


小结:对字符串匹配的方式
WHERE name = '太白';
WHERE name LIKE 'yua%';
WHERE name REGEXP 'on$';

2.多表查询

​ 首先说一下,我们写项目一般都会建一个数据库,那数据库里面是不是存了好多张表啊,不可能把所有的数据都放到一张表里面,肯定要分表来存数据,这样节省空间,数据的组织结构更清晰,解耦和程度更高,但是这些表本质上是不是还是一个整体啊,是一个项目所有的数据,那既然分表存了,就要涉及到多个表连接查询了,比如说员工信息一张表,部门信息一张表,那如果我想让你帮我查一下技术部门有哪些员工的姓名,你怎么办,单独找员工表能实现吗,不能,单独找部门表也无法实现,因为部门表里面没有员工的信息,对不对,所以就涉及到部门表和员工表来关联到一起进行查询了,好,那我们来建立这么两张表:

#建表
#部门表
create table department(
id int,
name varchar(20) 
);

#员工表,之前我们学过foreign key,强行加上约束关联,但是我下面这个表并没有直接加foreign key,这两个表我只是让它们在逻辑意义上有关系,并没有加foreign key来强制两表建立关系,为什么要这样搞,是有些效果要给大家演示一下
#所以,这两个表是不是先建立哪个表都行啊,如果有foreign key的话,是不是就需要注意表建立的顺序了。那我们来建表。
create table employee(
id int primary key auto_increment,
name varchar(20),
sex enum('male','female') not null default 'male',
age int,
dep_id int
);

#给两个表插入一些数据
insert into department values
(200,'技术'),
(201,'人力资源'),
(202,'销售'),
(203,'运营'); #注意这一条数据,在下面的员工表里面没有对应这个部门的数据

insert into employee(name,sex,age,dep_id) values
('太白','male',18,200),
('alex','female',48,201),
('wupeiqi','male',38,201),
('yuanhao','female',28,202),
('liwenzhou','male',18,200),
('jingliyang','female',18,204); #注意这条数据的dep_id字段的值,这个204,在上面的部门表里面也没有对应的部门id。所以两者都含有一条双方没有涉及到的数据,这都是为了演示一下效果设计的昂
;


#查看表结构和数据
mysql> desc department;
+-------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| id | int(11) | YES | | NULL | |
| name | varchar(20) | YES | | NULL | |
+-------+-------------+------+-----+---------+-------+

mysql> desc employee;
+--------+-----------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------+-----------------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(20) | YES | | NULL | |
| sex | enum('male','female') | NO | | male | |
| age | int(11) | YES | | NULL | |
| dep_id | int(11) | YES | | NULL | |
+--------+-----------------------+------+-----+---------+----------------+

mysql> select * from department;
+------+--------------+
| id | name |
+------+--------------+
| 200 | 技术 |
| 201 | 人力资源 |
| 202 | 销售 |
| 203 | 运营 |
+------+--------------+

mysql> select * from employee;
+----+------------+--------+------+--------+
| id | name | sex | age | dep_id |
+----+------------+--------+------+--------+
| 1 | 太白 | male | 18 | 200 |
| 2 | alex | female | 48 | 201 |
| 3 | wupeiqi | male | 38 | 201 |
| 4 | yuanhao | female | 28 | 202 |
| 5 | liwenzhou | male | 18 | 200 |
| 6 | jingliyang | female | 18 | 204 |
+----+------------+--------+------+--------+

2.1 连表查询

将关联的两张表连接起来,形成一张新的虚拟表,进行查询。

交叉连接

我们查询一张表时,其实后面可以跟多张表。

mysql> select * from employee,department;
+----+------------+--------+------+--------+------+--------------+
| id | name       | sex    | age  | dep_id | id   | name         |
+----+------------+--------+------+--------+------+--------------+
|  1 | 太白       | male   |   18 |    200 |  200 | 技术         |
|  1 | 太白       | male   |   18 |    200 |  201 | 人力资源     |
|  1 | 太白       | male   |   18 |    200 |  202 | 销售         |
|  1 | 太白       | male   |   18 |    200 |  203 | 运营         |
|  2 | alex       | female |   48 |    201 |  200 | 技术         |
|  2 | alex       | female |   48 |    201 |  201 | 人力资源     |
|  2 | alex       | female |   48 |    201 |  202 | 销售         |
|  2 | alex       | female |   48 |    201 |  203 | 运营         |
|  3 | wupeiqi    | male   |   38 |    201 |  200 | 技术         |
|  3 | wupeiqi    | male   |   38 |    201 |  201 | 人力资源     |
|  3 | wupeiqi    | male   |   38 |    201 |  202 | 销售         |
|  3 | wupeiqi    | male   |   38 |    201 |  203 | 运营         |
|  4 | yuanhao    | female |   28 |    202 |  200 | 技术         |
|  4 | yuanhao    | female |   28 |    202 |  201 | 人力资源     |
|  4 | yuanhao    | female |   28 |    202 |  202 | 销售         |
|  4 | yuanhao    | female |   28 |    202 |  203 | 运营         |
|  5 | liwenzhou  | male   |   18 |    200 |  200 | 技术         |
|  5 | liwenzhou  | male   |   18 |    200 |  201 | 人力资源     |
|  5 | liwenzhou  | male   |   18 |    200 |  202 | 销售         |
|  5 | liwenzhou  | male   |   18 |    200 |  203 | 运营         |
|  6 | jingliyang | female |   18 |    204 |  200 | 技术         |
|  6 | jingliyang | female |   18 |    204 |  201 | 人力资源     |
|  6 | jingliyang | female |   18 |    204 |  202 | 销售         |
|  6 | jingliyang | female |   18 |    204 |  203 | 运营         |
+----+------------+--------+------+--------+------+--------------+
24 rows in set (0.00 sec)

关于笛卡儿积:我们看一下上面的这些数据,有什么发现,首先看到这些字段都显示出来了,并且数据变得很多,我们来看一下,这么多条数据都是怎么来的,为什么会出现这么条数据,笛卡儿积这是一个数据名词,你可以去研究研究~~

  因为我们要进行连表查询,那么mysql并不知道你想要如何连接两个表的关系进行查询,那么mysql会将你两个表数据的所有组合关系都给你拼接成一条数据来显示,这样你就可以想查哪个关联关系的数据就查哪个了,如果还是不太理解看一下下面的图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6suY0TRZ-1668847693430)(F:\newschool\笔记总结\MySQL笔记总结\assets\BA8D470C-EDB4-4830-A912-E6CF186F37BD.png)]

​ 咱们为了更好的管理数据,为了节省空间,为了数据组织结构更清晰,将数据拆分到了不同表里面,但是本质上是不是还是一份数据,一份重复内容很多的很大的数据,所以我们即便是分表了,但是咱们是不是还需要找到一个方案把两个本来分开的表能够合并到一起来进行查询,那你是不是就可以根据部门找员工,根据员工找部门了,对不对,但是我们合并两个表的时候,如何合并,根据什么来合并,通过笛卡儿积这种合并有没有浪费,我们其实想做的是不是说我们的员工表中dep_id这个字段中的数据和部门表里面的id能够对应上就可以了,因为我们知道我们设计表的时候,是通过这两个字段来给两个表建立关系的,对不对,看下图:

内连接

在交叉连接的基础上做一个筛选,我们加一个筛选条件;

mysql> select * from employee,department where employee.dep_id = department.id;
+----+-----------+--------+------+--------+------+--------------+
| id | name      | sex    | age  | dep_id | id   | name         |
+----+-----------+--------+------+--------+------+--------------+
|  1 | 太白      | male   |   18 |    200 |  200 | 技术         |
|  2 | alex      | female |   48 |    201 |  201 | 人力资源     |
|  3 | wupeiqi   | male   |   38 |    201 |  201 | 人力资源     |
|  4 | yuanhao   | female |   28 |    202 |  202 | 销售         |
|  5 | liwenzhou | male   |   18 |    200 |  200 | 技术         |
+----+-----------+--------+------+--------+------+--------------+
5 rows in set (0.00 sec)

我们在这个基础上在做一次筛选,查看技术部员工的姓名。
我们的已知条件是:技术部,查看技术部所有的员工姓名。我们必须要连表操作。
select * from employee,department where employee.dep_id = department.id and department.name='技术';

mysql> select name from employee,department where employee.dep_id = department.id and department.name='技术';
# 我们形成的虚拟表有两个name字段,直接写name分辨不出来你需要的是哪个name
ERROR 1052 (23000): Column 'name' in field list is ambiguous

mysql> select employee.name from employee,department where employee.dep_id = department.id and department.name='技术';
+-----------+
| name      |
+-----------+
| 太白      |
| liwenzhou |
+-----------+
2 rows in set (0.00 sec)

仔细研究一下sql:

select employee.name from employee,department where employee.dep_id = department.id and department.name='技术';

select employee.name from employee inner join department on employee.dep_id=department.id where department.name='技术';

所以:

内连接: from 表1 inner join 表2 on 连表条件;

外连接

#重点:外链接语法

SELECT 字段列表
    FROM 表1 LEFT|RIGHT JOIN 表2
    ON 表1.字段 = 表2.字段;

左连接:将左表的所有的记录都查询出来。

select * from employee left join department on employee.dep_id=department.id;
mysql> select * from employee left join department on employee.dep_id=department.id;
+----+------------+--------+------+--------+------+--------------+
| id | name       | sex    | age  | dep_id | id   | name         |
+----+------------+--------+------+--------+------+--------------+
|  1 | 太白       | male   |   18 |    200 |  200 | 技术         |
|  5 | liwenzhou  | male   |   18 |    200 |  200 | 技术         |
|  2 | alex       | female |   48 |    201 |  201 | 人力资源     |
|  3 | wupeiqi    | male   |   38 |    201 |  201 | 人力资源     |
|  4 | yuanhao    | female |   28 |    202 |  202 | 销售         |
|  6 | jingliyang | female |   18 |    204 | NULL | NULL         |
+----+------------+--------+------+--------+------+--------------+
6 rows in set (0.00 sec)

右连接:将右表的所有的记录都查询出来。

 mysql> select * from employee right join department on employee.dep_id=department.id;
+------+-----------+--------+------+--------+------+--------------+
| id   | name      | sex    | age  | dep_id | id   | name         |
+------+-----------+--------+------+--------+------+--------------+
|    1 | 太白      | male   |   18 |    200 |  200 | 技术         |
|    2 | alex      | female |   48 |    201 |  201 | 人力资源     |
|    3 | wupeiqi   | male   |   38 |    201 |  201 | 人力资源     |
|    4 | yuanhao   | female |   28 |    202 |  202 | 销售         |
|    5 | liwenzhou | male   |   18 |    200 |  200 | 技术         |
| NULL | NULL      | NULL   | NULL |   NULL |  203 | 运营         |
+------+-----------+--------+------+--------+------+--------------+
6 rows in set (0.00 sec)xxxxxxxxxx select * from employee right join department on employee.dep_id=department.id;

全外连接 union

全外连接:在内连接的基础上增加左边有右边没有的和右边有左边没有的结果
#注意:mysql不支持全外连接 full JOIN
#强调:mysql可以使用此种方式间接实现全外连接
select * from employee left join department on employee.dep_id=department.id union select * from employee right join department on employee.dep_id=department.id;

+------+------------+--------+------+--------+------+--------------+
| id   | name       | sex    | age  | dep_id | id   | name         |
+------+------------+--------+------+--------+------+--------------+
|    1 | 太白       | male   |   18 |    200 |  200 | 技术         |
|    5 | liwenzhou  | male   |   18 |    200 |  200 | 技术         |
|    2 | alex       | female |   48 |    201 |  201 | 人力资源     |
|    3 | wupeiqi    | male   |   38 |    201 |  201 | 人力资源     |
|    4 | yuanhao    | female |   28 |    202 |  202 | 销售         |
|    6 | jingliyang | female |   18 |    204 | NULL | NULL         |
| NULL | NULL       | NULL   | NULL |   NULL |  203 | 运营         |
+------+------------+--------+------+--------+------+--------------+
7 rows in set (0.00 sec)

带条件的多表查询

#示例1:以内连接的方式查询employee和department表,并且employee表中的age字段值必须大于25,即找出年龄大于25岁的员工以及员工所在的部门

select employee.name,department.name from employee inner join department on employee.dep_id=department.id where employee.age > 25;


#示例2:以内连接的方式查询employee和department表,展示每个部门的部门名以及平均年龄
select department.name,avg(employee.age) from employee inner join department on employee.dep_id=department.id group by department.name;
+--------------+-------------------+
| name         | avg(employee.age) |
+--------------+-------------------+
| 人力资源     |           43.0000 |
| 技术         |           18.0000 |
| 销售         |           28.0000 |
+--------------+-------------------+
3 rows in set (0.01 sec)

2.2 子查询

将查询的sql语句用括号括起来作为另一个查询sql语句的查询条件,继续查询。

来,我们简单来个需求:技术部都有哪些员工的姓名,都显示出来:

  1. 我们可以通过技术已知条件查询出技术部的id.
  2. 然后我们通过employee表将刚才查出来的id作为条件,查询员工名。
# 连表做法
select employee.name from employee inner join department on employee.dep_id = department.id where department.name='技术'; 

# 子查询 
select id from department where name = '技术';
+------+
| id   |
+------+
|  200 |
+------+
# 在我们看来这个是一张虚拟表,但是作为sql语句的角度它就是数字200.

select name from employee where dep_id = (select id from department where name = '技术');

子查询的注意事项:

#1:子查询是将一个查询语句嵌套在另一个查询语句中。
#2:内层查询语句的查询结果,可以为外层查询语句提供查询条件。
#3:子查询中可以包含:IN、NOT IN、ANY、ALL、EXISTS 和 NOT EXISTS等关键字
#4:还可以包含比较运算符:= 、 !=、> 、<等

带in的子查询

#查询员工平均年龄在25岁以上的部门名,可以用连表,也可以用子查询,我们用子查询来搞一下

已知条件:按照部门进行分组,每组平均25岁以上dep_id,在employee表中。 部门名:在department表中。
select dep_id from employee group by dep_id having avg(age) > 25;
+--------+
| dep_id |
+--------+
|    201 |
|    202 |
+--------+
# 在我们看来这个是一张虚拟表,但是作为sql语句的角度它就是集合(201, 202).

select name from department where id in (select dep_id from employee group by dep_id having avg(age) > 25);

#查看技术部员工姓名
已知条件技术部,通过技术部可以查询技术部的id,作为条件查询员工表的姓名。
select id from department where name = '技术';

select name from employee where dep_id = (select id from department where name = '技术');

#查看不足1人的部门名(子查询得到的是有人的部门id)
我们最终要查询的是部门名,我们先查询employee大于等于1人的dep_id, 然后我们在查询department表中不在这个dep_id中的记录。

select dep_id from employee group by dep_id having count(id) >= 1;

select name from department where id not in (select dep_id from employee group by dep_id having count(id) >= 1);

带比较运算的子查询

#比较运算符:=、!=、>、>=、<、<=、<>
#查询大于所有人平均年龄的员工名与年龄
	# 先求出所有人的平均年龄,然后作为条件,在查询大于平均年龄的员工与年龄。
select avg(age) from employee;
select name,age from employee where age >(select avg(age) from employee);

带EXISTS关键字的子查询

​ EXISTS关字键字表示存在。在使用EXISTS关键字时,内层查询语句不返回查询的记录。而是返回一个真假值。True或False
    当返回True时,外层查询语句将进行查询;当返回值为False时,外层查询语句不进行查询。还可以写not exists,和exists的效果就是反的

mysql> select * from employee where exists (select id from department where id=200);
+----+------------+--------+------+--------+
| id | name       | sex    | age  | dep_id |
+----+------------+--------+------+--------+
|  1 | 太白       | male   |   18 |    200 |
|  2 | alex       | female |   48 |    201 |
|  3 | wupeiqi    | male   |   38 |    201 |
|  4 | yuanhao    | female |   28 |    202 |
|  5 | liwenzhou  | male   |   18 |    200 |
|  6 | jingliyang | female |   18 |    204 |
+----+------------+--------+------+--------+
6 rows in set (0.00 sec)

mysql> select * from employee where exists (select id from department where id=2000);
Empty set (0.00 sec)

2.3 连表查询+子查询

#查询大于部门内平均年龄的员工名、年龄
1. 先查出部门内的平均年龄
select dep_id, avg(age) from employee group by dep_id;

2. 将上面的临时表与employee表进行连表,找思路:
select * from employee inner join (select dep_id, avg(age) as av from employee group by dep_id) t2 on employee.dep_id = t2.dep_id;
+----+------------+--------+------+--------+--------+---------+
| id | name       | sex    | age  | dep_id | dep_id | av      |
+----+------------+--------+------+--------+--------+---------+
|  1 | 太白       | male   |   18 |    200 |    200 | 18.0000 |
|  2 | alex       | female |   48 |    201 |    201 | 43.0000 |
|  3 | wupeiqi    | male   |   38 |    201 |    201 | 43.0000 |
|  4 | yuanhao    | female |   28 |    202 |    202 | 28.0000 |
|  5 | liwenzhou  | male   |   18 |    200 |    200 | 18.0000 |
|  6 | jingliyang | female |   18 |    204 |    204 | 18.0000 |
+----+------------+--------+------+--------+--------+---------+
6 rows in set (0.00 sec)
3. 大于部门内平均年龄的员工名、年龄
select employee.name, employee.age from employee inner join (select dep_id, avg(age) as av from employee group by dep_id) t2 on employee.dep_id = t2.dep_id where employee.age > t2.av;

思路:

  • 先按照已知条件查询出虚拟表,然后在结合现存的表与虚拟表进行分析(连表,子查询),在向下依次查询。

1.Navicat数据库可视化工具安装与使用

1.1 引子

我们现在操作sql语句都是通过终端,开启服务端之后,我们需要用终端这个软件充当客户端去链接服务端,然后在终端这个客户端我们输入各种sql语句去操作服务端的MySQL数据库。

但是!我们用终端充当MySQL的服务端使用不方便,因为终端充当的客户端只是支持sql语句,其他没有任何功能。比如保存sql语句,可通过鼠标进行增删改查数据等等,一切都是需要命令的。这种客户端对于我们使用MySQL不友好,效率低。

市面上能充当MySQL客户端的软件有很多种,今天我们学习比较常用的一种软件:Navicat软件。

1.2 下载安装

下载安装之后,需要输入注册码:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yfEWcYIH-1669260779874)(F:\newschool\笔记总结\MySQL笔记总结\assets\EEEA58E3-50E9-48DB-895E-96822D63A495.png)]

从网上找一个注册码。

1.3 建立连接

打开界面建立链接:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pkC2L1pT-1669260779875)(F:\newschool\笔记总结\MySQL笔记总结\assets\3B9D18BA-C268-483F-A086-BB43B01B9BD8.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qMAcUgih-1669260779877)(F:\newschool\笔记总结\MySQL笔记总结\assets\8633B977-D890-4040-B461-AA280F69DF1C.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZSuNatRP-1669260779878)(F:\newschool\笔记总结\MySQL笔记总结\assets\DF2FD6F8-B0DD-42FA-BD92-584AAA44AEDC.png)]

1.4 常用操作

库的操作

选中任意一个数据库,点击右键就可以对库进行相关操作。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mAOwuV46-1669260779879)(F:\newschool\笔记总结\MySQL笔记总结\assets\FC3796E9-645E-40BA-AB55-30BB56C84BCE.png)]

表的操作

我们选择一个数据库,打开对应的里面的表,右键,就可以进行表的相关操作。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pcQSrVSg-1669260779879)(F:\newschool\笔记总结\MySQL笔记总结\assets\9D2B4EA4-D53E-4587-9535-5317FF130131.png)]

新建表:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RYGAtISd-1669260779888)(F:\newschool\笔记总结\MySQL笔记总结\assets\A41D3D4B-3FDF-4E18-8A8B-A252AE493406.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-998b9Dep-1669260779889)(F:\newschool\笔记总结\MySQL笔记总结\assets\64A720FF-6117-4D83-B834-DBF937AA0BF2.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Mf30usf9-1669260779890)(F:\newschool\笔记总结\MySQL笔记总结\assets\2A931B7C-17CB-4BF2-B22D-6D9C5050AE5C.png)]

我们查看一下MySQL的服务端,是否已经建表了。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5Ou5PYzo-1669260779891)(F:\newschool\笔记总结\MySQL笔记总结\assets\F50B139F-CED4-4858-9667-579FFDE6AA81.png)]

我们在建立一张dep表:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JBDPEYb4-1669260779892)(F:\newschool\笔记总结\MySQL笔记总结\assets\5ABAEC1C-7C3E-44FB-9F92-BB6051D98FFC.png)]

我们给emp表的dep_id建立外键受制于dep的id字段:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6Ex6o821-1669260779892)(F:\newschool\笔记总结\MySQL笔记总结\assets\2E060382-A63F-487A-9431-89227FC15F37.png)]

修改emp表的某些字段,就是选择设计表:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ayHM44S5-1669260779893)(F:\newschool\笔记总结\MySQL笔记总结\assets\D158FAA3-61DC-4F7A-88FF-3C4309024354.png)]

行记录的操作

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-v1XiSHpa-1669260779894)(F:\newschool\笔记总结\MySQL笔记总结\assets\D3148E08-6A59-46FF-8B4F-D7975299529F.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hgc1jzX8-1669260779894)(F:\newschool\笔记总结\MySQL笔记总结\assets\61F25C17-9E64-42C9-BBF1-7CE89BF0F330.png)]

查看er关系图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3m0LSFHx-1669260779895)(F:\newschool\笔记总结\MySQL笔记总结\assets\6E642045-8BEB-4B77-822D-A188FAD7A109.png)]

导入导出sql语句

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gHRmNykk-1669260779896)(F:\newschool\笔记总结\MySQL笔记总结\assets\EFFA36BC-41D4-418D-8CC4-B6DE158CE959.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Asgqw0gm-1669260779897)(F:\newschool\笔记总结\MySQL笔记总结\assets\287A2ABD-A2C8-4836-B8BA-F5D53BBA1196.png)]

导出之后,删除db1库,在点击导入:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Qzb5oMbs-1669260779898)(F:\newschool\笔记总结\MySQL笔记总结\assets\DB29A19C-1DA6-4385-931C-F32A6F638690.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-asXmuKeM-1669260779899)(F:\newschool\笔记总结\MySQL笔记总结\assets\4592070F-2050-4953-B75E-7696F2707FA1.png)]

导入sql文件,我们先建立一个数据库比如db2,打开此数据库:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-orhzSr50-1669260779899)(F:\newschool\笔记总结\MySQL笔记总结\assets\92935C60-F680-4FB2-8A5C-AF41358F06BA.png)]

新建查询 测试sql语句:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-q0oVBQ8l-1669260779900)(F:\newschool\笔记总结\MySQL笔记总结\assets\2C8F564D-9DAE-4107-988D-DD9F91073762.png)]

写sql语句,可以对其进行简化美化:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mCh0PFBW-1669260779901)(F:\newschool\笔记总结\MySQL笔记总结\assets\49FDB567-F421-4391-87A2-A1D3D2609DDA.png)]

1.5 总结

我们最好是使用Navicat进行sql语句的查询,对于其他的一些操作,我们是以sql语句为主。Navicat就是一个辅助的工具。

2.pymysql模块的使用

2.1 简单介绍以及实例

  • 引子

    我们无论是终端还是Vavicat都可以充当MySQL的客户端,但是他们都不能与我们的python代码结合使用。而我们写项目非常重要的一个环节就是与数据库中的数据进行交互。我们的python怎么和数据库的数据进行交互?我们需要使用pymysql模块。

    pymysql模块有两个角色:

    • 充当MySQL的客户端。
    • 结合到python代码中做python代码与数据库服务端的交互的特殊的客户端。
  • 下载安装

    pip3 install pymysql
    
  • 简单测试

    import pymysql
    
    # 1. 配置连接数据库
    conn = pymysql.connect(
        host='127.0.0.1',
        port=3306,
        user='root',
        password='123',
        database='db2',
        charset='utf8'
    )
    
    # 2. 创建cursor游标(类似于文件句柄)
    
    cursor = conn.cursor()
    

    3. 构建sql语句并执行

sql = 'select * from user;'
cursor.execute(sql)

4. 获取查询结果

all_data = cursor.fetchall()
print(all_data)

5. 关闭游标,关闭连接

cursor.close()
conn.close()

((1, 'barry', '123'), (2, 'yaocong', '123'), (3, 'zhili', '123'), (4, 'minghang', '123'))


由于这个结果的形式不易操作,我们可以给其进行相应的配置,配置成字典的形式。
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)


import pymysql

# 1.配置连接数据库
conn = pymysql.connect(
  host='127.0.0.1',
  port=3306,
  user='root',
  password='',
  database='db2',
  charset='utf8',
)
#2.创建cursor游标(类似于文件句柄)
# cursor = conn.cursor()
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
# 构建sql语句并执行
sql = 'select * from user;'
cursor.execute(sql)

#获取查询结果

all_data = cursor.fetchall()
print(all_data)
# print(cursor)

cursor.close()
conn.close()




[{'id': 1, 'username': 'barry', 'password': '123'}, {'id': 2, 'username': 'yaocong', 'password': '123'}, {'id': 3, 'username': 'zhili', 'password': '123'}, {'id': 4, 'username': 'minghang', 'password': '123'}]
  • 绝对移动相对移动

    # 绝对移动,每次都是从第一行开始移动
    cursor.scroll(2, 'absolute')
    one_data = cursor.fetchone()  # 获取一条记录
    print(one_data)
    cursor.scroll(4, 'absolute')
    one_data = cursor.fetchone()  # 获取一条记录
    print(one_data)
    import pymysql
    
    # 1.配置连接数据库
    conn = pymysql.connect(
        host='127.0.0.1',
        port=3306,
        user='root',
        password='',
        database='db2',
        charset='utf8',
    )
    #2.创建cursor游标(类似于文件句柄)
    # cursor = conn.cursor()
    cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
    # 构建sql语句并执行
    sql = 'select * from user;'
    cursor.execute(sql)
    
    #获取查询结果
    
    
    cursor.scroll(1, 'absolute')
    one_data = cursor.fetchone()
    print(one_data)
    
    
    cursor.close()
    conn.close()
    
    
    # 相对移动 针对于上一次记录的位置移动
    one_data = cursor.fetchone()  # 获取一条记录
    # print(one_data)
    cursor.scroll(2, 'relative')
    one_data = cursor.fetchone()  # 获取一条记录
    cursor.scroll(2, 'relative')
    one_data = cursor.fetchone()  # 获取一条记录
    print(one_data)
    
    
    
    

2.2 execute sql注入

我们使用pymysql进行数据库的操作时,尤其是登录等操作,可能会留有安全隐患。

我们简单的做一个登录功能:

import pymysql

# 1. 配置连接数据库
conn = pymysql.connect(
  host='127.0.0.1',
  port=3306,
  user='root',
  password='123',
  database='db2',
  charset='utf8'
)

cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)

username = input('请输入用户名:').strip()
password = input('请输入密码:').strip()

sql = 'select * from user where username = "%s" and password = "%s";' % (username, password)

res = cursor.execute(sql)
# print(res,type(res))

if res:
  print('登录成功')
else:
  print('登录失败')
# 5. 关闭游标,关闭连接
cursor.close()
conn.close()

上面的代码逻辑上没有任何问题,但是有安全隐患:

已知用户名,破解

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-whSiVwwD-1669260779901)(F:\newschool\笔记总结\MySQL笔记总结\assets\C9785769-B854-4ADF-AC2B-09527F3B8427.png)]

select * from user where username = "barry" -- fdsafdaf and password = "qweqwe";
-- 就是注释上面的sql语句:
select * from user where username = "barry";

用户名密码都不知道,破解:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PWjP9ZFp-1669260779902)(F:\newschool\笔记总结\MySQL笔记总结\assets\80B84DD9-E209-49CC-B231-6C7EF263E9B3.png)]

select * from user where username = "fdafd" or 1=1 -- fdsafdaf and password = "qweqwe";
select * from user where username = "fdafd" or 1=1;

sql注入:不法分子通过你程序中的设计漏洞,破解了你的数据库,从而获取到一些非法的重要的数据。

我们这个sql注入为什么可以成功?我们在构建sql语句时(字符串)如果遇到了格式化输出,一定不要使用python的格式化输出,要使用pymysql提供的格式化输出的功能。

解决方式:

username = input('请输入用户名:').strip()
password = input('请输入密码:').strip()

sql = 'select * from user where username = %s and password = %s;'

res = cursor.execute(sql, [username, password])
# print(res,type(res))

作业:自己网上查询一些sql注入的案例以及解决方式,最少找3种。

2.3 增删改

对表的数据的进行增删改的sql语句的操作,但是如果需要增删改 一定要加一个connmit()代码。

import pymysql

# 1. 配置连接数据库
conn = pymysql.connect(
    host='127.0.0.1',
    port=3306,
    user='root',
    password='123',
    database='db2',
    charset='utf8'
)

cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)

# 增
# 增加一条
# sql = 'insert into user(username,password) values("大招", "666");'
# cursor.execute(sql)
# 增加多条
# sql = 'insert into user(username,password) values(%s, %s);'
# 
# cursor.executemany(sql, [('xuange', '123'), ('小金', '123')])

# 删改
connmit()
cursor.close()
conn.close()

2.4 查

import pymysql

# 1. 配置连接数据库
conn = pymysql.connect(
    host='127.0.0.1',
    port=3306,
    user='root',
    password='123',
    database='db2',
    charset='utf8'
)

# 2. 创建cursor游标(类似于文件句柄)

cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)


# 3. 构建sql语句并执行

sql = 'select * from user;'

cursor.execute(sql)

# 4. 获取查询结果

# all_data = cursor.fetchall()  # 获取所有结果
# print(all_data)

# one_data = cursor.fetchone()  # 获取一条记录
# print(one_data)
# one_data = cursor.fetchone()  # 获取一条记录
# print(one_data)


# many_data = cursor.fetchmany(2)  # 获取n条记录
# print(many_data)


# 5. 关闭游标,关闭连接
cursor.close()
conn.close()

2.5 获取新增的数据id

只能获取新增的数据的id值。

import pymysql

# 1. 配置连接数据库
conn = pymysql.connect(
    host='127.0.0.1',
    port=3306,
    user='root',
    password='123',
    database='db2',
    charset='utf8'
)

cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)

# 增
# 增加一条
# sql = 'insert into user(username,password) values("xiaoyu", "666");'
sql = 'select * from user;'  # 查询时不能获取
cursor.execute(sql)
print(cursor.lastrowid)

# connmit()
cursor.close()
conn.close()

typora-copy-images-to: ./assets

1.索引介绍

为何要有索引?

一般的应用系统,读写比例在10:1左右,而且插入操作和一般的更新操作很少出现性能问题,在生产环境中,我们遇到最多的,也是最容易出问题的,还是一些复杂的查询操作,因此对查询语句的优化显然是重中之重。说起加速查询,就不得不提到索引了。

为什么要讲索引?我们之前创建primary key 创建unique都有加速查询的效果。假如我们数据库存储1万条数据,我就想查询第1万条数据,如果数据在磁盘中存储就是按照顺序1,2,3,4,5…1万条这样存储的,那么我们就需要1万次磁盘操作,效率低。以机械硬盘举例:7200/1分钟,1秒种转120次。磁盘是有盘面的,机械臂在上面一圈一圈的转,1秒钟转120次,首先我们要找到这个数据在那条环上,然后在寻找点,平均半圈找到1个点。7200的转速来说平均找到一个数据为9毫秒。如果找1百万条数据呢?1万次是1万个9毫秒,大概90秒太慢了。所以磁盘慢,我们争取要将和磁盘打交道的次数降低,争取一次就找到,这样就是最快的。这样就需要我们索引的知识了。索引不像是顺序存储的结构,而是像是树形结构。

什么是索引?

索引在MySQL中也叫做“键”或者"key"(primary key,unique key,还有一个index key),是存储引擎用于快速找到记录的一种数据结构。索引对于良好的性能非常关键,尤其是当表中的数据量越来越大时,索引对于性能的影响愈发重要,减少io次数,加速查询。(其中primary key和unique key,除了有加速查询的效果之外,还有约束的效果,primary key 不为空且唯一,unique key 唯一,而index key只有加速查询的效果,没有约束效果)
   索引优化应该是对查询性能优化最有效的手段了。索引能够轻易将查询性能提高好几个数量级。
   索引相当于字典的音序表,如果要查某个字,如果不使用音序表,则需要从几百页中逐页去查。

强调:一旦为表创建了索引,以后的查询最好先查索引,再根据索引定位的结果去找数据

                      30

        10                          40

   5         15               35          66

1    6    11   19          21   39     55    100

你是否对索引存在误解?

索引是应用程序设计和开发的一个重要方面。若索引太多,应用程序的性能可能会受到影响。而索引太少,对查询性能又会产生影响,要找到一个平衡点,这对应用程序的性能至关重要。一些开发人员总是在事后才想起添加索引----我一直认为,这源于一种错误的开发模式。如果知道数据的使用,从一开始就应该在需要处添加索引。开发人员往往对数据库的使用停留在应用的层面,比如编写SQL语句、存储过程之类,他们甚至可能不知道索引的存在,或认为事后让相关DBA加上即可。DBA往往不够了解业务的数据流,而添加索引需要通过监控大量的SQL语句进而从中找到问题,这个步骤所需的时间肯定是远大于初始添加索引所需的时间,并且可能会遗漏一部分的索引。当然索引也并不是越多越好,我曾经遇到过这样一个问题:某台MySQL服务器iostat显示磁盘使用率一直处于100%,经过分析后发现是由于开发人员添加了太多的索引,在删除一些不必要的索引之后,磁盘使用率马上下降为20%。可见索引的添加也是非常有技术含量的。

2.索引原理

​ 索引的目的在于提高查询效率,与我们查阅图书所用的目录是一个道理:先定位到章,然后定位到该章下的一个小节,然后找到页数。相似的例子还有:查字典,查火车车次,飞机航班等,下面内容看不懂的同学也没关系,能明白这个目录的道理就行了。 那么你想,书的目录占不占页数,这个页是不是也要存到硬盘里面,也占用硬盘空间。你再想,你在没有数据的情况下先建索引或者说目录快,还是已经存在好多的数据了,然后再去建索引,哪个快,肯定是没有数据的时候快,因为如果已经有了很多数据了,你再去根据这些数据建索引,是不是要将数据全部遍历一遍,然后根据数据建立索引。你再想,索引建立好之后再添加数据快,还是没有索引的时候添加数据快,索引是用来干什么的,是用来加速查询的,那对你写入数据会有什么影响,肯定是慢一些了,因为你但凡加入一些新的数据,都需要把索引或者说书的目录重新做一个,所以索引虽然会加快查询,但是会降低写入的效率。

索引的影响

1、在表中有大量数据的前提下,创建索引速度会很慢

2、在索引创建完毕后,对表的查询性能会发幅度提升,但是写性能会降低

本质都是:通过不断地缩小想要获取数据的范围来筛选出最终想要的结果,同时把随机的事件变成顺序的事件,也就是说,有了这种索引机制,我们可以总是用同一种查找方式来锁定数据。

数据库也是一样,但显然要复杂的多,因为不仅面临着等值查询,还有范围查询(>、<、between、in)、模糊查询(like)、并集查询(or)等等。数据库应该选择怎么样的方式来应对所有的问题呢?我们回想字典的例子,能不能把数据分成段,然后分段查询呢?最简单的如果1000条数据,1到100分成第一段,101到200分成第二段,201到300分成第三段…这样查第250条数据,只要找第三段就可以了,一下子去除了90%的无效数据。但如果是1千万的记录呢,分成几段比较好?稍有算法基础的同学会想到搜索树,其平均复杂度是lgN,具有不错的查询性能。但这里我们忽略了一个关键的问题,复杂度模型是基于每次相同的操作成本来考虑的。而数据库实现比较复杂,一方面数据是保存在磁盘上的,另外一方面为了提高性能,每次又可以把部分数据读入内存来计算,因为我们知道访问磁盘的成本大概是访问内存的十万倍左右,所以简单的搜索树难以满足复杂的应用场景。

​ 前面提到了访问磁盘,那么这里先简单介绍一下磁盘IO和预读,磁盘读取数据靠的是机械运动,每次读取数据花费的时间可以分为寻道时间、旋转延迟、传输时间三个部分,寻道时间指的是磁臂移动到指定磁道所需要的时间,主流磁盘一般在5ms以下;旋转延迟就是我们经常听说的磁盘转速,比如一个磁盘7200转/min,表示每分钟能转7200次,也就是说1秒钟能转120次,旋转延迟就是1/120/2 = 4.17ms,也就是半圈的时间(这里有两个时间:平均寻道时间,受限于目前的物理水平,大概是5ms的时间,找到磁道了,还需要找到你数据存在的那个点,寻点时间,这寻点时间的一个平均值就是半圈的时间,这个半圈时间叫做平均延迟时间,那么平均延迟时间加上平均寻道时间就是你找到一个数据所消耗的平均时间,大概9ms,其实机械硬盘慢主要是慢在这两个时间上了,当找到数据然后把数据拷贝到内存的时间是非常短暂的,和光速差不多了);传输时间指的是从磁盘读出或将数据写入磁盘的时间,一般在零点几毫秒,相对于前两个时间可以忽略不计。那么访问一次磁盘的时间,即一次磁盘IO的时间约等于5+4.17 = 9ms左右,听起来还挺不错的,但要知道一台500 -MIPS(Million Instructions Per Second)的机器每秒可以执行5亿条指令,因为指令依靠的是电的性质,换句话说执行一次IO的消耗的时间段下cpu可以执行约450万条指令,数据库动辄十万百万乃至千万级数据,每次9毫秒的时间,显然是个灾难,所以我们要想办法降低IO次数。下图是计算机硬件延迟的对比图,供大家参考:

考虑到磁盘IO是非常高昂的操作,计算机操作系统做了一些优化,当一次IO时,不光把当前磁盘地址的数据,而是把相邻的数据也都读取到内存缓冲区内,因为局部预读性原理告诉我们,当计算机访问一个地址的数据的时候,与其相邻的数据也会很快被访问到。每一次IO读取的数据我们称之为一页(page)。具体一页有多大数据跟操作系统有关,一般为4k或8k,也就是我们读取一页内的数据时候,实际上才发生了一次IO,这个理论对于索引的数据结构设计非常有帮助。

3.索引的数据结构

前面讲了索引的基本原理,数据库的复杂性,又讲了操作系统的相关知识,目的就是让大家了解,现在我们来看看索引怎么做到减少IO,加速查询的。任何一种数据结构都不是凭空产生的,一定会有它的背景和使用场景,我们现在总结一下,我们需要这种数据结构能够做些什么,其实很简单,那就是:每次查找数据时把磁盘IO次数控制在一个很小的数量级,最好是常数数量级。那么我们就想到如果一个高度可控的多路搜索树是否能满足需求呢?就这样,b+树应运而生(B+树是通过二叉查找树,再由平衡二叉树,B树演化而来,等到后面讲算法的时候再将,现在这个阶段,你大概了解一下就行了,别深究~~)。

如上图,是一颗b+树,最上层是树根,中间的是树枝,最下面是叶子节点,关于b+树的定义可以参见B+树,这里只说一些重点,浅蓝色的块我们称之为一个磁盘块或者叫做一个block块,这是操作系统一次IO往内存中读的内容,一个块对应四个扇区,可以看到每个磁盘块包含几个数据项(深蓝色所示,一个磁盘块里面包含多少数据,一个深蓝色的块表示一个数据,其实不是数据,后面有解释)和指针(黄色所示,看最上面一个,p1表示比上面深蓝色的那个17小的数据的位置在哪,看它指针指向的左边那个块,里面的数据都比17小,p2指向的是比17大比35小的磁盘块),如磁盘块1包含数据项17和35,包含指针P1、P2、P3,P1表示小于17的磁盘块,P2表示在17和35之间的磁盘块,P3表示大于35的磁盘块。真实的数据存在于叶子节点即3、5、9、10、13、15、28、29、36、60、75、79、90、99。非叶子节点只不存储真实的数据,只存储指引搜索方向的数据项,如17、35并不真实存在于数据表中。

b+树的查找过程

如图所示,如果要查找数据项29,那么首先会把磁盘块1由磁盘加载到内存,此时发生一次IO,在内存中用二分查找确定29在17和35之间,锁定磁盘块1的P2指针,内存时间因为非常短(相比磁盘的IO)可以忽略不计,通过磁盘块1的P2指针的磁盘地址把磁盘块3由磁盘加载到内存,发生第二次IO,29在26和30之间,锁定磁盘块3的P2指针,通过指针加载磁盘块8到内存,发生第三次IO,同时内存中做二分查找找到29,结束查询,总计三次IO。真实的情况是,3层的b+树可以表示上百万的数据,如果上百万的数据查找只需要三次IO,性能提高将是巨大的,如果没有索引,每个数据项都要发生一次IO,那么总共需要百万次的IO,显然成本非常非常高。除了叶子节点,其他的树根啊树枝啊保存的就是数据的索引,他们是为你建立这种数据之间的关系而存在的。

b+树性质

1**.索引字段要尽量的小并且区分度高**:通过上面的分析,我们知道IO次数取决于b+数的高度h或者说层级,这个高度或者层级就是你每次查询数据的IO次数,假设当前数据表的数据为N,每个磁盘块的数据项的数量是m,则有h=㏒(m+1)N,当数据量N一定的情况下,m越大,h越小;而m = 磁盘块的大小 / 数据项的大小,磁盘块的大小也就是一个数据页的大小,是固定的,如果数据项占的空间越小,数据项的数量越多,树的高度越低。这就是为什么每个数据项,即索引字段要尽量的小,比如int占4字节,要比bigint8字节少一半。这也是为什么b+树要求把真实的数据放到叶子节点而不是内层节点,一旦放到内层节点,磁盘块的数据项会大幅度下降,导致树增高。当数据项等于1时将会退化成线性表。

比如:你每个叶子节点只存两个数据的情况下,你如果想多加两个数据,你怎么办

所以我们需要将树建的越低越好,因为每个磁盘块的大小是一定的,那么意味着我们单个数据库里面的单个数据的大小越大越好还是越小越好,你想啊,你现在叶子节点的磁盘块,两个数据就沾满了,你数据要是更大的话,你这一个磁盘块就只能放一个数据了亲,这样随着你数据量的增大,你的树就越高啊,我们应该想办法让树的层数低下来,效率才高啊,所以我们应该让每个数据的大小尽可能的小,那就意味着,你每个磁盘块存的数据就越多,你树的层级就越少啊,树就越低啊,对不对。并且数据的数量越大,你需要的磁盘块越多,磁盘块越多,你需要的树的层级就越高,所以我们应该尽可能的用更少的磁盘块来装更多的数据项,这样树的高度才能降下来,怎么才能装更多的数据项啊,当然是你的数据项越小,你的磁盘块盛放的数据量就越多了,所以如果一张表中有很多的字段,我们应该用什么字段来建立索引啊,如果你有id字段、name字段、描述信息字段等等的,你应该用哪个来建立索引啊,当然是id字段了,你想想对不对,因为id是个数字,占用空间最少啊。

2.索引的最左匹配特性简单来说就是你的数据来了以后,从数据块的左边开始匹配,在匹配右边的,知道这句话就行啦~~~~,我们继续学下面的内容。当b+树的数据项是复合的数据结构,比如(name,age,sex)的时候,b+数是按照从左到右的顺序来建立搜索树的,比如当(张三,20,F)这样的数据来检索的时候,b+树会优先比较name来确定下一步的所搜方向,如果name相同再依次比较age和sex,最后得到检索的数据;但当(20,F)这样的没有name的数据来的时候,b+树就不知道下一步该查哪个节点,因为建立搜索树的时候name就是第一个比较因子,必须要先根据name来搜索才能知道下一步去哪里查询。比如当(张三,F)这样的数据来检索时,b+树可以用name来指定搜索方向,但下一个字段age的缺失,所以只能把名字等于张三的数据都找到,然后再匹配性别是F的数据了, 这个是非常重要的性质,即索引的最左匹配特性。

4.索引分类

4.1 索引三大类

MySQL数据的索引分为三大类:

索引的两大类型hash与b+tree

我们可以在创建上述索引的时候,为其指定索引类型,分两类
      hash类型的索引:查询单条快,范围查询慢
      b+tree类型的索引:b+树,层数越多,数据量指数级增长(我们就用它,因为innodb默认支持它)
   	  full-text类型的索引:全文索引。课下可以了解。
      #不同的存储引擎支持的索引类型也不一样
   
      InnoDB 支持事务,支持行级别锁定,支持 B-tree、Full-text 等索引,不支持 Hash 索引;
      MyISAM 不支持事务,支持表级别锁定,支持 B-tree、Full-text 等索引,不支持 Hash 索引;
      Memory 不支持事务,支持表级别锁定,支持 B-tree、Hash 等索引,不支持 Full-text 索引;
      NDB 支持事务,支持行级别锁定,支持 Hash 索引,不支持 B-tree、Full-text 等索引;
      Archive 不支持事务,支持表级别锁定,不支持 B-tree、Hash、Full-text 等索引;

mysql支持hash。full-text索引,跟算法有关,比较复杂我们不讲。

  • hash 索引: 查询单条快,范围查询慢。
  • **full-text全文索引:**优点是能够更快的检索和匹配,缺点是需要消耗额外的空间以及增删改操作都需要同步修改索引,耗时增加。
  • **b+tree索引:**常用的,树形结构,io与层数相关。

4.2 b+tree索引的索引细分

4.2.1 按照查询效率进行分类

按照查询效率分类分为聚集索引与辅助索引。在数据库中,B+树的高度一般都在24层,这也就是说查找某一个键值的行记录时最多只需要2到4次IO,这倒不错。因为当前一般的机械硬盘每秒至少可以做100次IO,24次的IO意味着查询时间只需要0.02~0.04秒。

  • 聚集索引

    称之为叫主键索引

    通过主键字段进行查询称之为聚集索引,一次完整的查询就可以查到结果。

    就是让主键字段作为sql语句的查询条件。如果我们的数据结构是3层,代表着三次IO,例如:

  select name,age from user where id=3000000;
  • **特点:**叶子节点存储的是完整的一整行记录数据。

  • 辅助索引

    称之为非主键索引(unique唯一索引,index普通索引)

    就是我们在查询的时候,where后面需要写id之外的其他字段名称来进行查询,比如说是where name=xx,没法用到主键索引的效率,怎么办,就需要我们添加辅助索引了,给name添加一个辅助索引。

    ​ 表中除了聚集索引外其他索引都是辅助索引(Secondary Index,也称为非聚集索引)(unique key啊、index key啊),与聚集索引的区别是:辅助索引的叶子节点不包含行记录的全部数据。

    ​ 叶子节点存放的是对应的那条数据的主键字段的值,除了包含键值以外,每个叶子节点中的索引行中还包含一个书签(bookmark),其实这个书签你可以理解为是一个{‘name字段’,name的值,主键id值}的这么一个数据。该书签用来告诉InnoDB存储引擎去哪里可以找到与索引相对应的行数据。如果我们select 后面要的是name,我们直接就可以在辅助索引的叶子节点找到对应的name值,比如:select name from tb1 where name=‘xx’;这个xx值你直接就在辅助索引的叶子节点就能找到,这种我们也可以称为覆盖索引。如果你select后面的字段不是name,例如:select age from tb1 where name=‘xx’;也就是说,我通过辅助索引的叶子节点不能直接拿到age的值,需要通过辅助索引的叶子节点中保存的主键id的值再去通过聚集索引来找到完整的一条记录,然后从这个记录里面拿出age的值,这种操作有时候也成为回表操作,就是从头再回去查一遍,这种的查询效率也很高,但是比覆盖索引低一些,再说一下昂,再辅助索引的叶子节点就能找到你想找的数据可称为覆盖索引。再看看下面的解释:

    ​ 由于InnoDB存储引擎是索引组织表,因此InnoDB存储引擎的辅助索引的书签就是相应行数据的聚集索引键或者称为主键的值。如下图

    辅助索引的存在并不影响数据在聚集索引中的组织,因此每张表上可以有多个辅助索引,但只能有一个聚集索引。当通过辅助索引来寻找数据时,InnoDB存储引擎会遍历辅助索引并通过叶子级别的指针获得只想主键索引的主键,然后再通过主键索引来找到一个完整的行记录,这种查找的效率也是非常高。

    举例来说,如果在一棵高度为3的辅助索引树种查找数据,那需要对这个辅助索引树遍历3次找到指定主键,如果聚集索引树的高度同样为3,那么还需要对聚集索引树进行3次查找,最终找到一个完整的行数据所在的页,因此一共需要6次逻辑IO访问才能得到最终的一个数据页。

    概念基本就说完了,下面我们来点实际操作吧。

    • 总结:

      辅助索引就是非主键索引,通过非主键索引字段作为查询条件,辅助索引叶子节点存放的是叶子节点存储的主键字段以及其余的索引字段信息,比如:我们user表中,id 为主键字段,name, age为索引字段,通过辅助索引查询时,叶子节点存储的就是id,name,age字段。

      • 覆盖索引

        通过索引字段作为条件,查询结果也是为索引字段的数据(即叶子节点存放的数据)。如果b+tree为3层,则通过3次IO就可以查到。

      select name from user where name='xxx';
  • 非覆盖索引

    通过索引字段作为条件,查询结果有非索引字段的数据(即叶子节点存放的数据),那么MySQL会根据主键字段再次通过聚集索引id进行回表查询,查询一遍,最终获取结果字段,这种就是非覆盖索引。如果B+tree为3层,则通过6次IO就可以查到。

      select name,sex from user where name='xxx';

ps:

插入300万条数据之后,我们可以进行测试效率:

  select count(*) from s1 where name ='barry';
  # 添加辅助索引name
  alter table s1 add index index_s1(name);
  select count(*) from s1 where name ='barry';

4.2.2 按照索引的特性进行分类

按照索引的效率分类:

聚集索引
辅助索引
	-- 覆盖索引
	-- 非覆盖索引  按照主键字段id进行回表查询

索引按照特性分类为:

普通索引INDEX:加速查找

唯一索引:
    -主键索引PRIMARY KEY:加速查找+约束(不为空、不能重复)
    -唯一索引UNIQUE: 加速查找+约束(不能重复)

联合索引:
    -PRIMARY KEY(id,name):联合主键索引
    -UNIQUE(id,name):联合唯一索引
    -INDEX(id,name):联合普通索引

语法

#方法一:创建表时
      CREATE TABLE 表名 (
                字段名1  数据类型 [完整性约束条件…],
                字段名2  数据类型 [完整性约束条件…],
                [UNIQUE | FULLTEXT | SPATIAL ]   INDEX | KEY
                [索引名]  (字段名[(长度)]  [ASC |DESC]) 
                );


#方法二:CREATE在已存在的表上创建索引
        CREATE  [UNIQUE | FULLTEXT | SPATIAL ]  INDEX  索引名 
                     ON 表名 (字段名[(长度)]  [ASC |DESC]) ;


#方法三:ALTER TABLE在已存在的表上创建索引
        ALTER TABLE 表名 ADD  [UNIQUE | FULLTEXT | SPATIAL ] INDEX
                             索引名 (字段名[(长度)]  [ASC |DESC]) ;
                             
#删除索引:DROP INDEX 索引名 ON 表名字;

5.索引管理

常用操作:

添加主键索引:
	创建的时候添加:  添加索引的时候要注意,给字段里面数据大小比较小的字段添加,给字段里面的数据区分度高的字段添加.
	聚集索引的添加方式
创建的是添加
Create table t1(
Id int primary key,
)
Create table t1(
Id int,
Primary key(id)
)

表创建完了之后添加
Alter table 表名 add primary key(id)
删除主键索引:
Alter table 表名 drop primary key;


唯一索引:
Create table t1(
Id int unique,
)

Create table t1(
Id int,
Unique key uni_name (id)
)

表创建好之后添加唯一索引:
alter table s1 add unique key  u_name(id);
删除:
Alter table s1 drop index u_name;

普通索引:
创建:
Create table t1(
Id int,
Index index_name(id)
)
Alter table s1 add index index_name(id);
Create index index_name on s1(id);

删除:
Alter table s1 drop index u_name;
DROP INDEX 索引名 ON 表名字;

简单示例

#方式一
create table t1(
    id int,
    name char,
    age int,
    sex enum('male','female'),
    unique key uni_id(id),
    index ix_name(name) #index没有key
);


#方式二
create index ix_age on t1(age);

#方式三
alter table t1 add index ix_sex(sex);

#查看
mysql> show create table t1;
| t1    | CREATE TABLE `t1` (
  `id` int(11) DEFAULT NULL,
  `name` char(1) DEFAULT NULL,
  `age` int(11) DEFAULT NULL,
  `sex` enum('male','female') DEFAULT NULL,
  UNIQUE KEY `uni_id` (`id`),
  KEY `ix_name` (`name`),
  KEY `ix_age` (`age`),
  KEY `ix_sex` (`sex`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1

如果你给一个表所有的字段都加入索引,这样在你插入数据时就会很慢,相当于你重写了一遍字典目录,所以索引不是越多越好,而是根据实际场景添加.

课上展示:

聚集索引的添加方式
创建的是添加
Create table t1(
Id int primary key,
)
Create table t1(
Id int,
Primary key(id)
)

表创建完了之后添加
Alter table 表名 add primary key(id)
删除主键索引:
Alter table 表名 drop primary key;


唯一索引:
Create table t1(
Id int unique,
)

Create table t1(
Id int,
Unique key uni_name (id)
)

表创建好之后添加唯一索引:
alter table s1 add unique key  u_name(id);
删除:
# 可以通过show create table t1; 查看
Alter table s1 drop index u_name;

普通索引:
创建:
Create table t1(
Id int,
Index index_name(id)
)
Alter table s1 add index index_name(id);
Create index index_name on s1(id);

删除:
Alter table s1 drop index u_name;
				DROP INDEX 索引名 ON 表名字;

6.索引测试

#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,'barry','male',concat('barry',i,'@pugongying'));
        set i=i+1;
    end while;
END$$ #$$结束
delimiter ; #重新声明分号为结束符号

#3. 查看存储过程
show create procedure auto_insert1\G 

#4. 调用存储过程
call auto_insert1();

情况一 有无索引时,测试速度

  • id没有索引的情况下,我们测试速度:
  mysql> select * from s1 where id = 2000000;
  +---------+-------+--------+-------------------------+
  | id      | name  | gender | email                   |
  +---------+-------+--------+-------------------------+
  | 2000000 | barry | male   | barry2000000@pugongying |
  +---------+-------+--------+-------------------------+
  1 row in set (1.47 sec)
  • 添加索引之后,测试速度
  mysql> alter table s1 add primary key(id);
  Query OK, 0 rows affected (8.06 sec)
  Records: 0  Duplicates: 0  Warnings: 0
  
  mysql> select * from s1 where id = 2000000;
  +---------+-------+--------+-------------------------+
  | id      | name  | gender | email                   |
  +---------+-------+--------+-------------------------+
  | 2000000 | barry | male   | barry2000000@pugongying |
  +---------+-------+--------+-------------------------+
  1 row in set (0.00 sec)

总结:

  #1. 一定是为搜索条件的字段创建索引,比如select * from s1 where id = 333;就需要为id加上索引
   
   #2. 在表中已经有大量数据的情况下,建索引会很慢,且占用硬盘空间,建完后查询速度加快
   比如create index idx on s1(id);会扫描表中所有的数据,然后以id为数据项,创建索引结构,存放于硬盘的表中。
   建完以后,再查询就会很快了。
   
   #3. 需要注意的是:innodb表的索引会存放于s1.ibd文件中,而myisam表的索引则会有单独的索引文件table1.MYI
   
   MySAM索引文件和数据文件是分离的,索引文件仅保存数据记录的地址。而在innodb中,表数据文件本身就是按照B+Tree(BTree即Balance True)组织的一个索引结构,这棵树的叶节点data域保存了完整的数据记录。这个索引的key是数据表的主键,因此innodb表数据文件本身就是主索引。
   因为inndob的数据文件要按照主键聚集,所以innodb要求表必须要有主键(Myisam可以没有),如果没有显式定义,则mysql系统会自动选择一个可以唯一标识数据记录的列作为主键,如果不存在这种列,则mysql会自动为innodb表生成一个隐含字段作为主键,这字段的长度为6个字节,类型为长整型.

你想一下,是不是全部加索引就一定好的呢,首先,我们加上索引,查询时快了,但是写入的时候就慢了,还记得吗,每次插入新的记录,你的整个索引结构都会跟着改,所以如果你乱加索引,你会发现,即便是你的网站没有几个人在注册,或者说没有几个写入数据的操作,你的磁盘IO会居高不下,磁盘在疯狂的转,因为你每插入一条数据,我们的索引都需要重新建,重建的索引要写入硬盘里面的,还记得我们给那三百万条数据建索引的时候的速度吗?每次都要把所有的数据取出来,做好一个数据结构,然后再写回硬盘,也就是要经历很多的IO才能实现这个事儿,所以乱加索引的一个弊端就是,你很少的写入都会导致你的磁盘IO非常的高,导致效率很差,所以我们要学一下怎么正确的加索引。

情况二 如何正确的使用索引

我们在使用索引字段进行查询时,并不是万无一失的,核心的思想是:使用索引字段进行查询,范围越小,查询的速度越快。

  1. 范围查询,范围越大查询速度越慢。
   mysql> select * from s1 where id = 2000000;  # 范围小
   +---------+-------+--------+-------------------------+
   | id      | name  | gender | email                   |
   +---------+-------+--------+-------------------------+
   | 2000000 | barry | male   | barry2000000@pugongying |
   +---------+-------+--------+-------------------------+
   1 row in set (0.00 sec)
   
   mysql> select count(*) from s1 where id > 2000;  # 范围大
   +----------+
   | count(*) |
   +----------+
   |  2998000 |
   +----------+
   1 row in set (0.82 sec)
   
   mysql> select count(*) from s1 where id != 200000;  # 范围大
   +----------+
   | count(*) |
   +----------+
   |  2999999 |
   +----------+
   1 row in set (0.81 sec)
   
   mysql> select count(*) from s1 where id >= 1000 and id <= 100000;  # 范围相对大
   +----------+
   | count(*) |
   +----------+
   |    99001 |
   +----------+
   1 row in set (0.03 sec)
   
   mysql> select count(*) from s1 where id >= 1000000 or id <= 100;  # 范围大
   +----------+
   | count(*) |
   +----------+
   |  2000101 |
   +----------+
   1 row in set (0.52 sec)
   
   mysql> select count(*) from s1 where id between 100 and 1000;  # 范围小
   +----------+
   | count(*) |
   +----------+
   |      901 |
   +----------+
   1 row in set (0.01 sec)
   
   mysql> select count(*) from s1 where id between 100 and 100000;  #范围大
   +----------+
   | count(*) |
   +----------+
   |    99901 |
   +----------+
   1 row in set (0.03 sec)

我们给email字段加一个普通索引index,我们测试一下like方法。

   create index index_email on  s1(email);
   
   # 范围小
   mysql> select count(*) from s1 where email like 'barry1000000@pugongying';  # 相当于等于
   +----------+
   | count(*) |
   +----------+
   |        1 |
   +----------+
   1 row in set (0.00 sec)
   
   # like 可以配合%与_  %放在最前面效率一定低。(会匹配所有行记录)
   mysql> select count(*) from s1 where email like 'barry2000000%';
   +----------+
   | count(*) |
   +----------+
   |        1 |
   +----------+
   1 row in set (0.00 sec)
   
   mysql> select count(*) from s1 where email like '%2000000@pugongying';
   +----------+
   | count(*) |
   +----------+
   |        1 |
   +----------+
   1 row in set (1.24 sec)


  1. 多条件查询
   mysql> select count(*) from s1 where name='barry' and gender='male';
   +----------+
   | count(*) |
   +----------+
   |  3000000 |
   +----------+
   1 row in set (1.23 sec)

   # sql语句如果使用and语句,所有条件都成立得到的最终结果,所以会优先按照索引字段进行查询。
   mysql> select count(*) from s1 where name='barry' and gender='male' and id = 3000000;
   +----------+
   | count(*) |
   +----------+
   |        1 |
   +----------+
   1 row in set (0.00 sec)
   # sql语句如果使用or语句,只要满足条件的,都获取到,从左到右依次按照每个条件获取结果。
   mysql> select count(*) from s1 where name='barry' or id = 3000000;
   +----------+
   | count(*) |
   +----------+
   |  3000000 |
   +----------+
   1 row in set (1.06 sec)
  1. 四则运算

    给字段使用四则运算,效率低。

   mysql> select * from s1 where id = 1000000/10;
   +--------+-------+--------+------------------------+
   | id     | name  | gender | email                  |
   +--------+-------+--------+------------------------+
   | 100000 | barry | male   | barry100000@pugongying |
   +--------+-------+--------+------------------------+
   1 row in set (0.00 sec)

   mysql> select * from s1 where id = 100000;
   +--------+-------+--------+------------------------+
   | id     | name  | gender | email                  |
   +--------+-------+--------+------------------------+
   | 100000 | barry | male   | barry100000@pugongying |
   +--------+-------+--------+------------------------+
   1 row in set (0.00 sec)

   mysql> select * from s1 where id*10 = 1000000;
   +--------+-------+--------+------------------------+
   | id     | name  | gender | email                  |
   +--------+-------+--------+------------------------+
   | 100000 | barry | male   | barry100000@pugongying |
   +--------+-------+--------+------------------------+
   1 row in set (1.12 sec)

我们上面的测试,是通过结果的秒数判断的,有些sql语句我们是拿捏不准的,所以,MySQL提供了一个功能:执行计划explain:让mysql预估执行操作(一般正确)

all < index < range < index_merge < ref_or_null < ref < eq_ref < system/const

相关测试:

explain select count(*) from s1 where id > 1000;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tvnjdmWf-1669260779907)(F:\newschool\笔记总结\MySQL笔记总结\assets\5174310F-3702-4314-B39D-E3111B0D8970.png)]

explain select count(*) from s1 where id = 1000000;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pIkrzbgU-1669260779907)(F:\newschool\笔记总结\MySQL笔记总结\assets\30482656-2B90-42D9-B5EE-57A50F6537C8.png)]

explain select * from s1 where id > 1000 limit 5;
# limit 提升了显示速度,但是并没有提升查询速度????

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4MR7viCC-1669260779908)(F:\newschool\笔记总结\MySQL笔记总结\assets\B7C13D98-7D89-478B-BF95-CCC4E9474318.png)]

查询优化步骤:

0.先运行看看是否真的很慢,注意设置SQL_NO_CACHE
1.where条件单表查,锁定最小返回记录表。这句话的意思是把查询语句的where都应用到表中返回的记录数最小的表开始查起,单表每个字段分别查询,看哪个字段的区分度最高
2.explain查看执行计划,是否与1预期一致(从锁定记录较少的表开始查询)
3.order by limit 形式的sql语句让排序的表优先查
4.了解业务方使用场景
5.加索引时参照建索引的几大原则
6.观察结果,不符合预期继续从0分析

7.慢日志管理配置

MySQL日志管理

错误日志: 记录 MySQL 服务器启动、关闭及运行错误等信息
二进制日志: 又称binlog日志,以二进制文件的方式记录数据库中除 SELECT 以外的操作
查询日志: 记录查询的信息
慢查询日志: 记录执行时间超过指定时间的操作
中继日志: 备库将主库的二进制日志复制到自己的中继日志中,从而在本地进行重放
通用日志: 审计哪个账号、在哪个时段、做了哪些事件

事务日志或称redo日志: 记录Innodb事务相关的如事务执行时间、检查点等

慢日志:项目运行中,肯定会涉及到很多的sql语句查询,这些sql语句的执行效率不定的,从而就会导致我们项目运行时尤其是在查询数据时变慢,但是我们又不知道是哪些sql语句致使查询数据变慢从而影响项目的效率,所以,我们就可以配置慢日志,在慢日志中,我们可以设一个一个时间,以及对应的慢日志的路径,假如我们设置的时间为0.01s,只要配置好慢日志,那么他就会实时监控所有的操作MySQL的sql语句,一旦有超过设置的时间长度的sql语句,马上记录到配置的慢日志中,以便后续的DBA去优化。

一、bin-log

1. 启用

vim /etc/myf

[mysqld]
log-bin[=dir\[filename]]

service mysqld restart

2. 暂停
   //仅当前会话
   SET SQL_LOG_BIN=0;
   SET SQL_LOG_BIN=1;
3. 查看
   查看全部:

mysqlbinlog mysql.000002

按时间:

mysqlbinlog mysql.000002 --start-datetime="2012-12-05 10:02:56"

mysqlbinlog mysql.000002 --stop-datetime="2012-12-05 11:02:54"

mysqlbinlog mysql.000002 --start-datetime="2012-12-05 10:02:56" --stop-datetime="2012-12-05 11:02:54" 

按字节数:

mysqlbinlog mysql.000002 --start-position=260

mysqlbinlog mysql.000002 --stop-position=260

mysqlbinlog mysql.000002 --start-position=260 --stop-position=930

4. 截断bin-log(产生新的bin-log文件)
   a. 重启mysql服务器
   b. # mysql -uroot -p123 -e 'flush logs'
5. 删除bin-log文件

mysql -uroot -p123 -e 'reset master' 


二、查询日志
启用通用查询日志

vim /etc/myf

[mysqld]
log[=dir\[filename]]

service mysqld restart

三、慢查询日志
启用慢查询日志

vim /etc/myf

[mysqld]
log-slow-queries[=dir\[filename]]
long_query_time=n  # 设置的时间

service mysqld restart

MySQL 5.6:
slow-query-log=1
slow-query-log-file=slow.log  # 设置的慢日志的路径
long_query_time=3
查看慢查询日志
测试:BENCHMARK(count,expr)
SELECT BENCHMARK(50000000,2*3);

8.用户的创建权限配置

我们知道我们的最高权限管理者是root用户,它拥有着最高的权限操作。包括select、update、delete、update、grant等操作。那么一般情况在公司之后DBA工程师会创建一个用户和密码,让你去连接数据库的操作,并给当前的用户设置某个操作的权限(或者所有权限)。那么这时就需要我们来简单了解一下:

  • 如何创建用户和密码
  • 给当前的用户授权
  • 移除当前用户的权限

如果你想创建一个新的用户,则需要以下操作:

操作新用户

利用root用户进入mysql数据库服务端的mysql库进行下列操作:

   1.创建用户:不仅需要用户名,密码还需要你电脑的(客户端)ip地址。

指定ip:192.118.1.1的chao用户登录(客户端的ip地址)

   create user 'chao'@'192.118.1.1' identified by '123';

指定ip:192.118.1.开头的chao用户登录:同一网段的用户密码都可以连接

   create user 'chao'@'192.118.1.%' identified by '123';

指定任何ip的chao用户登录

   create user 'chao'@'%' identified by '123';

   2.删除用户
   drop user '用户名'@'IP地址';


   3.修改用户
   rename user '用户名'@'IP地址' to '新用户名'@'IP地址';

   4.修改密码
   set password for '用户名'@'IP地址'=Password('新密码');

   5.查看所有的用户:
   SELECT DISTINCT CONCAT('User: ''',user,'''@''',host,''';') AS query FROM mysql.user;

      6. 刷新动作
         flush privileges;
   一般公司中都会做限制,只有内网才可以连接数据库。

权限操作

   #查看权限

   show grants for '用户'@'IP地址'

   #授权 chao用户仅对db1.t1文件有查询、插入和更新的操作

   grant select ,insert,update on db1.t1 to "chao"@'%';

   # 表示有所有的权限,除了grant这个命令,这个命令是root才有的。chao用户对db1下的t1文件有任意操作

   grant all privileges  on db1.t1 to "chao"@'%';

   #chao用户对db1数据库中的文件执行任何操作

   grant all privileges  on db1.* to "chao"@'%';

   #chao用户对所有数据库中文件有任何操作

   grant all privileges  on *.*  to "chao"@'%';
    

   #取消权限

   # 取消chao用户对db1的t1文件的任意操作

   revoke all on db1.t1 from 'chao'@"%";  

取消来自远程服务器的chao用户对数据库db1的所有表的所有权限

   revoke all on db1.* from 'chao'@"%";  

   取消来自远程服务器的chao用户所有数据库的所有的表的权限
   revoke all privileges on *.* from 'chao'@'%';

9.数据库的备份

1.首先我们先创建一个名为crm2的库

    mysql> create database crm2;
    mysql> show create database crm2;
  2.切换到crm2库下
    mysql> use crm2;
  3.创建两张表,student表和class表
    mysql> create table tb1(id int primary key,name char(8) not null,age int,class_id int not null);
    Query OK, 0 rows affected (0.63 sec)

    mysql> create table class(id int primary key,cname char(20) not null);
    Query OK, 0 rows affected (0.34 sec)

  4.给两张表插入一些数据

    mysql> insert into class values(1,'一班'),(2,'二班');
    mysql> insert into student values(1,'Jaden',18,1),(2,'太白',45,1),(3,'彦涛',30,2);

  5.查看一下两个表的数据
    mysql> select * from student;
    +----+--------+------+----------+
    | id | name | age | class_id |
    +----+--------+------+----------+
    | 1 | Jaden | 18 | 1 |
    | 2 | 太白 | 45 | 1 |
    | 3 | 彦涛 | 30 | 2 |
    +----+--------+------+----------+
    3 rows in set (0.00 sec)

    mysql> select * from class;
    +----+--------+
    | id | cname |
    +----+--------+
    | 1 | 一班 |
    | 2 | 二班 |
    +----+--------+
    2 rows in set (0.00 sec)
  • 备份单个数据库

    我们将这个db2数据库通过命令导出,mysqldump,不要进入到mysql客户端,直接在cmd命令行中输入:

  mysqldump -h ip地址 -P 端口号 -u 用户名 -p 密码 数据库 > .sql文件的路径

  mysqldump  -u root -B -p db3 > /Users/barry/Desktop/db3.sql
  • 备份多个数据库

    • 方式一(不常用)
    mysqldump -h ip地址 -P 端口号 -u 用户名 -p 密码 数据库1 数据库2 数据库n > .sql文件的路径

一般我们备份多个数据库是要一个库一个sql文件,但是我们又不能很low 一个一个去备份。工作中一般都会写一个shell脚本,循环遍历数据库的名字,遍历一个备份一个,自动化备份。这个就是方式二。

  • 方式二

    利用shell脚本,循环遍历数据库的所有数据库的名字,然后通过命令备份。

  • 备份表

    • 单个表
    mysqldump -h ip地址 -P 端口号 -u 用户名 -p 密码 数据库1 表名 > .sql文件的路径

  • 多个表
    mysqldump -h ip地址 -P 端口号 -u 用户名 -p 密码 数据库1 表名1 表名2 > .sql文件的路径

mysqldump的关键参数说明:

  		  1.-B指定多个库,增加建库语句和use 语句
        2.--compact 去掉注释,适合调试输出,生产上不用
        3.-A或者--all-databases
          例如:C:\WINDOWS\system32>mysqldump -uroot -p -B -A> f:\数据库备份练习\all.sql
          Enter password: ***

        4.-F刷新binlog日志(binlog具体是什么,后面咱们再解释)
        5.--master-data 增加binlog日志文件名及对应的为支点。
        6.-x,--lock-all-tables 将所有的表锁住,一般mysql引擎都是锁表,全部都不能使用了,所有不太友好

        7.--add-locks这个选项会在INSERT语句中捆上一个LOCK TABLE和UNLOCK TABLE语句。这就防止在这些记录被再次导入数据库时其他用户对表进行的操作(mysql默认是加上的)
        8.-l,--lock-tables Lock all tables for read
        9.-d 只备份表结构
        10.-t 只备份数据
        11. --single-transaction 开启事务,适合innodb事务数据库备份
          InnoDB表在备份时,通常启用选项--single-transaction来保证备份的一致性,实际上他的工作原理时设定本次会话的隔离界别为:REPEATABLE READ,以确保本次会话(dump)时,不会看到其他会话已经提交了数据。

          MyISAM全库备份指令推荐:(gzip是压缩文件为zip类型的)
          mysqldump -uroot -p666 -A -B --master-data=2 -x|gzip>f:\数据库备份练习\all.sql.gz
          InnoDB全库备份指令推荐:
          mysqldump -uroot -p666 -A -B --master-data=2 --single-transaction|gzip>f:\数据库备份练习\all.sql.gz

恢复数据库

  一、通过source命令恢复数据库
      进入mysql数据库控制台,mysql -uroot -p666登陆后
      mysql>use 数据库;
      然后使用source命令,后面参数为脚本文件(如这里用到的是.sql文件,如果你备份的是.txt文件,那这里写.txt文件)
  
      mysql>source crm2.sql #这个文件是系统路径下的,默认是登陆mysql前的系统路径,在mysql中查看系统路径的方法是通过system+系统命令来搞的
      mysql>system ls
  二、利用mysql命名恢复(标准)
      mysql -uroot -p666 -e "use crm2;drop table student;show tables;" 必须是双引号
      mysql -uroot -p666 <f:\数据库备份练习\crm2.sql
      mysql -uroot -p666 -e "use crm2;show tables;"
  
      注:如果sql文件里面没有use db这样的字样时,在导入时就要指定数据库名了。
  
      mysql -uroot -p666 crm2<.sql文件
  
      建议备份数据库时都指定上-B参数,效果好
  
      说明:mysql不光可以恢复mysqldump的备份,只要文件中是sql语句,都可以通过mysql命令执行到数据库中
  
      mysql 带-e参数实现非交互式对话,就是不需要到mysql里面去,在外面执行里面的指令的方法,例如:mysql -uroot -p666 -e "use crm2;show tables;",但是语句必须是双引号包裹。


      批量恢复库:找到所有的数据库名,然后通过库名去循环恢复

我们先把db3数据库删除。

   mysql -uroot -p </Users/barry/Desktop/db3.sql
  • 恢复表
  mysql -uroot -p t1 < /Users/barry/Desktop/stu.sql

2.数据库的备份读写分离(理论)

数据库的备份可以按照多种角度去分类:

备份方式的角度:

  • 物理备份。(简单粗暴的将数据库这些文件夹保存。)
  • 逻辑备份。(我们使用mysqldump命令将创建数据库以及表的所有sql语句导出到一个sql文件中)

按照时间来备份:

  • 整备。定期备份,一周,一个月,三个月,半年将我们的数据库备份一次。
  • 实时(增量)备份。使用binlog日志。在my.ini配置文件中配置binlog日志。

主从复制

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WLuChb9p-1669260779909)(F:\newschool\笔记总结\MySQL笔记总结\assets\77D90F61-9A21-4CEB-B639-DD81B3AABAEF.png)]

读写分类

如果主库处理所有的业务,增删改查,一旦用户量访问过多,业务过多,数据量大等等,一个主库是不能够应对所有的操作的,我们一般会采用读写分离这种方式。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fIsHu7yM-1669260779910)(F:\newschool\笔记总结\MySQL笔记总结\assets\4A2FEDF3-8BD5-41A3-8D15-10AA7C46EE2F.png)]

主从复制与读写分类基本上都是一起配置的。

3.数据库的高可用和集群(理论)

数据库高可用:

主库负责的写的业务,如果某时间段比如双十一,访问量剧增,或者由于主库所在的服务器出现问题,导致主库出问题了,数据就不能增删改等操作, 这样就会致使你的网站、项目停了,用户体验感很不好,所以,我们应该解决主库一旦发生问题,从库马上将所有的业务切过来,继续让项目执行,我们就需要在主库部署类似于一个实时监控的系统,实时监听主库的情况,一旦出问题给从库发信号,让从库接受所有的业务。数据库的高可用就是实现这种监控系统,解决上述问题。

  • Heartbeat drbd。
  • keepalive lvs。
  • docker + k8s。

数据库的集群:

如果某时间段比如双十一,访问量剧增,我们一台服务器上的一个数据库是应付不来的。我们需要部署多个数据库。两个难点:

  • 访问量如何平均分配。(涉及到算法或者权重,尽量做到平均分配)
  • 这些数据库的数据肯定要同步。

4.视图

视图是一个虚拟表(非真实存在),是跑到内存中的表,真实表是硬盘上的表,怎么就得到了虚拟表,就是你查询的结果,只不过之前我们查询出来的虚拟表,从内存中取出来显示在屏幕上,内存中就没有了这些表的数据,但是下次我要是想用这个虚拟表呢,没办法,只能重新查一次,每次都要重新查。其本质是【根据SQL语句获取动态的数据集,并为其命名】,用户使用时只需使用【名称】即可获取结果集,可以将该结果集当做表来使用。如果我们想查询一些有关联的表,比如我们前面的老师学生班级什么的表,我可能需要几个表联合查询的结果,但是这几张表在硬盘上是单独存的,所以我们需要通过查询的手段,将这些表在内存中拼成一个虚拟表,然后是不是我们再基于虚拟表在进行进一步的查询,然后我们如果以后想重新再查一下这些关系数据,还需要对硬盘上这些表进行再次的重新加载到内容,联合成虚拟表,然后再筛选等等的操作,意味着咱们每次都在写重复的sql语句,那有没有好的方法啊,其实很简单,我们把重复用的这些sql逻辑封装起来,然后下次使用的时候直接调用这个封装好的操作就可以了,这个封装起来的操作就类似我们下面要说的视图

为什么要用视图:使用视图我们可以把查询过程中的临时表摘出来,保存下来,用视图去实现,这样以后再想操作该临时表的数据时就无需重写复杂的sql了,直接去视图中查找即可,但视图有明显地效率问题,并且视图是存放在数据库中的,如果我们程序中使用的sql过分依赖数据库中的视图,即强耦合,那就意味着扩展sql极为不便,因此并不推荐使用

总结:

视图是一张虚拟表,将我们在查询过程中产生了虚拟表临时保存在内存中,以便我们在后续的sql语句的查询时,我们随时可使用。如果没有视图这个概念,我们在使用之前的虚拟表时,需要将其sql语句复制过来,但是有了视图我们直接使用这个虚拟表的表名即可。

视图慎用:因为如果我们对视图形成的虚拟表进行增删改数据,有可能会对原真实表进行破坏。

临时表应用举例:

两张有关系的表

mysql> select * from course;
+-----+--------+------------+
| cid | cname  | teacher_id |
+-----+--------+------------+
|   1 | 生物   |          1 |
|   2 | 物理   |          2 |
|   3 | 体育   |          3 |
|   4 | 美术   |          2 |
+-----+--------+------------+
4 rows in set (0.00 sec)

mysql> select * from teacher;
+-----+-----------------+
| tid | tname           |
+-----+-----------------+
|   1 | 张磊老师        |
|   2 | 李平老师        |
|   3 | 刘海燕老师      |
|   4 | 朱云海老师      |
|   5 | 李杰老师        |
+-----+-----------------+
5 rows in set (0.00 sec)

#查询李平老师教授的课程名

mysql> select cname from course where teacher_id = (select tid from teacher where tname='李平老师'); #子查询的方式

+--------+
| cname  |
+--------+
| 物理   |
| 美术   |
+--------+
2 rows in set (0.00 sec)

#子查询出临时表,作为teacher_id等判断依据

select tid from teacher where tname='李平老师'
注意:但是在硬盘上你找到自己的mysql安装目录里面的data文件夹里面的对应的那个库的文件夹,这个文件夹里面存着咱们的表信息,打开之后你会发现,这个视图表,只有表结构的teacher_view.frm文件,没有那个.idb存放数据的文件
其实他并没有真实的数据,也没有必要再存一份数据,因为它的数据来源于其他两个表,所以他本质在后台对应的就是一个sql语句而已,所以记住了,视图只有表结构,没有表数据
视图的好处是以后我们如果再需要查询或者使用上面的虚拟表,就可以直接使用这个视图了,sql的代码量也会省很多。但是弊端也很致命,看下面注意的内容。
  • 注意:
  1. 使用视图以后就无需每次都重写子查询的sql,开发的时候是方便了很多,但是这么效率并不高,还不如我们写子查询的效率高

  2. 而且有一个致命的问题:视图是存放到数据库里的,如果我们程序中的sql过分依赖于数据库中存放的视图,那么意味着,一旦sql需要修改且涉及到视图的部分,则必须去数据库中进行修改,然后再到自己的应用程序里面将那个sql语句改一改,需要很多的修改工作,并而对视图的更改通常在一般中型及以上公司中数据库有专门的DBA负责,你要想完成修改,必须付出大量的沟通成本DBA可能才会帮你完成修改,极其地不方便

    这么多的弊端,为什么mysql还要提供这个东西呢,有一点是因为mysql想把所有数据处理的工作全部接手过来,但其实还有其他的原因,等我们讲完存储过程在和大家说吧。

  3. 并且注意:视图一般都是用于查询,尽量不要修改(插入、删除等)视图中的数据,虽然有时候可以修改成功,但是尽量不要这样做,因为这个视图可能是多个表联合起来生成的一个结果,如果你修改它,可能会造成很多表里面的数据都跟着被修改了

二 使用视图

#修改视图,原始表也跟着改

mysql> select * from course;
+-----+--------+------------+
| cid | cname  | teacher_id |
+-----+--------+------------+
|   1 | 生物   |          1 |
|   2 | 物理   |          2 |
|   3 | 体育   |          3 |
|   4 | 美术   |          2 |
+-----+--------+------------+
4 rows in set (0.00 sec)

mysql> create view course_view as select * from course; #创建表course的视图
Query OK, 0 rows affected (0.52 sec)

mysql> select * from course_view;
+-----+--------+------------+
| cid | cname  | teacher_id |
+-----+--------+------------+
|   1 | 生物   |          1 |
|   2 | 物理   |          2 |
|   3 | 体育   |          3 |
|   4 | 美术   |          2 |
+-----+--------+------------+
4 rows in set (0.00 sec)

mysql> update course_view set cname='xxx'; #更新视图中的数据
Query OK, 4 rows affected (0.04 sec)
Rows matched: 4  Changed: 4  Warnings: 0

mysql> insert into course_view values(5,'yyy',2); #往视图中插入数据
Query OK, 1 row affected (0.03 sec)

mysql> select * from course; #发现原始表的记录也跟着修改了
+-----+-------+------------+
| cid | cname | teacher_id |
+-----+-------+------------+
|   1 | xxx   |          1 |
|   2 | xxx   |          2 |
|   3 | xxx   |          3 |
|   4 | xxx   |          2 |
|   5 | yyy   |          2 |
+-----+-------+------------+
5 rows in set (0.00 sec)

三 修改视图

语法:ALTER VIEW 视图名称 AS SQL语句,这基本就和删掉视图重新创建一个视图的过程是一样的,修改视图没什么好讲的,这里就简单提一下,就不讲啦~~,还不如我们直接删掉,再重新创建呢
mysql> alter view teacher_view as select * from course where cid>3;
Query OK, 0 rows affected (0.04 sec)

mysql> select * from teacher_view;
+-----+-------+------------+
| cid | cname | teacher_id |
+-----+-------+------------+
|   4 | xxx   |          2 |
|   5 | yyy   |          2 |
+-----+-------+------------+
2 rows in set (0.00 sec)

四 删除视图

语法:DROP VIEW 视图名称

DROP VIEW teacher_view

五 查看视图

查看视图
  SELECT * from information_schema.VIEWS   //视图
  SELECT * from information_schema.TABLES   //表

总结:

  • 如果我们想要使用视图,一定只能对视图进行查询,不要对视图进行增删改的操作。

5.触发器

使用触发器可以定制用户对某一张表的数据进行【增、删、改】操作时前后的行为,注意:没有查询,在进行增删改操作的时候,触发的某个操作,称为触发器,也就是增删改的行为触发另外的一种行为,触发的行为无非就是sql语句的事情,及自动运行另外一段sql语句。来看一下触发器怎么来创建:

语法

   # 插入前

   CREATE TRIGGER tri_before_insert_tb1 BEFORE INSERT ON tb1 FOR EACH ROW
   BEGIN #begin和end里面写触发器要做的sql事情,注意里面的代码缩进,并且给触发器起名字的时候,名字的格式最好这样写,有表示意义,一看名字就知道要做什么,是给哪个表设置的触发器
       ...
   END

   # 插入后

   CREATE TRIGGER tri_after_insert_tb1 AFTER INSERT ON tb1 FOR EACH ROW
   BEGIN
       ...
   END

   # 删除前

   CREATE TRIGGER tri_before_delete_tb1 BEFORE DELETE ON tb1 FOR EACH ROW
   BEGIN
       ...
   END

   # 删除后

   CREATE TRIGGER tri_after_delete_tb1 AFTER DELETE ON tb1 FOR EACH ROW
   BEGIN
       ...
   END

   # 更新前

   CREATE TRIGGER tri_before_update_tb1 BEFORE UPDATE ON tb1 FOR EACH ROW
   BEGIN
       ...
   END

   # 更新后

   CREATE TRIGGER tri_after_update_tb1 AFTER UPDATE ON tb1 FOR EACH ROW
   BEGIN
       ...
   END

测试

   #准备表

   CREATE TABLE cmd (  #这是一张指令信息表,你在系统里面执行的任何的系统命令都在表里面写一条记录
       id INT PRIMARY KEY auto_increment,  #id
       USER CHAR (32),  #用户
       priv CHAR (10),  #权限          
       cmd CHAR (64),   #指令
       sub_time datetime, #提交时间  
       success enum ('yes', 'no') #是否执行成功,0代表执行失败
   );

   CREATE TABLE errlog ( #指令执行错误的信息统计表,专门提取上面cmd表的错误记录
       id INT PRIMARY KEY auto_increment, #id
       err_cmd CHAR (64),  #错误指令
       err_time datetime   #错误命令的提交时间
   );

   #现在的需求是:不管正确或者错误的cmd,都需要往cmd表里面插入,然后,如果是错误的记录,还需要往errlog表里面插入一条记录

   #若果没有触发器,我们会怎么实现,我们完全可以通过咱们的应用程序来做,根据cmd表里面的success这个字段是哪个值(yes成功,no表示失败),在给cmd插入记录的时候,判断一下这个值是yes或者no,来判断一下成功或者失败,如果失败了,直接给errlog来插入一条记录

   #但是mysql说,你的应用程序可以省事儿了,你只需要往cmd表里面插入数据就行了,没必要你自己来判断了,可以使用触发器来实现,可以判断你插入的这条记录的success这个字段对应的值,然后自动来触发触发器,进行errlog表的数据插入

   #创建触发器

   delimiter //      (或者写$$,其他符号也行,但是不要写mysql不能认识的,知道一下就行了),delimiter 是告诉mysql,遇到这句话的时候,就将sql语句的结束符分号改成delimiter后面的//
   
   CREATE TRIGGER tri_after_insert_cmd AFTER INSERT ON cmd FOR EACH ROW              
   #在你cmd表插入一条记录之后触发的。
   BEGIN           
   #每次给cmd插入一条记录的时候,都会被mysql封装成一个对象,叫做NEW,里面的字段都是这个NEW的属性
       IF NEW.success = 'no' THEN           
       #mysql里面是可以写这种判断的,等值判断只有一个等号,然后写then
  	INSERT INTO errlog(err_cmd, err_time) VALUES(NEW.cmd, NEW.sub_time) ;     
  #必须加分号,并且注意,我们必须用delimiter来包裹,不然,mysql一看到分号,就认为你的sql结束了,所以会报错
      END IF ;       #然后写end if,必须加分号  
   END  //      #只有遇到//这个完成的sql才算结束
   delimiter ;        #然后将mysql的结束符改回为分号
   往表cmd中插入记录,触发触发器,根据IF的条件决定是否插入错误日志
   INSERT INTO cmd (

     USER,
     priv,
     cmd,
     sub_time,
     success
 )
 VALUES
     ('chao','0755','ls -l /etc',NOW(),'yes'),
     ('chao','0755','cat /etc/passwd',NOW(),'no'),
     ('chao','0755','useradd xxx',NOW(),'no'),
     ('chao','0755','ps aux',NOW(),'yes');

# 查询错误日志,发现有两条
 mysql> select * from errlog;
  +----+-----------------+---------------------+
 | id | err_cmd         | err_time            |
 +----+-----------------+---------------------+
 |  1 | cat /etc/passwd | 2017-09-14 22:18:48 |
 |  2 | useradd xxx     | 2017-09-14 22:18:48 |
 +----+-----------------+---------------------+
 rows in set (0.00 sec)

6.存储过程

存储过程,就是将把sql语句、视图、触发器、事务、if、while等等全部都封装到存储过程中去,相当于python中的类,可以封装方法、属性、while等等。他是想将mysql所有的关于数据操作的内容自己处理,不需要你应用程序去处理。存储过程存储在服务端,应用程序调用即可。sql开发程序猿就是专门写存储过程,构建复杂的sql语句等等,我们开发程序猿连接上mysql服务端直接调用这个存储过程即可。

存储过程包含了一系列可执行的sql语句,存储过程存放于MySQL中,通过调用它的名字可以执行其内部的一堆sql。到目前为止,我们上面学的视图、触发器、事务等为我们简化了应用程序级别写sql语句的复杂程度,让我们在应用程序里面写sql更简单方便了,但是我们在应用程序上还是需要自己写sql的,而我们下面要学的存储过程,它是想让我们的应用程序不需要再写sql语句了,所有的sql语句,全部放到mysql里面,被mysql封装成存储过程,说白了它就是一个功能,这个功能对应着一大堆的sql语句,这些语句里面可以包括我们前面学的视图啊、触发器啊、事务啊、等等的内容,也就是说存储过程其实是什么?是一堆sql的集合体,可以直接用mysql里面提供的一堆功能,有了存储过程以后,它的好处是我项目逻辑中需要的各种查询都可以让DBA或者你自己封装到存储过程里面,以后使用的时候直接调用存储过程名就可以了,在开发应用的时候就简单了,就不要应用程序员进行sql语句的开发了,但是你想如果你真的这么做了,确实很有好处,简单很多,应用程序的开发和数据库sql语句的开发,完全的解耦了,这样,专门的人做专门的事情,专门招一个应用开发的人开发应用程序,招一个开发型DBA,会sql的开发,他把sql写完之后,封装成一个个的存储过程,给应用程序员用就行了,对不对,这个DBA就不单纯的是管理数据库系统了,还需要会写sql语句,那这样你的应用程序开发的效率就高了,运行效率也提高了,你开发应用程序的时候如果写了一堆的sql语句,这些语句是不是要通过网络传输,传输到mysql服务端来执行,然后将结果返回给你的应用程序,那么在传输的时候,你说好多的sql语句和简单的一个存储过程的名字,哪个传输的速度快,哪个发送给服务端的速度快,当然是单纯的一个存储过程的名字更快。

总结:

简单介绍:
存储过程类似于python中的函数,一个存储过程包含多组复杂的sql语句。DBA创建存储过程,保存在MySQL的服务端。
我们开发人员使用时,在客户端直接输入调用存储过程的指令,发送到MySQL的服务端,MySQL的服务端解析指令,执行对应的存储过程得到最终的结果,返回给我们的客户端。

现在后端的开发模式有如下几种:

  • 后端开发兼任数据库开发。有可能sql语句写的一般,但是整体项目开发效率高。 “装逼型”公司。
  • 后端开发+DBA开发。一般存在于大公司,看起来可能效率更高,但是浪费人与人之间的沟通成本上,实际上这样配合效率并不高。大公司。
  • 后端开发 + ORM。后端人员更倾向于写逻辑代码,ORM是一个对象关系映射系统,它提供了很多python方法,我们只需学会这些python方法即可,通过ORM底层就会转化成相应的sql语句(sql语句的效率一般)。中小型公司。

创建简单的存储过程(无参)

delimiter //
create procedure p1()
begin
select * from emp;
insert into user(username, password) values('硕哥', '123');
end //
delimiter ;

通过 call p1(); 调用存储过程

# 在python中基于pymysql调用
    cursor.callproc('p1') 
    print(cursor.fetchall())

创建存储过程(有参)

三种类型参数:

   对于存储过程,可以接收参数,其参数有三类:

# in    仅用于传入参数用
# out   仅用于返回值用
# inout  既可以传入又可以当作返回值
  • in 参数

    delimiter //
    create procedure p2(in n1 int, in n2 int)
    begin
    select * from user where id between n1 and n2;
    end //
    delimiter ;
    
    mysql> call p2(1,5);
    +----+----------+----------+
    | id | username | password |
    +----+----------+----------+
    |  1 | barry    | 123      |
    |  2 | yaocong  | 123      |
    |  3 | zhili    | 123      |
    |  4 | minghang | 123      |
    |  5 | shuhong  | 123123   |
    +----+----------+----------+
    5 rows in set (0.01 sec)
    
    #在python中基于pymysql调用
        cursor.callproc('p2',(3,2))
        print(cursor.fetchall())
    
  • out 参数

    在MySQL中,我们也可以设置变量,设置的变量一定是以@开头。

    mysql> set @n = 100;
    Query OK, 0 rows affected (0.00 sec)
    
    mysql> select @n;
    +------+
    | @n   |
    +------+
    |  100 |
    +------+
    1 row in set (0.00 sec)
    

    out参数可以修改我们全局的变量。

    delimiter //
    create procedure p3(in n1 int, out n2 int)
    begin
    select * from emp where id > n1;
    set n2 = 666;
    end //
    delimiter ;
    
    mysql> call p3(5,@n);
    Empty set (0.00 sec)
    
    Query OK, 0 rows affected (0.00 sec)
    
    mysql> select @n;
    +------+
    | @n   |
    +------+
    |  666 |
    +------+
    1 row in set (0.00 sec)
     #在python中基于pymysql调用,在python中只需要知道存储过程的名字就行了
     cursor.callproc('p3',(3,0)) 
     #0相当于set @res=0,为什么这里这个out参数可以写常数0啊,因为你用的pymysql,人家会帮你搞定,pymysql其实会帮你写成这样:第一个参数变量名:@_p3_0=3,第二个:@_p3_1=0,也就是pymysql会自动帮你对应上一个变量名,pymysql只是想让你写的时候更方便
     #沿着网络将存储过程名和参数发给了mysql服务端,比咱们发一堆的sql语句肯定要快对了,mysql帮你调用存储过程
     print(cursor.fetchall()) #查询select的查询结果
    
     cursor.execute('select @_p3_0,@_p3_1;') #@_p3_0代表第一个参数,@_p3_1代表第二个参数,即返回值
     print(cursor.fetchall())
     #别忘了关掉:
     cursor.close()
     conn.close()
     #注意昂:存储过程在哪个库里面建的,就只能在哪个库里面用
    
  • inout

    delimiter //
    create procedure p4(
        inout n1 int
    )
    BEGIN
        # select * from blog where id > n1;
        set n1 = 1;
    END //
    delimiter ;
    
  • 存储过程相关sql语句

    # 查看所有的存储过程
    select SPECIFIC_NAME from information_schema.parameters ;
    
    # 查看某个存储过程的状态
    SHOW CREATE PROCEDURE procedure_name;
    
    

删除存储过程

drop procedure procedure_name;

7.事务

原子性操作,一组sql语句,要不全成功,要不全失败。

事务用于将某些操作的多个SQL作为原子性操作,也就是这些sql语句要么同时成功,要么都不成功,一旦有某一个出现错误,即可回滚到原来的状态,从而保证数据库数据完整性。

没有开启事务,可能会出现的问题:

mysql> create table user(
    -> id int primary key auto_increment,
    -> name char(32),
    -> balance int
    -> );
Query OK, 0 rows affected (0.03 sec)

mysql> insert into user(name,balance)
    -> values
    -> ('菜籽',1000),
    -> ('明航',1000),
    -> ('小涛子',1000);
Query OK, 3 rows affected (0.00 sec)
Records: 3  Duplicates: 0  Warnings: 0

mysql> 
mysql> 
mysql> select * from user;
+----+-----------+---------+
| id | name      | balance |
+----+-----------+---------+
|  1 | 菜籽      |    1000 |
|  2 | 明航      |    1000 |
|  3 | 小涛子    |    1000 |
+----+-----------+---------+
3 rows in set (0.00 sec)

mysql> update user set balance=1100 where name='菜籽';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> update user set balance=1100 where name='小涛子';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> update user1 set balance=800 where name='明航';
ERROR 1146 (42S02): Table 'db2.user1' doesn't exist
mysql> 
mysql> 
mysql> select * from user;
+----+-----------+---------+
| id | name      | balance |
+----+-----------+---------+
|  1 | 菜籽      |    1100 |
|  2 | 明航      |    1000 |
|  3 | 小涛子    |    1100 |
+----+-----------+---------+
3 rows in set (0.00 sec)

像这种一组sql语句,要不全成功要不全失败,我们应该加上事务:

create table user(
id int primary key auto_increment,
name char(32),
balance int
);

insert into user(name,balance)
values
('菜籽',1000),
('明航',1000),
('小涛子',1000);

# 原子操作

start transaction;
update user set balance=900 where name='wsb'; #买支付100元
update user set balance=1010 where name='chao'; #中介拿走10元
update user set balance=1090 where name='ysb'; #卖家拿到90元
commit;  #只要不进行commit操作,就没有保存下来,没有刷到硬盘上

# 出现异常,回滚到初始状态

start transaction;
update user set balance=900 where name='wsb'; #买支付100元
update user set balance=1010 where name='chao'; #中介拿走10元
uppdate user set balance=1090 where name1='ysb'; #卖家拿到90元,出现异常没有拿到
rollback;  

# 如果上面三个sql语句出现了异常,就直接rollback,数据就直接回到原来的状态了。但是执行了commit之后,rollback这个操作就没法回滚了

# 我们要做的是检测这几个sql语句是否异常,没有异常直接commit,有异常就rollback,但是现在单纯的只是开启了事务,但是还没有说如何检测异常,我们先来一个存储过程来捕获异常,等我们学了存储过程,再细说存储过程。

commit;

如果我们需要再MySQL中构建事务,一般都是结合存储过程以及异常处理去构建的:

delimiter //
create PROCEDURE p15()
BEGIN 
DECLARE exit handler for sqlexception   
BEGIN  
rollback;  
END; 
START TRANSACTION;  
update user set balance=900 where name='wsb'; #买支付100元
update user set balance=1010 where name='chao'; #中介拿走10元

# update user2 set balance=1090 where name='ysb'; #卖家拿到90元

update user set balance=1090 where name='ysb'; #卖家拿到90元
COMMIT; 
END //
delimiter ;

mysql> select * from user;
+----+------+---------+
| id | name | balance |
+----+------+---------+
|  1 | wsb  |    1000 |
|  2 | chao |    1000 |
|  3 | ysb  |    1000 |
+----+------+---------+
rows in set (0.00 sec)

8.函数

类似于内置函数

一、数学函数

ROUND(x,y)
    返回参数x的四舍五入的有y位小数的值
    
RAND()
    返回0到1内的随机值,可以通过提供一个参数(种子)使RAND()随机数生成器生成一个指定的值。

二、聚合函数(常用于GROUP BY从句的SELECT查询中)

AVG(col)返回指定列的平均值
COUNT(col)返回指定列中非NULL值的个数
MIN(col)返回指定列的最小值
MAX(col)返回指定列的最大值
SUM(col)返回指定列的所有值之和
GROUP_CONCAT(col) 返回由属于一组的列值连接组合而成的结果    

三、字符串函数

CHAR_LENGTH(str)
    返回值为字符串str 的长度,长度的单位为字符。一个多字节字符算作一个单字符。
CONCAT(str1,str2,...)
    字符串拼接
    如有任何一个参数为NULL ,则返回值为 NULL。
CONCAT_WS(separator,str1,str2,...)
    字符串拼接(自定义连接符)
    CONCAT_WS()不会忽略任何空字符串。 (然而会忽略所有的 NULL)。

CONV(N,from_base,to_base)
    进制转换
    例如:
        SELECT CONV('a',16,2); 表示将 a 由16进制转换为2进制字符串表示

FORMAT(X,D)
    将数字X 的格式写为'#,###,###.##',以四舍五入的方式保留小数点后 D 位, 并将结果以字符串的形式返回。若  D 为 0, 则返回结果不带有小数点,或不含小数部分。
    例如:
        SELECT FORMAT(12332.1,4); 结果为: '12,332.1000'
INSERT(str,pos,len,newstr)
    在str的指定位置插入字符串
        pos:要替换位置其实位置
        len:替换的长度
        newstr:新字符串
    特别的:
        如果pos超过原字符串长度,则返回原字符串
        如果len超过原字符串长度,则由新字符串完全替换
INSTR(str,substr)
    返回字符串 str 中子字符串的第一个出现位置。

LEFT(str,len)
    返回字符串str 从开始的len位置的子序列字符。

LOWER(str)
    变小写

UPPER(str)
    变大写

REVERSE(str)
    返回字符串 str ,顺序和字符顺序相反。
    
SUBSTRING(str,pos) , SUBSTRING(str FROM pos) SUBSTRING(str,pos,len) , SUBSTRING(str FROM pos FOR len)
    不带有len 参数的格式从字符串str返回一个子字符串,起始于位置 pos。带有len参数的格式从字符串str返回一个长度同len字符相同的子字符串,起始于位置 pos。 使用 FROM的格式为标准 SQL 语法。也可能对pos使用一个负值。假若这样,则子字符串的位置起始于字符串结尾的pos 字符,而不是字符串的开头位置。在以下格式的函数中可以对pos 使用一个负值。

    mysql> SELECT SUBSTRING('Quadratically',5);
        -> 'ratically'
    
    mysql> SELECT SUBSTRING('foobarbar' FROM 4);
        -> 'barbar'
    
    mysql> SELECT SUBSTRING('Quadratically',5,6);
        -> 'ratica'
    
    mysql> SELECT SUBSTRING('Sakila', -3);
        -> 'ila'
    
    mysql> SELECT SUBSTRING('Sakila', -5, 3);
        -> 'aki'
    
    mysql> SELECT SUBSTRING('Sakila' FROM -4 FOR 2);
        -> 'ki'

四、日期和时间函数

CURDATE()或CURRENT_DATE() 返回当前的日期
CURTIME()或CURRENT_TIME() 返回当前的时间
DAYOFWEEK(date)   返回date所代表的一星期中的第几天(1~7)
DAYOFMONTH(date)  返回date是一个月的第几天(1~31)
DAYOFYEAR(date)   返回date是一年的第几天(1~366)
DAYNAME(date)   返回date的星期名,如:SELECT DAYNAME(CURRENT_DATE);
FROM_UNIXTIME(ts,fmt)  根据指定的fmt格式,格式化UNIX时间戳ts
HOUR(time)   返回time的小时值(0~23)
MINUTE(time)   返回time的分钟值(0~59)
MONTH(date)   返回date的月份值(1~12)
MONTHNAME(date)   返回date的月份名,如:SELECT MONTHNAME(CURRENT_DATE);
NOW()    返回当前的日期和时间
QUARTER(date)   返回date在一年中的季度(1~4),如SELECT QUARTER(CURRENT_DATE);
WEEK(date)   返回日期date为一年中第几周(0~53)
YEAR(date)   返回日期date的年份(1000~9999)

重点:
DATE_FORMAT(date,format) 根据format字符串格式化date值

   mysql> SELECT DATE_FORMAT('2009-10-04 22:23:00', '%W %M %Y');
    -> 'Sunday October 2009'
   mysql> SELECT DATE_FORMAT('2007-10-04 22:23:00', '%H:%i:%s');
    -> '22:23:00'
   mysql> SELECT DATE_FORMAT('1900-10-04 22:23:00',
    ->                 '%D %y %a %d %m %b %j');
    -> '4th 00 Thu 04 10 Oct 277'
   mysql> SELECT DATE_FORMAT('1997-10-04 22:23:00',
    ->                 '%H %k %I %r %T %S %w');
    -> '22 22 10 10:23:00 PM 22:23:00 00 6'
   mysql> SELECT DATE_FORMAT('1999-01-01', '%X %V');
    -> '1998 52'
   mysql> SELECT DATE_FORMAT('2006-06-00', '%d');
    -> '00'

五、加密函数

MD5()    
    计算字符串str的MD5校验和
PASSWORD(str)   
    返回字符串str的加密版本,这个加密过程是不可逆转的,和UNIX密码加密过程使用不同的算法。

六、控制流函数

CASE WHEN[test1] THEN [result1]...ELSE [default] END
    如果testN是真,则返回resultN,否则返回default
CASE [test] WHEN[val1] THEN [result]...ELSE [default]END  
    如果test和valN相等,则返回resultN,否则返回default

IF(test,t,f)   
    如果test是真,返回t;否则返回f

IFNULL(arg1,arg2) 
    如果arg1不是空,返回arg1,否则返回arg2

NULLIF(arg1,arg2) 
    如果arg1=arg2返回NULL;否则返回arg1        

七、控制流函数小练习

# 7.1、准备表,将下面这些内容保存为一个.txt文件或者.sql,然后通过navicat的运行sql文件的功能导入到数据库中,还记得吗?

/*
Navicat MySQL Data Transfer

Source Server         : localhost_3306
Source Server Version : 50720
Source Host           : localhost:3306
Source Database       : student

Target Server Type    : MYSQL
Target Server Version : 50720
File Encoding         : 65001

Date: 2018-01-02 12:05:30
*/

SET FOREIGN_KEY_CHECKS=0;

------

-- Table structure for course

------

DROP TABLE IF EXISTS `course`;
CREATE TABLE `course` (
  `c_id` int(11) NOT NULL,
  `c_name` varchar(255) DEFAULT NULL,
  `t_id` int(11) DEFAULT NULL,
  PRIMARY KEY (`c_id`),
  KEY `t_id` (`t_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

------

-- Records of course

------

INSERT INTO `course` VALUES ('1', 'python', '1');
INSERT INTO `course` VALUES ('2', 'java', '2');
INSERT INTO `course` VALUES ('3', 'linux', '3');
INSERT INTO `course` VALUES ('4', 'web', '2');

------

-- Table structure for score

------

DROP TABLE IF EXISTS `score`;
CREATE TABLE `score` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `s_id` int(10) DEFAULT NULL,
  `c_id` int(11) DEFAULT NULL,
  `num` double DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8;

------

-- Records of score

------

INSERT INTO `score` VALUES ('1', '1', '1', '79');
INSERT INTO `score` VALUES ('2', '1', '2', '78');
INSERT INTO `score` VALUES ('3', '1', '3', '35');
INSERT INTO `score` VALUES ('4', '2', '2', '32');
INSERT INTO `score` VALUES ('5', '3', '1', '66');
INSERT INTO `score` VALUES ('6', '4', '2', '77');
INSERT INTO `score` VALUES ('7', '4', '1', '68');
INSERT INTO `score` VALUES ('8', '5', '1', '66');
INSERT INTO `score` VALUES ('9', '2', '1', '69');
INSERT INTO `score` VALUES ('10', '4', '4', '75');
INSERT INTO `score` VALUES ('11', '5', '4', '66.7');

------

-- Table structure for student

------

DROP TABLE IF EXISTS `student`;
CREATE TABLE `student` (
  `s_id` varchar(20) NOT NULL,
  `s_name` varchar(255) DEFAULT NULL,
  `s_age` int(10) DEFAULT NULL,
  `s_sex` char(1) DEFAULT NULL,
  PRIMARY KEY (`s_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

------

-- Records of student

------

INSERT INTO `student` VALUES ('1', '鲁班', '12', '男');
INSERT INTO `student` VALUES ('2', '貂蝉', '20', '女');
INSERT INTO `student` VALUES ('3', '刘备', '35', '男');
INSERT INTO `student` VALUES ('4', '关羽', '34', '男');
INSERT INTO `student` VALUES ('5', '张飞', '33', '女');

------

-- Table structure for teacher

------

DROP TABLE IF EXISTS `teacher`;
CREATE TABLE `teacher` (
  `t_id` int(10) NOT NULL,
  `t_name` varchar(50) DEFAULT NULL,
  PRIMARY KEY (`t_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

------

-- Records of teacher

------

INSERT INTO `teacher` VALUES ('1', '大王');
INSERT INTO `teacher` VALUES ('2', 'alex');
INSERT INTO `teacher` VALUES ('3', 'chao');
INSERT INTO `teacher` VALUES ('4', 'peiqi');

# 7.2、统计各科各分数段人数.显示格式:课程ID,课程名称,[100-85],[85-70],[70-60],[ <60]

select  score.c_id,
      course.c_name, 
  sum(CASE WHEN num BETWEEN 85 and 100 THEN 1 ELSE 0 END) as '[100-85]',
  sum(CASE WHEN num BETWEEN 70 and 85 THEN 1 ELSE 0 END) as '[85-70]',
  sum(CASE WHEN num BETWEEN 60 and 70 THEN 1 ELSE 0 END) as '[70-60]',
  sum(CASE WHEN num < 60 THEN 1 ELSE 0 END) as '[ <60]'


from score,course where score.c_id=course.c_id GROUP BY score.c_id;

自定义函数(自己简单看看吧)

# !!!注意!!!

# 函数中不要写sql语句(否则会报错),函数仅仅只是一个功能,是一个在sql中被应用的功能

# 若要想在begin...end...中写sql,请用存储过程
delimiter //
create function f1(

i1 int,
i2 int)


returns int
BEGIN
declare num int;
set num = i1 + i2;
return(num);


END //
delimiter ;

delimiter //
create function f5(
i int

)
returns int
begin
declare res int default 0;
if i = 10 then
    set res=100;
elseif i = 20 then
    set res=200;
elseif i = 30 then
    set res=300;
else
    set res=400;
end if;
return res;
end //
delimiter ;


二 删除函数

drop function func_name; 

三 执行函数

# 获取返回值
select UPPER('chao') into @res;
SELECT @res;
# 在查询中使用
select f1(11,nid) ,name from tb2;

关于查看存储过程,函数,视图,触发器的语法:

查询数据库中的存储过程和函数

   select name from mysql.proc where db = 'xx' and type = 'PROCEDURE'   //查看xx库里面的存储过程
   select name from mysql.proc where db = 'xx' and type = 'FUNCTION'   //函数

   show procedure status; //存储过程
   show function status;     //函数

查看存储过程或函数的创建代码

  show create procedure proc_name;
  show create function func_name;

查看视图
  SELECT * from information_schema.VIEWS   //视图
  SELECT * from information_schema.TABLES   //表

查看触发器
  SHOW TRIGGERS [FROM db_name][LIKE expr]
  SELECT * FROM triggers T WHERE trigger_name=”mytrigger” \G;其中triggers T就是triggers as T的意思,起别名

9.流程控制

一 条件语句

    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循环:#后面讲索引的时候,咱们会用到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 ;

END //
delimiter ;

  到这里你会发现,其实sql也是一个开发语言,基本数据类型啊函数啊流程控制啊(if、while等)它都有。下面这两个我们简单看一下用法就行啦~~~

    repeat循环:

delimiter //
CREATE PROCEDURE proc_repeat ()
BEGIN

DECLARE i INT ;
SET i = 0 ;
repeat

select i;
set i = i + 1;
until i >= 5

end repeat;

END //
delimiter ; 

    loop:

BEGIN
declare i int default 0;
loop_label: loop

set i=i+1;
if i<8 then
    iterate loop_label;
end if;
if i>=10 then
    leave loop_label;
end if;
select i;


end loop loop_label;
END

更多推荐

MySQL笔记总结

本文发布于:2023-04-13 22:24:00,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/598089a85bd59f2c32d0cf58d52b4d64.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:笔记   MySQL

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!

  • 73767文章数
  • 14阅读数
  • 0评论数