mysql详解

☆樱花仙子☆ 提交于 2019-12-01 01:19:52

数据库

"""
1、什么是数据库:管理数据的系统 - 安装某种管理数据的系统 - 管理的对象本质是文件
2、存储数据的位置:内存、硬盘
3、什么是系统:运行在硬件基础上,管理其他软件
"""

# 数据库的组成
"""
库:存放多张表 - 文件夹
表:包含多条相同结构的记录 - 文件
记录:包含多个key-value键值对的一条数据 - 二进制数据
字段:描述信息 - 信息本身 == key-value - 二进制数据

stu
id  name    age     gender
1   Bob     18      男
2   Tom     17      女
...

teacher
...

"""

数据库的分类

# 1)关系与非关系
#   关系:数据库中表与表之间有联系 - mysql
#   非关系:没有表概念 - redis、mongodb(介于关系与非关系)

# 2)内存与硬盘
#   硬盘:数据可以永久存储 - mysql、mongodb
#   内存:数据的存取效率极高 - redis、memcache

# 3)sql与nosql
#   sql:数据库操作通过sql语句
#   nosql:数据库操作就是key-value形式(value就是一条记录)
#           stu - {'name': 'Bob', 'age': 18}
#           stus - [{'name': 'Bob', 'age': 18}, {'name': 'Tom', 'age': 18}]
#           name - 'Jerry'

安装

"""
1、解压免安装版本(5.6 - 没有初始密码)
2、配置环境变量:数据库绝对路径下的bin
3、配置服务:
    启动管理员终端:mysqld --install [nmysql]  #安装服务端
    
"""

卸载

"""
前提)启动终端,输入 mysql,如果不是提示 不是内部或外部命令,代表已安装数据库

1)直接连入
    i 搜索:服务,检索mysql服务,有
        停止服务,并移除服务 - 启动管理员终端:mysqld --remove
            
    ii 搜索:服务,检索mysql服务,无 (mysql使用命令启动)
        停止服务,启动管理员终端
            tasklist | findstr mysql
            taskkill /pid 进程pid /f
    
2)连接超时
    搜索:服务,检索mysql服务,移除服务即可
    
3)移除环境变量

总结:如果能连入,先停止服务 | 如果添加了服务,就移除服务 | 取消环境变量配置
"""

连接数据库

1)游客登陆(不一定能登入,登入了也啥都不能干)
>: mysql

2)账号密码登录
>: mysql -u root -p[密码]
再输入密码,没有任何提示,没有密码直接回车

3)连接指定服务器的mysql
>: mysql -h ip地址 -P 端口号 -u 账号 -p[密码]
回车后敲入密码
eg:
>: mysql -hlocalhost -P3306 -uroot -p[密码]

4)退出数据库
>: quit
>: exit

用户信息查看

1)查看当前登录的用户
mysql>: select user();
mysql>: select version(); 查看mysql版本: 
2)root权限下可以查看所有用户信息
mysql>: select * from mysql.user;    #查看所有用户的所有信息
mysql>: select * from mysql.user \G; #查看所有用户的权限
mysql>: select user,password,host from mysql.user;  #查看所有用户的(user,password,host)

3)root登录下,删除游客(操作后要重启mysql服务)
mysql>: delete from mysql.user where user='';

4)root登录下,修改密码(操作后要重启mysql服务)
mysql>: update mysql.user set password=password('12345678') where host='localhost';

updata mysql.user set password=password('root') where host='localhost';

5)没有登录
>: mysqladmin -u用户名 -p旧密码 -h域名 password "新密码"
eg>: mysqladmin -uroot -p12345678 -hlocalhost password "root"

6)root登录下,创建用户
mysql>:grant 权限们 on 数据库名.表名 to 用户名@主机名 identified by '密码';

数据库的基本操作

1)查看已有数据库(当前用户可以操作的)所有数据库
mysql>:show databases;

2)选择某个数据库
mysql>:use 数据库名  #进入某个具体的数据库,可以直接对该数据库下的表进行操作,不需要用 . 

3)查看当前所在数据库
mysql>:select database();  #需要通过 use 进入具体数据库,并查看当前数据库锁拥有的表

4)创建数据库
mysql>:create database 数据库名 [charset=编码格式]; #不指定编码格式的话,默认为latin1
eg>: create database owen;
eg>: create database zero charset=utf8;
eg>: create database tank;

5)查看创建数据库的详细内容
mysql>:show create database 数据库名;
eg>: show create database owen;

6)删除数据库
mysql>: drop database 数据库名;
eg>: drop database tank;

7)修改字符编码
mysql>: alter database 数据库名 charset=编码格式;

SQL语句、语句扩展

top:TOP 子句用于规定要返回的记录的数目。
列(s) = *
sql top 语法
select top number|precent 列(s) from table_name;     # 列(s) 表示 所有的列名称 
top number例:select top 2 * from table_name; 或 select * from table_name limit 2;
top precent例:select top 50 precent * from table_name; # 选取表中50%的记录

like:LIKE 操作符用于在 WHERE 子句中搜索列中的指定模式
sql like 语法
select 列(s) from table_name where 列 like pattern;
例:select * from table_name where 列 like 'A%'(在表中搜寻以A开头的)|'%B'(以B结尾的)|'%AB%'(包含AB的);

in:IN 操作符允许我们在 WHERE 子句中规定多个值
sql in 语法
select * from table_name where 列 in(值1,值2,...);

between:操作符 BETWEEN ... AND 会选取介于两个值之间的数据范围。这些值可以是数值、文本或者日期。
sql between 语法
select * from table_name where 列 between 值1 and 值2;  # 范围取头不取尾

alias:为列名称和表名称指定别名(Alias)。
sql alias 语法
表:select * from table_name as alias_name;
表别名例:select p.列1,p.列2,p.列3 from table_name1 as p,table_name2 as po where p.列1='值1' and p.列2='值2';
列:select 列 as alias_name from table_name;
列别名例:select 列1 as alias_name1,列2 as alias_name2 from table_name;

join:用于根据两个或多个表中的列之间的关系,从这些表中查询数据。
sql join 语法
select tb1.列1,tb1.列2,tb2.列 from tb1 inner join tb2 on tb1.列3=tb2.列3 order by tb1.列1;
或
select tb1.列1,tb1.列2,tb2.列 from tb1,tb2 where tb1.列3=tb2.列3;
备注:不同的 SQL JOIN
除了在上面的例子中使用的 INNER JOIN(内连接),还可以使用其他几种连接。
下面列出了可以使用的 JOIN 类型,以及它们之间的差异。
JOIN: 如果表中有至少一个匹配,则返回行
LEFT JOIN: 即使右表中没有匹配,也从左表返回所有的行
RIGHT JOIN: 即使左表中没有匹配,也从右表返回所有的行
FULL JOIN: 只要其中一个表中存在匹配,就返回行

inner join 关键字语法:在表中存在至少一个匹配时,INNER JOIN 关键字返回行。
select * from tb1 inner join tb2 on tb1.列=tb2.列;        # inner join 与join是相同的

left join 关键字语法:LEFT JOIN 关键字会从左表(tb1)那里返回所有的行,即使在右表(tb2)中没有匹配的行。
select * from tb1 left join tb2 on tb1.列=tb2.列;

right join 关键字语法:RIGHT JOIN 关键字会右表(tb2)那里返回所有的行,即使在左表(tb1)中没有匹配的行。
select * from tb1 right join tb2 on tb1.列=tb2.列;

full join 关键字语法:只要其中某个表存在匹配,FULL JOIN 关键字就会返回行
select * from tb1 full join tb2 on tb1.列=tb2.列;

11、union:用于合并两个或多个 SELECT 语句的结果集(UNION 内部的 SELECT 语句必须拥有相同数量的列。列也必须拥有相似的数据类型。同时,每条 SELECT 语句中的列的顺序必须相同)
sql union 与 union all 语法
select * from table_name1 union select * from table_name2;
select * from table_name1 union all select * from table_name2;
备注:默认地,union 选取不同的值,如果允许重复的值,就使用 union all(列出所有的值)

select into:从一个表中选取数据,然后把数据插入另一个表中。常用于创建表的备份复件或者用于对记录进行存档。
sql select into 语法
select * into new_table_name [in externaldatabase] from old_table_name; # 所有列插入新表
select 列 into new_table_name [in externaldatabase] from old_table_name;  # 某一列插入新表
例:备份某张表  select * into tb1_backup from tb1;
向另一个数据库中拷贝表  select * into tb1 in 'backup.mdb' from tb1;
多表查询存入到新表   select tb1.列1,tb2.列2 into new_table_name from tb1 inner join tb2 on tb1.列3=tb2.列3;

create index:用于在表中创建索引。在不读取整个表的情况下,索引使数据库应用程序可以更快地查找数据。
注释:更新一个包含索引的表需要比更新一个没有索引的表更多的时间,这是由于索引本身也需要更新。因此,理想的做法是仅仅在常常被搜索的列(以及表)上面创建索引。
sql create index 语法
create index index_name on table_name(列);   # 简单的索引,允许使用重复的值
create index index_name on table_name(列1 desc,列2);      # 默认是升序,希望降序在列名称后添加保留字 desc,索引不止一个列可以逗号隔开

sql create unique index 语法      # 唯一的索引
create unique index index_name on table_name(列);    # 唯一的索引意味着两个行不能拥有相同的索引值

alter table : 语句用于在已有的表中添加、修改或删除列。
sql alter table 语法
添加列:alter table table_name add 列 数据类型;
删除列:alter table table_name drop column 列;
改变列中数据类型:alter table table_name modify column 列 需要修改的类型;
只修改列的数据类型的方法:
通常可以写成 alter table 表名 modify column 列名 新的列的类型
例如:student表中列sname的类型是char(20),现在要修改为varchar(20),SQL语句如下
     alter table student modify column sname varchar(20);
同时修改列名和列的数据类型的方法:
通常可以写成 alter table 表名 change column 旧列名 新列名 新的列类型
例如:student表中列sname的类型是char(20),现在要修改为stuname varchar(20),SQL语句如下
     alter table student change column sname stuname varchar(20);

alter table table_name add <新列名> <数据类型> [约束条件][FLRST(添加列到表的最前面)|AFTER(指定列后面)  <已存在的列>]; # 添加列
alter table table_name <旧列名> <新列名> <新数据类型>; # 修改列名
alter table table_name modify <列名> <数据类型>; # 修改列的数据类型
alter table table_name modify <列1(想要改变的列)> <数据类型> FLRST|AFTER <列2>; # 修改列的位置
alter table table_name drop <列>;  # 删除列
alter table <旧表名> rename to <新表名>; # 修改表名

view(视图): 视图是可视化的表。视图包含行和列,就像一个真实的表
注释:数据库的设计和结构不会受到视图中的函数、where 或 join 语句的影响。

sql create view 语法
create view view_name as select 列(s) from table_name where 条件;
注释:视图总是显示最近的数据。每当用户查询视图时,数据库引擎通过使用 SQL 语句来重建数据。

更新视图:create or replace view view_name as select 列(*) from table_name where 条件;
删除视图:drop view view_name;

数据库配置

# 通过配置文件统一配置的目的:统一管理 服务端(mysqld) 、客户端(client)
#       配置了 mysqld(服务端) 的编码为utf8,那么再创建的数据库,默认编码都采用utf8

# 配置流程
# 1)在mysql安装根目录下,创建配置文件:my.ini
#       mac下配置文件名叫 my.cnf

# 2)设置配置文件内容并保存
[mysqld]  # 服务器配置
port=3306  # 可以修改数据库默认端口(如果数据库端口被其他软件占用)
character-set-server=utf8  # 编码格式
#非安全模式
sql_mode = no_engine_substitution
#安全模式
#sql_mode = strict_trans_tables
#设置引擎
default-storage-engine=INNODB
collation-server=utf8_general_ci  # 排序方式(默认跟编码格式走)
#utf8_general_ci 不区分大小写,这个你在注册用户名和邮箱的时候就要使用。
#utf8_general_cs 区分大小写,如果用户名和邮箱用这个 就会照成不良后果
#utf8_bin:字符串每个字符串用二进制数据编译存储。 区分大小写,而且可以存二进制的内容
[client]  # mysql自己的客户端叫[mysql],配置[client]即配置了[mysql],也配置了其他存在方式的客户端,比如Navicat可视化客户端
default-character-set=utf8  # 编码格式

# 3)重启数据库服务

用户操作:重点

# 为特定的数据库分配有该数据库 操作权限 的用户
mysql>: grant 权限们 on 数据库.表 to 用户名@'主机名' identified by '密码';

# 1)all:所有权限
# 2)oldboy.*:oldboy数据库下所有表
# 3)oldboy@'localhost':本机可以通过oldboy用户登入
# 4)identified by 'Oldboy123':密码为Oldboy123
eg>: grant all on oldboy.* to oldboy@'localhost' identified by 'Oldboy123';

# 1)select,delete,update,insert,drop,alter:指定的权限
# 2)oldboy.*:oldboy数据库下所有表
# 3)oldboy@'%':任何机器可以通过oldboy用户登入
# 4)identified by 'Oldboy123':密码为Oldboy123
eg>: grant select,delete,update,insert,drop,alter on oldboy.* to oldboy@'%' identified by 'Oldboy123';

# 撤销权限
mysql>: revoke 权限1,权限2,... on 数据库名.表名 from 用户名@'主机名';
# 禁掉本地oldboy用户对oldboy数据库的所有表的drop权限
eg:> revoke drop on oldboy.* from oldboy@'localhost';

# 删除用户
drop user 用户名@'主机名';

数据库表的引擎:驱动数据的方式 - 数据库优化

# 前提: 引擎是建表是规定, 提供给表使用的, 不是数据库

# 展示所有引擎
mysql> show engines; 

# innodb(默认): 支持事务, 行级锁, 外键
mysql>: create table t11(id int)engine=innodb;

# myisam: 查询效率要优于innodb, 当不需要支持事务, 行级锁, 外键, 可以通过设置myisam来优化数据库
mysql>: create table t12(id int)engine=myisam;

# blackhole:黑洞,存进去的数据都会消失(可以理解不存数据)
mysql>: create table t13(id int)engine=blackhole;

# memory:表结构是存储在硬盘上的,但是表数据全部存储在内存中
mysql>: create table t14(id int)engine=memory;

MySQL各大存储引擎:

最好先看下你下的MySQL支持什么数据库引擎

这里写图片描述

存储引擎主要有: 1. MyIsam , 2. InnoDB, 3. Memory, 4. Blackhole, 5. CSV, 6. Performance_Schema, 7. Archive, 8. Federated , 9 Mrg_Myisam

但是我们主要分析使用MyIsam 和InnoDB。其余略微带过,详情请分别百度。

(1)InnoDB:

定义:(默认的存储引擎)

InnoDB是一个事务型的存储引擎,有行级锁定和外键约束。

Innodb引擎提供了对数据库ACID事务的支持,并且实现了SQL标准的四种隔离级别,关于数据库事务与其隔离级别的内容请见数据库事务与其隔离级别这类型的文章。该引擎还提供了行级锁和外键约束,它的设计目标是处理大容量数据库系统,它本身其实就是基于MySQL后台的完整数据库系统,MySQL运行时Innodb会在内存中建立缓冲池,用于缓冲数据和索引。但是该引擎不支持FULLTEXT类型的索引,而且它没有保存表的行数,当SELECT COUNT(*) FROM TABLE时需要扫描全表。当需要使用数据库事务时,该引擎当然是首选。由于锁的粒度更小,写操作不会锁定全表,所以在并发较高时,使用Innodb引擎会提升效率。但是使用行级锁也不是绝对的,如果在执行一个SQL语句时MySQL不能确定要扫描的范围,InnoDB表同样会锁全表。

//这个就是select锁表的一种,不明确主键。增删改查都可能会导致锁全表,在以后我们会详细列出。
SELECT * FROM products WHERE name='Mouse' FOR UPDATE;

适用场景:

1)经常更新的表,适合处理多重并发的更新请求。

2)支持事务。

3)可以从灾难中恢复(通过bin-log日志等)。

4)外键约束。只有他支持外键。

5)支持自动增加列属性auto_increment。

MySQL官方对InnoDB的讲解:

1)InnoDB给MySQL提供了具有提交、回滚和崩溃恢复能力的事务安全(ACID兼容)存储引擎。

2)InnoDB锁定在行级并且也在SELECT语句提供一个Oracle风格一致的非锁定读,这些特色增加了多用户部署和性能。没有在InnoDB中扩大锁定的需要,因为在InnoDB中行级锁定适合非常小的空间。

3)InnoDB也支持FOREIGN KEY强制。在SQL查询中,你可以自由地将InnoDB类型的表与其它MySQL的表的类型混合起来,甚至在同一个查询中也可以混合。

4)InnoDB是为处理巨大数据量时的最大性能设计,它的CPU效率可能是任何其它基于磁盘的关系数据库引擎所不能匹敌的。

5) InnoDB被用来在众多需要高性能的大型数据库站点上产生。

补充:什么叫事务?简称ACID

A 事务的原子性(Atomicity):指一个事务要么全部执行,要么不执行.也就是说一个事务不可能只执行了一半就停止了.比如你从取款机取钱,这个事务可以分成两个步骤:1划卡,2出钱.不可能划了卡,而钱却没出来.这两步必须同时完成.要么就不完成.

C 事务的一致性(Consistency):指事务的运行并不改变数据库中数据的一致性.例如,完整性约束了a+b=10,一个事务改变了a,那么b也应该随之改变.

I 独立性(Isolation):事务的独立性也有称作隔离性,是指两个以上的事务不会出现交错执行的状态.因为这样可能会导致数据不一致.

D 持久性(Durability):事务的持久性是指事务执行成功以后,该事务所对数据库所作的更改便是持久的保存在数据库之中,不会无缘无故的回滚.

(2)M4yIsam:

定义

MyIASM是MySQL默认的引擎,但是它没有提供对数据库事务的支持,也不支持行级锁和外键,因此当INSERT(插入)或UPDATE(更新)数据时即写操作需要锁定整个表,效率便会低一些。

MyIsam 存储引擎独立于操作系统,也就是可以在windows上使用,也可以比较简单的将数据转移到linux操作系统上去。

意味着:引擎在创建表的时候,会创建三个文件,一个是.frm文件用于存储表的定义,一个是.MYD文件用于存储表的数据,另一个是.MYI文件,存储的是索引。操作系统对大文件的操作是比较慢的,这样将表分为三个文件,那么.MYD这个文件单独来存放数据自然可以优化数据库的查询等操作。有索引管理和字段管理。MyISAM还使用一种表格锁定的机制,来优化多个并发的读写操作,其代价是你需要经常运行OPTIMIZE TABLE命令,来恢复被更新机制所浪费的空间。

适用场景

1)不支持事务的设计,但是并不代表着有事务操作的项目不能用MyIsam存储引擎,可以在service层进行根据自己的业务需求进行相应的控制。

2)不支持外键的表设计。

3)查询速度很快,如果数据库insert和update的操作比较多的话比较适用。

4)整天 对表进行加锁的场景。

5)MyISAM极度强调快速读取操作。

6)MyIASM中存储了表的行数,于是SELECT COUNT(*) FROM TABLE时只需要直接读取已经保存好的值而不需要进行全表扫描。如果表的读操作远远多于写操作且不需要数据库事务的支持,那么MyIASM也是很好的选择。

缺点:

就是不能在表损坏后恢复数据。(是不能主动恢复)

补充:ISAM索引方法–索引顺序存取方法

定义:

是一个定义明确且历经时间考验的数据表格管理方法,它在设计之时就考虑到 数据库被查询的次数要远大于更新的次数。

特性:

ISAM执行读取操作的速度很快,而且不占用大量的内存和存储资源。

在设计之初就预想数据组织成有固定长度的记录,按顺序存储的。—ISAM是一种静态索引结构。

缺点:

1.它不 支持事务处理

2.也不能够容错。如果你的硬盘崩溃了,那么数据文件就无法恢复了。如果你正在把ISAM用在关键任务应用程序里,那就必须经常备份你所有的实 时数据,通过其复制特性,MYSQL能够支持这样的备份应用程序。

(3)Memory(也叫HEAP)堆内存嘛:

定义

使用存在内存中的内容来创建表。每个MEMORY表只实际对应一个磁盘文件。MEMORY类型的表访问非常得快,因为它的数据是放在内存中的,并且默认使用HASH索引。

但是一旦服务关闭,表中的数据就会丢失掉。 HEAP允许只驻留在内存里的临时表格。驻留在内存里让HEAP要比ISAM和MYISAM都快,但是它所管理的数据是不稳定的,而且如果在关机之前没有进行保存,那么所有的数据都会丢失。在数据行被删除的时候,HEAP也不会浪费大量的空间。HEAP表格在你需要使用SELECT表达式来选择和操控数据的时候非常有用。

适用场景:

1)那些内容变化不频繁的代码表,或者作为统计操作的中间结果表,便于高效地堆中间结果进行分析并得到最终的统计结果。

2)目标数据比较小,而且非常频繁的进行访问,在内存中存放数据,如果太大的数据会造成内存溢出。可以通过参数max_heap_table_size控制Memory表的大小,限制Memory表的最大的大小。

3)数据是临时的,而且必须立即可用得到,那么就可以放在内存中。

4)存储在Memory表中的数据如果突然间丢失的话也没有太大的关系。

注意: Memory同时支持散列索引和B树索引,B树索引可以使用部分查询和通配查询,也可以使用<,>和>=等操作符方便数据挖掘,散列索引相等的比较快但是对于范围的比较慢很多。

特性要求:

1)要求存储的数据是数据长度不变的格式,比如,Blob和Text类型的数据不可用(长度不固定的)。

2)要记住,在用完表格之后就删除表格。

(4)Mrg_Myisam:(分表的一种方式–水平分表)

定义:

是一个相同的可以被当作一个来用的MyISAM表的集合。“相同”意味着所有表同样的列和索引信息。

也就是说,他将MyIsam引擎的多个表聚合起来,但是他的内部没有数据,真正的数据依然是MyIsam引擎的表中,但是可以直接进行查询、删除更新等操作。

比如:我们可能会遇到这样的问题,同一种类的数据会根据数据的时间分为多个表,如果这时候进行查询的话,就会比较麻烦,Merge可以直接将多个表聚合成一个表统一查询,然后再删除Merge表(删除的是定义),原来的数据不会影响。

(5)Blackhole(黑洞引擎)

定义

任何写入到此引擎的数据均会被丢弃掉, 不做实际存储;Select语句的内容永远是空。

他会丢弃所有的插入的数据,服务器会记录下Blackhole表的日志,所以可以用于复制数据到备份数据库。

使用场景

1)验证dump file语法的正确性

2)以使用blackhole引擎来检测binlog功能所需要的额外负载

3)充当日志服务器

其余引擎,大家感兴趣就各自先百度吧。本文主要是对比引擎使用以及其原理。

数据库的模式

# mysql 5.7 以后默认都是安全模式

# mysql 5.6 版本
sql_model=no_engine_substitution  # 非安全性,默认
sql_model=strict_trans_tables  # 安全性

# 查看当前数据库模式:
show variables like "%sql_mode%"; # %匹配0~n个任意字符 => 模糊查询

# 临时设置为安全模式,服务重启后会被重置
mysql>: set global sql_mode="strict_trans_tables";  # 在root用户登录状态下
# 在设置后,quit断开数据库连接后(服务器不重启)就会进入安全模式

# 安全模式下,非安全模式下sql执行的警告语句,都会抛异常
eg>: create table t1(name char(2));
eg>: insert into t1 values ("ab") # 正常
eg>: insert into t1 values ("owen") # 错误 Data too long for column 'name' at row 1

常用字符集

位(bit):是计算机 内部数据 储存的最小单位,11001100是一个八位二进制数。

字节(byte):是计算机中 数据处理 的基本单位,习惯上用大写 B 来表示,1B(byte,字节)= 8bit(位)

字符:是指计算机中使用的字母、数字、字和符号

ASCIIS码: 1个英文字母(不分大小写)= 1个字节的空间

​ 1个中文汉字 = 2个字节的空间

​ 1个ASCII码 = 一个字节

UTF-8编码:1个英文字符 = 1个字节

​ 英文标点 = 1个字节

​ 1个中文(含繁体) = 3个字节

​ 中文标点 = 3个字节

Unicode编码:1个英文字符 = 2个字节

​ 英文标点 = 2个字节

​ 1个中文(含繁体) = 2个字节

​ 中文标点 = 2个字节

数据存储是以“字节”(Byte)为单位,数据传输大多是以“位”(bit,又名“比特”)为单位,一个位就代表一个0或1(即二进制),每8个位(bit,简写为b)组成一个字节(Byte,简写为B),是最小一级的信息单位。

B与iB

1KiB(Kibibyte)=1024byte

1KB(Kilobyte)=1000byte

1MiB(Mebibyte)=1048576byte

1MB(Megabyte)=1000000byte

硬盘生产商是以GB(十进制),即10的3次方=1000,如1MB=1000KB)计算的,而电脑(操作系统)是以GiB(2进制,即2的10次方, 如1MiB=1024KiB)计算的,但是国内用户一般理解为1MiB=1M=1024 KB, 所以为了便于中文化的理解,翻译MiB为MB也是可以的。

同样根据硬盘)厂商与用户对于1MB大小的不同理解,所以好多160G的硬盘实际容量按计算机实际的1MiB=1024KB算都不到160G,这也可以解释为什么新买的硬盘“缺斤短两”并没有它所标示的那么大。

国际单位制(SI):

1KB=1024B;1MB=1024KB=1024×1024B。

1B(byte,字节)= 8 bit(见下文);

1KB(Kilobyte,千字节])=1024B= 2^10 B;

1MB(Megabyte,兆字节,百万字节,简称“兆”)=1024KB= 2^20 B;

1GB(Gigabyte,吉字节,十亿字节,又称“千兆”)=1024MB= 2^30 B;

1TB(Terabyte,万亿字节,太字节)=1024GB= 2^40 B;

1PB(Petabyte,千万亿字节,拍字节)=1024TB= 2^50 B;

1EB(Exabyte,百亿亿字节,艾字节)=1024PB= 2^60 B;

1ZB(Zettabyte,十万亿亿字节,泽字节) =102424EB= 2^70 B;

1YB(Yottabyte,一亿亿亿字节,尧字节)= 1024ZB= 2^80 B;

1BB(Brontobyte,一千亿亿亿字节)= 1024YB= 2^90 B;

1NB(NonaByte,一百万亿亿亿字节) = 1024BB = 2^100 B;

1DB(DoggaByte,十亿亿亿亿字节) = 1024 NB = 2^110 B;

数据存储是以10进制表示,数据传输是以2进制表示的,所以1KB不等于1000B。

  1. ASCII

    ASCII是7比特的字符集,涵盖了英语中的绝大多数字符。编码从0到127.

    ASCII码实现的是大小写英文字母,阿拉伯数字,及常用的标点符、运算符、控制字符(换行、删除等)和通信字符(文头、确认等)与计算机编码之间的对应。ASCII编码采用单字节(8 Bit)存储,实际数据存储空间是7 Bit,最高位的1 Bit是奇偶校验位。

    ASCII编码对于英语国家足够用了,但是却无法表达非英语国家字符到计算机编码之间的映射,如中文汉字、法语字母等。所以,出现了很多非ASCII编码(但大多数都是兼容ASCII码的)。

  2. ISOLatin-1(the ISO-8859-1 standard)

    ISO Latin-1是8比特的字符集,定义了256个字符。前128个字符(00000000-01111111)与ASCII完全一致。

    Latin1是ISO-8859-1的别名,有些环境下写作Latin-1。Latin1编码是单字节编码,向下兼容ASCII,其编码范围是0x00-0xFF,0x00-0x7F之间完全和ASCII一致,0x80-0x9F之间是控制字符,0xA0-0xFF之间是文字符号。

    ASCII编码是一个7位的容器,ISO-8859-1编码是一个8位的容器。

    因为Latin1编码范围使用了单字节内的所有空间,在支持Latin1编码的系统中传输和存储其他任何编码的字节流都不会被抛弃。换言之,把其他任何编码的字节流当作Latin1编码看待都没有问题。这是个很重要的特性,MySQL数据库默认编码是Latin1就是利用了这个特性。

  3. Unicode

    世界上存在着多种编码方式,同一个二进制数字可以被解释成不同的符号。因此,要想打开一个文本文件,就必须知道它的编码方式,否则用错误的编码方式解读,就会出现乱码。为什么电子邮件常常出现乱码?就是因为发信人和收信人使用的编码方式不一样。

    可以想象,如果有一种编码,将世界上所有的符号都纳入其中。每一个符号都给予一个独一无二的编码,那么乱码问题就会消失。这就是Unicode。

    Unicode当然是一个很大的集合,现在的规模可以容纳100多万个符号。每个符号的编码都不一样需要注意的是,Unicode只是一个符号集,它只规定了符号的二进制代码,却没有规定这个二进制代码应该如何存储。

    如果所有字符都按照最大存储空间存储,那必然会浪费很大的空间,比如所有字符都按照3字节存储,但是英文字母只需要一个字节存储就够了,就等于说一个Unicode编码的英文文档是ASCII编码文档存储空间的三倍。
    所以,便有了变长编码—-UTF-8。

    Unicode只是定义了字符的编码值,并未指定值以何种形式存储。比如汉字“田”的Unicode编码是7530,转换为二进制是01110101,00110000。比方现在定义一种unicode的实现方式UTF-FAKE,规则是

    a. 使用24个字节

    b. 每个字节的高7位都是1

    c. 每个字节的最末一位存储unicode编码值

    那么01110101,00110000的存储形式是

    11111110, 11111110, 11111110, 11111110, 11111110, 11111110, 11111110, 11111110,

    11111110, 11111111, 11111111, 11111111, 11111110, 11111111, 11111110, 11111111,

    11111110, 11111110, 11111111, 11111111, 11111110, 11111110, 11111110, 11111110

    其中末位为蓝色0的字节为补足字节。

    实际使用的编码方式UTF-8使用三个字节存储“田” 01110101,00110000,如下

    11100111, 10010100, 10110000

    Unicode的第一个版本于1991年发布,该版本允许的的最大编码空间是两个字节。96年发布的Unicode 2.0版本引入了surrogate pair,将Unicode的编码数目扩充到了百万级,由于可见的将来该数目不大可能用光,因此Unicode委员会对外宣称该上限永不会更改。Surrogate pair在UTF-16和UTF-32中得到了实现。Unicode的前256个字符及编码值与Latin-1完全一致。比如大写字母A在Latin-1和Unicode中的编码值都是0x41(十进制的65)。

    Unicode的编码值范围是 000000hex 到10FFFFhex,可划分为17个plane,每个plane包含65536(= 216)个字符。Plane的代码从0到16(十进制),对应于 000000hex,010000hex,020000hex,… … ,0F0000hex,10FFFFhex的蓝色部分。

    Unicode的表示方式一般是”U+”后缀上4到6个十六进制字符,如”田“的Unicode表示方式是U+7530。

  4. UTF-8

    UTF-8就是在互联网上使用最广的一种Unicode的实现方式。其他实现方式还包括UTF-16和UTF-32,不过在互联网上基本不用。

    UTF-8最大的一个特点,就是它是一种变长的编码方式。它可以使用1~4个字节表示一个符号,根据不同的符号而变化字节长度。如ASCII编码的内容UTf-8中就是用一个字符存储的。

    UTF-8采用可变长度的编码,长度从1到4个字节不等。某些应用识别非标准的"utf8" 或"UTF 8"别名。只有ASCII字符在UTF-8中使用一个字节编码,且值与ASCII完全相同,其余字符在UTF-8中使用2到4个字节。因此UTF-8中的单字节且只有单字节编码字符的最高的1个比特是0。

    UTF-8对Unicode字符的编码规则如下

    utf8对unicode字符的编码规则

    说明如下:

    1. 只有ASCII使用单字节编码

    2. Unicode编码值大于127的字符使用多个字节编码。多字节序列的第一个字节称为leading byte,后续的字节称为continuation bytes。Leading byte的高位是110,1110或11110,其中的“1”的个数标明了序列中的字节总数。如2个字节序列的leading byte为110xxxxx,其中的11标明了这是一个双字节的序列,亦即该序列有1个continuation byte。Continuation bytes的每个字节的高两位均是10。

    3. 单字节序列、leading bytes和continuationbytes的高位分别是0,110/1110/11110和10,因此不会混淆。

    还是以汉字”田“为例,展示Unicode字符如何按照UTF-8存储。”田“的Unicode值是U+7530,比对上表发现介于U+0800 - U+FFFF之间,因此需要3个字节来存储。7530转为二进制是1110101,00110000,一共15位。但由于UTF-8的3字节编码存储16个比特,因此将1110101,00110000的高一位补零变成16比特01110101,00110000。然后将这16比特依次填入三字节序列1110xxxx 10xxxxxx 10xxxxxx的x中,得到结果

    11100111 10010100 10110000,写成16进制就是E7 94 B0

    注意:虽然Unicode中的前256个字符及编码值与Latin-1完全一致,但UTF-8只对前128个即ASCII字符采用单字节编码,其余128个Latin-1字符是采用2个字节编码。因此ASCII编码的文件可以直接以UTF-8方式读取,而Latin-1的文件若包含值为128-255的字符则不可以。

    UTF-8是一种变长字节编码方式。对于某一个字符的UTF-8编码,如果只有一个字节则其最高二进制位为0;如果是多字节,其第一个字节从最高位开始,连续的二进制位值为1的个数决定了其编码的位数,其余各字节均以10开头。UTF-8最多可用到6个字节。
    如表:
    1字节 0xxxxxxx
    2字节 110xxxxx 10xxxxxx
    3字节 1110xxxx 10xxxxxx 10xxxxxx
    4字节 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
    5字节 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
    6字节 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx Unicode

    UNICODE UTF8转换规则

    符号范围 | UTF-8编码方式

    (十六进制) | (二进制)

    --------------------+---------------------------------------------

    0000 0000-0000 007F | 0xxxxxxx

    0000 0080-0000 07FF | 110xxxxx 10xxxxxx

    0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx

    0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

    下面,还是以汉字“严”为例,演示如何实现UTF-8编码。

    下面,还是以汉字“严”为例,演示如何实现UTF-8编码。

    已知“严”的unicode是4E25(100111000100101),根据上表,可以发现4E25处在第三行的范围内(0000 0800-0000 FFFF),因此“严”的UTF-8编码需要三个字节,即格式是“1110xxxx 10xxxxxx 10xxxxxx”。然后,从“严”的最后一个二进制位开始,依次从后向前填入格式中的x,多出的位补0。这样就得到了,“严”的UTF-8编码是“11100100 10111000 10100101”,转换成十六进制就是E4B8A5。

  5. UTF-16

    UTF-16也是采用可变长度编码,可以是一个或者两个16比特。某些应用中允许使用非标准的UTF_16或者UTF16作为别名。Unicode中的第一个plane的65536(= 216)codepoints采用16比特编码,其余的16个plane均采用2个16比特编码。采用2个16比特编码的前后两个16bit分别称为lead surrogate pair和trail surrogate pair,之所以称为surrogate是因为单独一个16bit不代表任何字符,只有组合起来才有意义。

    既然UTF-16也是可变长度编码,如何区分某个16bit是单独表示一个字符还是与后续的16bit组合起来表示一个字符呢?Unicode将D800–DBFF和DC00–DFFF这两个范围作为保留区间,没有将之分配给任何Unicode字符,若某16比特落在D800–DBFF范围内,便可将之视为采用2个16bit编码字符的第一个16bit,而落在DC00–DFFF的16bit可视为采用2个16bit编码字符的第二个16bit。这就使得Unicode第一个plane实际可分配使用的code points只有65536 – (DFFF - D800 + 1) = 65536 – 8*256 = 63488。

    采用一个16bit编码的Unicode字符在UTF-16中的编码值与其在Unicode中是相等的,比如英文大写字母A的Unicode值是U+0041,其UTF-16编码是0041 hex 。Unicode第二到第十七个plane采用两个16bit即surrogate pairs的字符从其Unicode code point到UTF-16的转换规则是

    1. 范围为0x10000 … 0x10FFFF的codepoint减去0x010000,减过后的结果范围是0x00000到0xFFFFF,使得该结果可以使用5位16进制亦即20位2进制数表示

    2. 结果中高10位(范围是0x0到0x3FF)加上0xD800(结果范围是0xD800到0xDBFF)作为双16bit的第一个16bit即leadsurrogate

    3. 结果中低10位(范围是0x0到0x3FF)加上0xDC00(结果范围是0xDC00到0xDFFF)作为双16bit的第二个16bit即trailsurrogate

    这样UTF-16与UTF-8都是self-synchronizing的,即某个16bit是否是一个字符的开始无需检查其前一个或者后一个16bit。与UTF-8的不同之处是,UTF-16不支持从某个随机的字节开始读取。

    举例:UTF-16 序列 0041, D801DC01 (字符"A"),若第一个字节丢失即从第二个字节读取,那么UTF-16认为序列是41D8,01DC,01;而UTF-8不存在这个问题。

  6. GKB

    GBK 是又一个汉字编码标准,全称《汉字内码扩展规范》(GBK),英文名称 Chinese Internal Code Specification ,中华人民共和国全国信息技术标准化技术委员会 1995 年 12 月 1 日制订,国家技术监督局标准化司、电子工业部科技与质量监督司 1995 年 12 月 15 日联合以技监标函 [1995] 229 号文件的形式,将它确定为技术规范指导性文件,发布和实施。这一版的 GBK 规范为 1.0 版。GB 即“国标”,K 是“扩展”的汉语拼音第一个字母。

    GBK 向下与 GB 2312 编码兼容,向上支持 ISO 10646.1 国际标准,是前者向后者过渡过程中的一个承上启下的标准。
    ISO 10646 是国际标准化组织 ISO 公布的一个编码标准,即 Universal Multilpe-Octet Coded Character Set(简称 UCS),大陆译为《通用多八位编码字符集》,台湾译为《广用多八位元编码字元集》,它与 Unicode 组织的 Unicode 编码完全兼容。ISO 10646.1 是该标准的第一部分《体系结构与基本多文种平面》。我国 1993 年以 GB 13000.1 国家标准的形式予以认可(即 GB 13000.1 等同于 ISO 10646.1)。
    ISO 10646 是一个包括世界上各种语言的书面形式以及附加符号的编码体系。其中的汉字部分称为“CJK 统一汉字”(C 指中国,J 指日本,K 指朝鲜)。而其中的中国部分,包括了源自中国大陆的 GB 2312、GB 12345、《现代汉语通用字表》等法定标准的汉字和符号,以及源自台湾的 CNS 11643 标准中第 1、2 字面(基本等同于 BIG-5 编码)、第 14 字面的汉字和符号。

    一、字汇
    GBK 规范收录了 ISO 10646.1 中的全部 CJK 汉字和符号,并有所补充。具体包括:
    1. GB 2312 中的全部汉字、非汉字符号。
    2. GB 13000.1 中的其他 CJK 汉字。以上合计 20902 个 GB 化汉字。
    3. 《简化字总表》中未收入 GB 13000.1 的 52 个汉字。
    4. 《康熙字典》及《辞海》中未收入 GB 13000.1 的 28 个部首及重要构件。
    5. 13 个汉字结构符。
    6. BIG-5 中未被 GB 2312 收入、但存在于 GB 13000.1 中的 139 个图形符号。
    7. GB 12345 增补的 6 个拼音符号。
    8. 汉字“○”。
    9. GB 12345 增补的 19 个竖排标点符号(GB 12345 较 GB 2312 增补竖排标点符号 29 个,其中 10 个未被 GB 13000.1 收入,故 GBK 亦不收)。
    10. 从 GB 13000.1 的 CJK 兼容区挑选出的 21 个汉字。
    11. GB 13000.1 收入的 31 个 IBM OS/2 专用符号。

    二、码位分配及顺序
    GBK 亦采用双字节表示,总体编码范围为 8140-FEFE,首字节在 81-FE 之间,尾字节在 40-FE 之间,剔除 xx7F 一条线。总计 23940 个码位,共收入 21886 个汉字和图形符号,其中汉字(包括部首和构件)21003 个,图形符号 883 个。
    全部编码分为三大部分:

    1. 汉字区。包括: a. GB 2312 汉字区。即 GBK/2: B0A1-F7FE。收录 GB 2312 汉字 6763 个,按原顺序排列。 b. GB 13000.1 扩充汉字区。包括: (1) GBK/3: 8140-A0FE。收录 GB 13000.1 中的 CJK 汉字 6080 个。

      (2) GBK/4: AA40-FEA0。收录 CJK 汉字和增补的汉字 8160 个。CJK 汉字在前,按 UCS 代码大小排列;增补的汉字(包括部首和构件)在后,按《康熙字典》的页码/字位排列。

    2. 图形符号区。包括: a. GB 2312 非汉字符号区。即 GBK/1: A1A1-A9FE。其中除 GB 2312 的符号外,还有 10 个小写罗马数字和 GB 12345 增补的符号。计符号 717 个。

      b. GB 13000.1 扩充非汉字区。即 GBK/5: A840-A9A0。BIG-5 非汉字符号、结构符和“○”排列在此区。计符号 166 个。

    3. 用户自定义区:分为(1)(2)(3)三个小区。 (1) AAA1-AFFE,码位 564 个。 (2) F8A1-FEFE,码位 658 个。

      (3) A140-A7A0,码位 672 个。
      第(3)区尽管对用户开放,但限制使用,因为不排除未来在此区域增补新字符的可能性。

    三、字形
    GBK 对字形作了如下的规定:
    1. 原则上与 GB 13000.1 G列(即源自中国大陆法定标准的汉字)下的字形/笔形保持一致。
    2. 在 CJK 汉字认同规则的总框架内,对所有的 GBK 编码汉字实施“无重码正形”(“GB 化”);即在不造成重码的前提下,尽量采用中国新字形。
    3. 对于超出 CJK 汉字认同规则的、或认同规则尚未明确规定的汉字,在 GBK 码位上暂安放旧字形。这样,在许多情况下 GBK 收入了同一汉字的新旧两种字形。
    4. 非汉字符号的字形,凡 GB 2312 已经包括的,与 GB 2312 保持一致;超出 GB 2312 的部分,与 GB 13000.1 保持一致。
    5. 带声调的拼音字母取半角形式。

    GBK编码是在GB2312-80(也称作GB2312,GB码)标准基础上的内码扩展规范,使用了双字节编码方案。

    GBK是国家标准GB2312基础上扩容后兼容GB2312的标准。GBK的文字编码是用双字节来表示的,即不论中、英文字符均使用双字节来表示,为了区分中文,将其最高位都设定成1。GBK包含全部中文字符,是国家编码,通用性比UTF8差,不过UTF8占用的数据库比GBK大

    GBK、GB2312等与UTF8之间都必须通过Unicode编码才能相互转换:

    GBK、GB2312<= = =>Unicode<= = =>UTF8

    补充:

     GB2312 最早一版的中文编码,每个字占据2bytes。由于要和ASCII兼容,那这2bytes最高位不可以为0   了(否则和ASCII会有冲突)。在GB2312中收录了6763个汉字以及682个特殊符号,已经囊括了生活中最  常用的所有汉字。

    ​ GBK 由于GB2312只有6763个汉字,我汉语博大精深,只有6763个字怎么够?于是GBK中在保证不和 GB2312、ASCII冲突(即兼容GB2312和ASCII)的前提下,也用每个字占据2bytes的方式又编码了许多 汉字。经过GBK编码后,可以表示的汉字达到了20902个,另有984个汉语标点符号、部首等。值得注意 的是这20902个汉字还包含了繁体字。

    ​ GB18030 然而,GBK的两万多字也已经无法满足我们的需求了,还有更多可能你自己从来没见过的汉字 需要编码。这时候显然只用2bytes表示一个字已经不够用了(2bytes最多只有65536种组合,然而为了和 ASCII兼容,最高位不能为0就已经直接淘汰了一半的组合,只剩下3万多种组合无法满足全部汉字求)。 因此GB18030多出来的汉字使用4bytes编码。当然,为了兼容GBK,这个四字节的前两位显然不能与 GBK冲突(实操中发现后两位也并没有和GBK冲突)。我国在2000年和2005年分别颁布的两次GB18030 编码,其中2005年的是在2000年基础上进一步补充。至此,GB18030编码的中文文件已经有七万多个汉 字了,甚至包含了少数民族文字。

GKB编码家族

​ 这图中展示了前文所述的几种编码在编码完成后,前2个byte的值的范围(用16进制表示)。每个byte可以表 示00到FF(即0至255)。从图中我们可以一目了然地看到为什么GB18030可以兼容GBK,GB2312和ASCII 了。他们几种编码之间前两位没有重合部分。需要注意的是ASCII只有1byte,所以是没有第二位的。另外 GB18030在上图中占的面积虽然很小,但是它是4bytes编码,这图只展示了前两位。如果后两位也算上, GB18030的字数要远多于GBK。另外需要注意的是,由于GBK兼容GB2312,因此属于GB2312的蓝色区域其 实也可以算作是GBK的区域。同理GBK的区域理论上也属于GB18030的区域。上表中只是展示了多出来的部 分。

​ 实际生活中,我们用到的99%以上的汉字,其实都在GB2312那一块区域内。至于GB2312每个编码对应的到 底是哪个汉字本文不再赘述,可以参考链接(链接地址)查询。GBK编码所对应的汉字可以参考链接 (链接地址)查询。至于GB18030编码,由于字数实在太多太难写,已经很难在网上找到在线的编码全表 了。不过经过一番搜寻,还是找到了我国发布GB18030编码时的相关文档(GB18030-2005文档GB18030-2000文档)。

​ 在实际使用中,GBK编码已经可以满足大部分场景了,GB18030编码中所有汉字都是我们这辈子都不一定能 见到的文字,这也是平时为什么经常会使用GBK的原因吧。

表的基本操作

##前提:先选取要操作的数据库

1)查看已有表
mysql>:show tables;

2)创建表
mysql>:create table 表名(字段1 类型, ..., 字段n 类型);
eg>: create table student(name varchar(16), age int);
eg>: create table teacher(name varchar(16), age int);

3)查看创建表的sql
mysql>:show create table 表名;
eg>: show create table student;


4)查看创建表的结构
mysql>:desc 表名;

5)改表结构
# 修改表名
mysql>: alter table 旧表 rename 新表;

# 修改字段名
mysql>: alter table 表名 change 旧字段 新字段 类型(长度);

# 修改字段属性
mysql>: alter table 表名 modify 字段 新类型(长度);

6)删除表
mysql>: drop table 表名;
eg>: drop table teacher;

创建表的完整语法

# 长度和约束在某些情况下是可以省略的
mysql>: create table 表名 (
    属性名1 类型(长度) 约束,
    ...
    属性名n 类型(长度) 约束
) engine=引擎 default charset=utf8;

记录的基本操作

1)查看某个数据库中的某个表的所有记录,如果在对应数据库中,可以直接查找表
mysql>: select * from [数据库名.]表名;
注:*代表查询所有字段

2)给表的所有字段插入数据
mysql>: insert [into] [数据库名.]表名 values (值1,...,值n);
eg:如果给有name和age两个字段的student表插入数据
1条>:insert into student values ('Bob', 18);
多条>:insert into student values ('张三', 18),('李四', 20);
指定库>:insert owen.student values ('张三', 18),('李四', 20);

3)根据条件修改指定内容
mysql>: update [数据库名.]表名 set 字段1=新值1, 字段n=新值n where 字段=旧值;
eg:> update student set name='王五', age='100' where name='张三';
注:i) 可以只修改部分字段 ii) 没有条件下,所有记录都会被更新
eg:> update student set name='呵呵';

4)根据条件删除记录
mysql>: delete from [数据库名.]表名 where 条件;
eg:> delete from student where age<30;

sql函数

函数及描述
NOW():返回当前的日期和时间
CURDATE():返回当前的日期
CURTIME():返回当前的时间
DATE():提取日期或日期/时间表达式的日期部分
EXTRACT():返回日期/时间按的单独部分
DATE_ADD():给日期添加指定的时间间隔
DATE_SUB():从日期减去指定的时间间隔
DATEDIFF():返回两个日期之间的天数
DATE_FORMAT():用不同的格式显示日期/时间

mysql支持的数据类型

# mysql数据库支持存放哪些数据

# 整型 | 浮点型 | 字符型 | 时间类型 | 枚举类型 | 集合类型

整型

类型 大小 范围(有符号) 范围(无符号) 用途
TINYINT 1 字节 (-128,127) (0,255) 小整数值
SMALLINT 2 字节 (-32 768,32 767) (0,65 535) 大整数值
MEDIUMINT 3 字节 (-8 388 608,8 388 607) (0,16 777 215) 大整数值
INT或INTEGER 4 字节 (-2 147 483 648,2 147 483 647) (0,4 294 967 295) 大整数值
BIGINT 8 字节 (-9,223,372,036,854,775,808,9 223 372 036 854 775 807) (0,18 446 744 073 709 551 615) 极大整数值
'''约束
unsigned:无符号
zerofill:0填充
'''
3位  1 001
# 建表
mysql>: create table tb1(x tinyint, y smallint, z int(6));

# 插入数据
mysql>: insert into tb1 values(128, 32768, 32768);  # 结果:127,32767,32768

# 结论:整型的长度由所占字节(取值范围)决定,可以自定义长度,但是不影响所占字节(取值范围)
#    所有整型变量的长度一般都省略不写,不同类型所占字节数不一样, 决定所占空间及存放数据的大小限制
结论>: create table tb1(x tinyint, y smallint, z int);


# 整型约束
mysql>: create table tb2(x tinyint unsigned);  # 0~255
mysql>: insert into tb2 values(256), (-1);    # 255, 0
    
# 0填充约束
mysql>: create table tb3(x tinyint unsigned zerofill);
mysql>: insert into tb3 values(10);  # 010

'''宽度
1.不能决定整型存放数据的宽度, 超过宽度可以存放, 最终由数据类型所占字节决定
2.如果没有超过宽度,且有zerofill限制, 会用0填充前置位的不足位
3.没有必要规定整型的宽度, 默认设置的宽度就为该整型能存放数据的最大宽度 *
'''

浮点型

类型 大小 范围(有符号) 范围(无符号) 用途
FLOAT 4 字节 (-3.402 823 466 E+38,-1.175 494 351 E-38),0,(1.175 494 351 E-38,3.402 823 466 351 E+38) 0,(1.175 494 351 E-38,3.402 823 466 E+38) 单精度 浮点数值
DOUBLE 8 字节 (-1.797 693 134 862 315 7 E+308,-2.225 073 858 507 201 4 E-308),0,(2.225 073 858 507 201 4 E-308,1.797 693 134 862 315 7 E+308) 0,(2.225 073 858 507 201 4 E-308,1.797 693 134 862 315 7 E+308) 双精度 浮点数值
DECIMAL 对DECIMAL(M,D) ,如果M>D,为M+2否则为D+2 依赖于M和D的值 依赖于M和D的值 小数值
# 在安全模式下测试浮点型类型

'''类型
float(M, D):4字节,3.4E–38~3.4E+38
double(M, D):8字节,1.7E–308~1.7E+308
decimal(M, D):所在字节M,D大值基础上+2,其实就是M值+2就是decimal字段所占字节数,(M, D) => M为位数,D为小数位
'''
'''宽度:
限制存储宽度
(M, D) => M为位数,D为小数位,M要大于等于D
float(255, 30):精度最低,最常用
double(255, 30):精度高,占位多
decimal(65, 30):字符串存,全精度
'''

# 建表:
mysql>: create table tb4 (age float(256, 30)); # Display width out of range for column 'age' (max = 255)
mysql>: create table tb5 (age float(255, 31)); # Too big scale 31 specified for column 'age'. Maximum is 30.
mysql>: create table tb5 (age float(65, 30)); # 在合理取值范围
    
mysql>: create table t12 (x float(255, 30));
mysql>: create table t13 (x double(255, 30));
mysql>: create table t14 (x decimal(65, 30));
# 1.111111164093017600000000000000 
mysql>: insert into t12 values(1.11111111111111111119);  
# 1.111111111111111200000000000000
mysql>: insert into t13 values(1.11111111111111111119);
# 1.111111111111111111190000000000
mysql>: insert into t14 values(1.11111111111111111119);

# 重点:长度与小数位分析
# 报错,总长度M必须大于等于小数位D
mysql>: create table t14 (x decimal(2, 3));
# 能存储 -0.9999
mysql>: create table t14 (x decimal(3, 3));

字符串:

数据库优化 - char效率要高于varchar

类型 大小 用途
CHAR 0-255字节 定长字符串
VARCHAR 0-65535 字节 变长字符串
TINYBLOB 0-255字节 不超过 255 个字符的二进制字符串
TINYTEXT 0-255字节 短文本字符串
BLOB 0-65 535字节 二进制形式的长文本数据
TEXT 0-65 535字节 长文本数据
MEDIUMBLOB 0-16 777 215字节 二进制形式的中等长度文本数据
MEDIUMTEXT 0-16 777 215字节 中等长度文本数据
LONGBLOB 0-4 294 967 295字节 二进制形式的极大文本数据
LONGTEXT 0-4 294 967 295字节 极大文本数据

​ CHAR 和 VARCHAR 类型类似,但它们保存和检索的方式不同。它们的最大长度和是否尾部空格被保留等方面 也不同。在存储或检索过程中不进行大小写转换。

​ BINARY 和 VARBINARY 类似于 CHAR 和 VARCHAR,不同的是它们包含二进制字符串而不要非二进制字符 串。也就是说,它们包含字节字符串而不是字符字符串。这说明它们没有字符集,并且排序和比较基于列值 字节的数值值。

​ BLOB 是一个二进制大对象,可以容纳可变数量的数据。有 4 种 BLOB 类型:TINYBLOB、BLOB、 MEDIUMBLOB 和 LONGBLOB。它们区别在于可容纳存储范围不同。

​ 有 4 种 TEXT 类型:TINYTEXT、TEXT、MEDIUMTEXT 和 LONGTEXT。对应的这 4 种 BLOB 类型,可存储的 最大长度不同,可根据实际情况选择。

'''类型
char:定长
varchar:不定长
'''
'''宽度
限制存储宽度
char(4):以4个字符存储定长存储数据
varchar(4):数据长度决定字符长度,为可变长度存储数据
'''
# eg:
create table t15 (x char(4), y varchar(4));
insert into t15 values("zero", 'owen'); # '' | "" 均可以表示字符
select x,y from t15; # 正常
insert into t15 values("yanghuhu", 'lxxVSegon'); # 非安全模式数据丢失,可以存放, 安全模式报错
select x,y from t15; # 可以正常显示丢失后(不完整)的数据
insert into t15 values('a', 'b');

# 验证数据所在字符长度
# 前提: 安全模式下以空白填充字符
set global sql_mode="strict_trans_tables,PAD_CHAR_TO_FULL_LENGTH";
# 重启连接
select char_length(x), char_length(y) from t15; # a占4 b占1

'''重点: 存储数据的方式 **  => 数据库优化
char: 一定按规定的宽度存放数据, 以规定宽度读取数据, 通常更占空间
varchar: 首先根据数据长度计算所需宽度, 并在数据开始以数据头方式将宽度信息保存起来, 是一个计算耗时过程, 取先读取宽度信息,以宽度信息为依准读取数据, 通常节省空间
'''
8: zero    egon    lxx     yanghuhu
8: 4zero4egon3lxx8yanghuhu
注: varchar的数据头占1~2字节
    规定char|varchar宽度均为4,用来存放4个字符的数据, char存取更高效,char占4字符,varchar占5字符,char更省空间

总结: 数据长度相近的数据提倡用char来存放数据, 数据需要高速存取,以空间换时间, 采用char

时间

​ 每个时间类型有一个有效值范围和一个"零"值,当指定不合法的MySQL不能表示的值时使用"零"值。

类型 大小 (字节) 范围 格式 用途
DATE 3 1000-01-01/9999-12-31 YYYY-MM-DD 日期值
TIME 3 '-838:59:59'/'838:59:59' HH:MM:SS 时间值或持续时间
YEAR 1 1901/2155 YYYY 年份值
DATETIME 8 1000-01-01 00:00:00/9999-12-31 23:59:59 YYYY-MM-DD HH:MM:SS 混合日期和时间值
TIMESTAMP 4 1970-01-01 00:00:00/2038结束时间是第 2147483647 秒,北京时间 2038-1-19 11:14:07,格林尼治时间 2038年1月19日 凌晨 03:14:07 YYYYMMDD HHMMSS 混合日期和时间值,时间戳
'''类型
year:yyyy(1901/2155)
date:yyyy-MM-dd(1000-01-01/9999-12-31)
time:HH:mm:ss
datetime:yyyy-MM-dd HH:mm:ss(1000-01-01 00:00:00/9999-12-31 23:59:59)
timestamp:yyyy-MM-dd HH:mm:ss(1970-01-01 00:00:00/2038-01-19 ??)
'''
# eg: 1
create table t16(my_year year, my_date date, my_time time);
insert into t16 values(); # 三个时间类型的默认值均是null
insert into t16 values(2156, null, null); # 在时间范围外,不允许插入该数据
insert into t16 values(1, '2000-01-01 12:00:00', null); # 2001 2000-01-01 null
insert into t16 values(2019, '2019-01-08', "15-19-30"); # time报格式错误 => 按照时间规定格式存放数据

alter table t16 change my_year myYear year(2); # 时间的宽度修改后还是采用默认宽度 => 不需要关系宽度


# eg:2
create table t17(my_datetime datetime, my_timestamp timestamp);
insert into t17 values(null, null); # 可以为空, 不能为null,赋值null采用默认值current_timestamp
insert into t17 values('4000-01-01 12:00:00', '2000-01-01 12:00:00'); # 在各自范围内可以插入对应格式的时间数据

# datetime VS timestamp
datetime:时间范围,不依赖当前时区,8字节,可以为null
timestamp:时间范围,依赖当前时区,4字节,有默认值CURRENT_TIMESTAMP

二进制数据(_Blob)

​ 1._BLOB和_text存储方式不同,_TEXT以文本方式存储,英文存储区分大小写,而_Blob是以二进制方式存储,不 分大小写。

​ 2._BLOB存储的数据只能整体读出。

​ 3._TEXT可以指定字符集,_BLO不用指定字符集。

MYSQL数据类型的长度和范围

各数据类型及字节长度一览表:

数据类型 字节长度 范围或用法
Bit 1 无符号[0,255],有符号[-128,127],天缘博客备注:BIT和BOOL布尔型都占用1字节
TinyInt 1 整数[0,255]
SmallInt 2 无符号[0,65535],有符号[-32768,32767]
MediumInt 3 无符号[0,2^24-1],有符号[-2^23,2^23-1]]
Int 4 无符号[0,2^32-1],有符号[-2^31,2^31-1]
BigInt 8 无符号[0,2^64-1],有符号[-2^63 ,2^63 -1]
Float(M,D) 4 单精度浮点数。天缘博客提醒这里的D是精度,如果D<=24则为默认的FLOAT,如果D>24则会自动被转换为DOUBLE型。
Double(M,D) 8 双精度浮点。
Decimal(M,D) M+1或M+2 未打包的浮点数,用法类似于FLOAT和DOUBLE,天缘博客提醒您如果在ASP中使用到Decimal数据类型,直接从数据库读出来的Decimal可能需要先转换成Float或Double类型后再进行运算。
Date 3 以YYYY-MM-DD的格式显示,比如:2009-07-19
Date Time 8 以YYYY-MM-DD HH:MM:SS的格式显示,比如:2009-07-19 11:22:30
TimeStamp 4 以YYYY-MM-DD的格式显示,比如:2009-07-19
Time 3 以HH:MM:SS的格式显示。比如:11:22:30
Year 1 以YYYY的格式显示。比如:2009
Char(M) M 定长字符串。
VarChar(M) M 变长字符串,要求M<=255
Binary(M) M 类似Char的二进制存储,特点是插入定长不足补0
VarBinary(M) M 类似VarChar的变长二进制存储,特点是定长不补0
Tiny Text Max:255 大小写不敏感
Text Max:64K 大小写不敏感
Medium Text Max:16M 大小写不敏感
Long Text Max:4G 大小写不敏感
TinyBlob Max:255 大小写敏感
Blob Max:64K 大小写敏感
MediumBlob Max:16M 大小写敏感
LongBlob Max:4G 大小写敏感
Enum 1或2 最大可达65535个不同的枚举值
Set 可达8 最大可达64个不同的值
Geometry
Point
LineString
Polygon
MultiPoint
MultiLineString
MultiPolygon
GeometryCollection

使用建议

​ 1、在指定数据类型的时候一般是采用从小原则,比如能用TINY INT的最好就不用INT,能用FLOAT类型的就 不用DOUBLE类型,这样会对MYSQL在运行效率上提高很大,尤其是大数据量测试条件下。

​ 2、不需要把数据表设计的太过复杂,功能模块上区分或许对于后期的维护更为方便,慎重出现大杂烩数据表

​ 3、数据表和字段的起名字也是一门学问

​ 4、设计数据表结构之前请先想象一下是你的房间,或许结果会更加合理、高效

​ 5、数据库的最后设计结果一定是效率和可扩展性的折中,偏向任何一方都是欠妥的

选择数据类型的基本原则

前提:使用适合存储引擎。

选择原则:根据选定的存储引擎,确定如何选择合适的数据类型。

下面的选择方法按存储引擎分类:

  • MyISAM 数据存储引擎和数据列:MyISAM数据表,最好使用固定长度(CHAR)的数据列代替可变长度(VARCHAR)的数据列。
  • MEMORY存储引擎和数据列:MEMORY数据表目前都使用固定长度的数据行存储,因此无论使用CHAR或VARCHAR列都没有关系。两者都是作为CHAR类型处理的。
  • InnoDB 存储引擎和数据列:建议使用 VARCHAR类型。

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

下面说一下固定长度数据列与可变长度的数据列。

char与varchar

CHAR和VARCHAR类型类似,但它们保存和检索的方式不同。它们的最大长度和是否尾部空格被保留等方面也不同。在存储或检索过程中不进行大小写转换。

下面的表显示了将各种字符串值保存到CHAR(4)和VARCHAR(4)列后的结果,说明了CHAR和VARCHAR之间的差别:

CHAR(4) 存储需求 VARCHAR(4) 存储需求
'' ' ' 4个字节 '' 1个字节
'ab' 'ab ' 4个字节 'ab ' 3个字节
'abcd' 'abcd' 4个字节 'abcd' 5个字节
'abcdefgh' 'abcd' 4个字节 'abcd' 5个字节

请注意上表中最后一行的值只适用不使用严格模式时;如果MySQL运行在严格模式,超过列长度不的值不保存,并且会出现错误。

从CHAR(4)和VARCHAR(4)列检索的值并不总是相同,因为检索时从CHAR列删除了尾部的空格。通过下面的例子说明该差别:
mysql> CREATE TABLE vc (v VARCHAR(4), c CHAR(4));
Query OK, 0 rows affected (0.02 sec)

mysql> INSERT INTO vc VALUES ('ab ', 'ab ');
Query OK, 1 row affected (0.00 sec)

mysql> SELECT CONCAT(v, '+'), CONCAT(c, '+') FROM vc;
+----------------+----------------+
| CONCAT(v, '+') | CONCAT(c, '+') |
+----------------+----------------+
| ab + | ab+ |
+----------------+----------------+
1 row in set (0.00 sec)

text和blob

在使用text和blob字段类型时要注意以下几点,以便更好的发挥数据库的性能。

①BLOB和TEXT值也会引起自己的一些问题,特别是执行了大量的删除或更新操作的时候。删除这种值会在数据表中留下很大的"空洞",以后填入这些"空洞"的记录可能长度不同,为了提高性能,建议定期使用 OPTIMIZE TABLE 功能对这类表进行碎片整理.

②使用合成的(synthetic)索引。合成的索引列在某些时候是有用的。一种办法是根据其它的列的内容建立一个散列值,并把这个值存储在单独的数据列中。接下来你就可以通过检索散列值找到数据行了。但是,我们要注意这种技术只能用于精确匹配的查询(散列值对于类似=等范围搜索操作符 是没有用处的)。我们可以使用MD5()函数生成散列值,也可以使用SHA1()或CRC32(),或者使用自己的应用程序逻辑来计算散列值。请记住数值型散列值可以很高效率地存储。同样,如果散列算法生成的字符串带有尾部空格,就不要把它们存储在CHAR或VARCHAR列中,它们会受到尾部空格去除的影响。

合成的散列索引对于那些BLOB或TEXT数据列特别有用。用散列标识符值查找的速度比搜索BLOB列本身的速度快很多。

③在不必要的时候避免检索大型的BLOB或TEXT值。例如,SELECT *查询就不是很好的想法,除非你能够确定作为约束条件的WHERE子句只会找到所需要的数据行。否则,你可能毫无目的地在网络上传输大量的值。这也是 BLOB或TEXT标识符信息存储在合成的索引列中对我们有所帮助的例子。你可以搜索索引列,决定那些需要的数据行,然后从合格的数据行中检索BLOB或 TEXT值。

④把BLOB或TEXT列分离到单独的表中。在某些环境中,如果把这些数据列移动到第二张数据表中,可以让你把原数据表中 的数据列转换为固定长度的数据行格式,那么它就是有意义的。这会减少主表中的碎片,使你得到固定长度数据行的性能优势。它还使你在主数据表上运行 SELECT *查询的时候不会通过网络传输大量的BLOB或TEXT值。

浮点数与定点数

为了能够引起大家的重视,在介绍浮点数与定点数以前先让大家看一个例子:
mysql> CREATE TABLE test (c1 float(10,2),c2 decimal(10,2));
Query OK, 0 rows affected (0.29 sec)

mysql> insert into test values(131072.32,131072.32);
Query OK, 1 row affected (0.07 sec)

mysql> select * from test;
+-----------+-----------+
| c1 | c2 |
+-----------+-----------+
| 131072.31 | 131072.32 |
+-----------+-----------+
1 row in set (0.00 sec)

从上面的例子中我们看到c1列的值由131072.32变成了131072.31,这就是浮点数的不精确性造成的。

在mysql中float、double(或real)是浮点数,decimal(或numberic)是定点数。

浮点数相对于定点数的优点是在长度一定的情况下,浮点数能够表示更大的数据范围;它的缺点是会引起精度问题。在今后关于浮点数和定点数的应用中,大家要记住以下几点:

  1. 浮点数存在误差问题;
  2. 对货币等对精度敏感的数据,应该用定点数表示或存储;
  3. 编程中,如果用到浮点数,要特别注意误差问题,并尽量避免做浮点数比较;
  4. 要注意浮点数中一些特殊值的处理。

补充

​ 1、一个汉字占多少长度与编码有关:

UTF-8:一个汉字=3个字节

GBK:一个汉字=2个字节

​ 2、varchar(n) 表示 n 个字符,无论汉字和英文,Mysql 都能存入 n 个字符,仅是实际字节长度有所区别

​ 3、MySQL 检查长度,可用 SQL 语言来查看:

select LENGTH(字段名) from 表名

枚举与集合

枚举类型,enum

每个枚举值均有一个索引值:

在列说明中列表值所允许的成员值被从 1 开始编号。

一般来说就是单选,在定义枚举的时候列出所有的可能性;

代码如下

1. create table type_enum(  
2. gender enum('male','remale','serect'),  
3. );  
4. insert into type_enum values ('remale');  

在处理时,类似字符串型进行操作!

意义在于:

1, 限定值的可能性!

2, 速度快,比普通的字符串速度快!

原因是枚举型 是利用 整数进行管理的,能够2个字节进行管理!

每个值,都是一个整数标识,从第一个选项开始为1,逐一递增!

管理时整数的形式,速度比字符串快!

一共有2 个字节,0-65535,因此可以有 65535个选项可以使用!、

集合 set 不定想项选

类似于 enum枚举,在定义时,也需要指定其已有值!

与字符串相比,优势是:

1, 也是采用 整数进行管理的!采用位运算,从第一位开始为1,逐一x2!

2, 每个集合类型8个字节,64位,因此可以表示64个元素!

注意:站在 mysql的角度,尽量多用枚举和集合!

扩展:

集合 set 扩展

在创建表时,就指定SET类型的取值范围。

属性名 SET('值1','值2','值3'...,'值n')

其中,“属性名”参数指字段的名称;“值n”参数表示列表中的第n个值,这些值末尾的空格将会被系统直接删除。其基本形式与ENUM类型一样。SET类型的值可以取列表中的一个元素或者多个元素的组合。取多个元素时,不同元素之间用逗号隔开。SET类型的值最多只能是有64个元素构成的组合,根据成员的不同,存储上也有所不同:

1~8成员的集合,占1个字节。
9~16成员的集合,占2个字节。
17~24成员的集合,占3个字节。
25~32成员的集合,占4个字节。
33~64成员的集合,占8个字节。

同ENUM类型一样,列表中的每个值都有一个顺序排列的编号。MySQL中存入的是这个编号,而不是列表中的值。

插入记录时,SET字段中的元素顺序无关紧要。存入MySQL数据库后,数据库系统会自动按照定义时的顺序显示。如果插入的成员中有重复,则只存储一次。

枚举类型,enum扩展

ENUM类型(枚举类型),与C#的概念一样,在定义时指定取值范围。

ENUM类型的值范围需要在创建表时通过枚举方式显式指定,对1~255个成员的枚举需要1个字节存储;对于256~65535个成员,需要2个字节存储。最多可以有65535个成员,而SET类型最多只能包含64个成员。两者的取值只能在成员列表中选取。ENUM类型只能从成员中选择一个,而SET类型可以选择多个。

因此,对于多个值中选取一个的,可以选择ENUM类型。例如,“性别”字段就可以定义成ENUM类型,因为只能在“男”和“女”中选其中一个。对于可以选取多个值的字段,可以选择SET类型。例如,“爱好”字段就可以选择SET类型,因为可能有多种爱好。

属性名 ENUM('值1','值2','值3'...'值n')

◆其中,属性名参数指字段的名称;“值n”参数表示列表中的第n个值,这些值末尾的空格将会被系统直接删除。ENUM类型的值只能取列表中的一个元素。其取值列表中最多能有65535个值。列表中的每个值都有一个顺序排列的编号,MySQL中存入的是这个编号,而不是列表中的值。

◆ENUM 有 NOT NULL 属性,其默认值为取值列表的第一个元素;
◆ENUM 无 NOT NULL,则ENUM类型将允许插入NULL,并且NULL为默认值;

CREATE TABLE Test4(Sex ENUM('男','女'));
INSERT INTO Test4 VALUES('男');
INSERT INTO Test4 VALUES('爷'); --这行报错
SELECT * FROM Test4;

ENUM 是一个字符串对象,其值通常选自一个允许值列表中,该列表在表创建时的列规格说明中被明确地列举。

在下列某些情况下,值也可以是空串('') 或 NULL:
◆如果将一个无效值插入一个 ENUM (即,一个不在允许值列表中的字符串),空字符串将作为一个特殊的错误值被插入。事实上,这个字符串有别于一个'普通的'空字符串,因为这个字符串有个数字索引值为 0。稍后有更详细描述。
◆如果一个 ENUM 被声明为 NULL,NULL 也是该列的一个合法值,并且该列的缺省值也将为 NULL 。如果一个 ENUM 被声明为 NOT NULL,该列的缺省值将是该列表所允许值的第一个成员。

每个枚举值均有一个索引值:
◆在列说明中列表值所允许的成员值被从 1 开始编号。
◆空字符串错误值的索引值为 0。这就意味着,你可以使用下面所示的 SELECT 语句找出被赋于无效 ENUM值的记录行。
mysql> SELECT * FROM tbl_name WHERE enum_col=0;
◆NULL 值的索引值为 NULL。

例如,指定为 ENUM('one', 'two', 'three') 的一个列,可以有下面所显示的任一值。每个值的索引值也如下所示:

索引值
NULL NULL
'' 0
'one' 1
'two' 2
'three' 3

换个枚举最大可以有 65535 个成员值。

从 MySQL 3.23.51 开始,当表被创建时,ENUM 值尾部的空格将会自动删除。

当为一个 ENUM 列赋值时,字母的大小写是无关紧要的。然而,以后从列中检索出来的值的大小写却是匹配于创建表时所指定的允许值。

如果在一个数字语境中检索一个ENUM,列值的索引值将被返回。例如,你可以像这样使用数字值检索一个 ENUM 列:

mysql> SELECT enum_col+0 FROM tbl_name;

如果将一个数字存储到一个 ENUM 中,数字被当作为一个索引值,并且存储的值是该索引值所对应的枚举成员。(但是,这在 LOAD DATA 将不能工作,因为它视所有的输入均为字符串。) 在一个 ENUM 字符串中存储数字是不明智的,因为它可能会打乱思维。

ENUM 值依照列规格说明中的列表顺序进行排序。(换句话说,ENUM 值依照它们的索引号排序。)举例来说,对于 ENUM('a', 'b') 'a' 排在 'b' 后,但是对于 ENUM('b', 'a') , 'b' 却排在 'a' 之前。空字符串排在非空字符串前,NULL 值排在其它所有的枚举值前。为了防止意想不到的结果,建议依照字母的顺序定义 ENUM 列表。也可以通过使用 GROUP BY CONCAT(col) 来确定该以字母顺序排序而不是以索引值。

如果希望得到一个 ENUM 列的所有可能值,可以使用:

SHOW COLUMNS FROM table_name LIKE enum_colum;

SET 与 ENUM 类型的查询修改操作
SET FOREIGN_KEY_CHECKS=0;


-- Table structure for setenum
-- ----------------------------
DROP TABLE IF EXISTS setenum;
CREATE TABLE setenum (
id int(11) NOT NULL AUTO_INCREMENT,
settype set('we','周','李','孙','钱','赵') DEFAULT NULL,
enumtype enum('ZZZ','南海','长江','黄河') DEFAULT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;


-- Records of setenum
-- ----------------------------
INSERT INTO setenum VALUES ('1', 'we,周,钱', '南海');
INSERT INTO setenum VALUES ('2', '钱,赵', '黄河');
INSERT INTO setenum VALUES ('3', 'we,赵', '南海');
INSERT INTO setenum VALUES ('4', '李,孙,钱', '长江');

set('we','周','李','孙','钱','赵')=111111=63
enum('ZZZ','南海','长江','黄河')=100=4

如下表所示:

SET类型:低位(右) → 高位(左)

we
1 1 1 1 1 1

从右到左排:11111=63

we
1 1 0 0 1 0

从右到左排:010011=19

we
0 0 0 0 1 1

从右到左排:110000=48

we
1 0 0 0 0 1

从右到左排:100001=33

we
0 0 1 1 1 0

从右到左排:011100=28

ENUM类型

ZZZ 南海 长江 黄河
1 2 3 4
ZZZ 南海 长江 黄河
1 2 3 4

黄河=4=100

ZZZ 南海 长江 黄河
1 2 3 4

长江=3=11

ZZZ 南海 长江 黄河
1 2 3 4

南海=2=10

ZZZ 南海 长江 黄河
1 2 3 4

ZZZ=1=1

mysql> select * from setenum;
+----+----------+----------+
| id | settype | enumtype |
+----+----------+----------+
| 1 | we,周,钱 | 南海 |
| 2 | 钱,赵 | 黄河 |
| 3 | we,赵 | 南海 |
| 4 | 李,孙,钱 | 长江 |
+----+----------+----------+
4 rows in set

mysql> select settype,settype+0,bin(settype+0),enumtype,enumtype+0,bin(enumtype+0) from setenum;
+----------+-----------+----------------+----------+------------+-----------------+
| settype | settype+0 | bin(settype+0) | enumtype | enumtype+0 | bin(enumtype+0) |
+----------+-----------+----------------+----------+------------+-----------------+
| we,周,钱 | 19 | 10011 | 南海 | 2 | 10 |
| 钱,赵 | 48 | 110000 | 黄河 | 4 | 100 |
| we,赵 | 33 | 100001 | 南海 | 2 | 10 |
| 李,孙,钱 | 28 | 11100 | 长江 | 3 | 11 |
+----------+-----------+----------------+----------+------------+-----------------+
4 rows in set

mysql> select * from setenum where settype=33;
+----+---------+----------+
| id | settype | enumtype |
+----+---------+----------+
| 3 | we,赵 | 南海 |
+----+---------+----------+
1 row in set

mysql> select * from setenum where enumtype=2;
+----+----------+----------+
| id | settype | enumtype |
+----+----------+----------+
| 1 | we,周,钱 | 南海 |
| 3 | we,赵 | 南海 |
+----+----------+----------+
2 rows in set

--不支持二进制查询
mysql> select * from setenum where settype=b'010011';
Empty set

mysql> select * from setenum where settype=b'10011';
Empty set

mysql> select * from setenum where enumtype=b'100';
Empty set

mysql> SELECT * FROM setenum WHERE settype LIKE '%赵%';
+----+---------+----------+
| id | settype | enumtype |
+----+---------+----------+
| 2 | 钱,赵 | 黄河 |
| 3 | we,赵 | 南海 |
+----+---------+----------+
2 rows in set

--与FIND_IN_SET函数同
mysql> SELECT * FROM setenum WHERE FIND_IN_SET('赵',settype)>0;
+----+---------+----------+
| id | settype | enumtype |
+----+---------+----------+
| 2 | 钱,赵 | 黄河 |
| 3 | we,赵 | 南海 |
+----+---------+----------+
2 rows in set

--当查询只是集合某个值的一部分时,与FIND_IN_SET函数不同
mysql> SELECT * FROM setenum WHERE FIND_IN_SET('e',settype)>0;
Empty set

mysql> SELECT * FROM setenum WHERE settype LIKE '%e%';
+----+----------+----------+
| id | settype | enumtype |
+----+----------+----------+
| 1 | we,周,钱 | 南海 |
| 3 | we,赵 | 南海 |
+----+----------+----------+
2 rows in set

mysql> SELECT * FROM setenum WHERE settype LIKE '赵';
Empty set

mysql> SELECT * FROM setenum WHERE settype = '赵';
Empty set

mysql> SELECT * FROM setenum WHERE settype LIKE 'we,赵';
+----+---------+----------+
| id | settype | enumtype |
+----+---------+----------+
| 3 | we,赵 | 南海 |
+----+---------+----------+
1 row in set

mysql> SELECT * FROM setenum WHERE settype = 'we,赵';
+----+---------+----------+
| id | settype | enumtype |
+----+---------+----------+
| 3 | we,赵 | 南海 |
+----+---------+----------+
1 row in set

--如果把集合的顺序改一下,照样无效
mysql> SELECT * FROM setenum WHERE settype LIKE '赵,we';
Empty set

mysql> SELECT * FROM setenum WHERE settype = '赵,we';
Empty set

mysql> SELECT * FROM setenum WHERE enumtype LIKE '%海%';

+----+----------+----------+
| id | settype | enumtype |
+----+----------+----------+
| 1 | we,周,钱 | 南海 |
| 3 | we,赵 | 南海 |
+----+----------+----------+
2 rows in set

mysql> SELECT * FROM setenum WHERE enumtype = '南海';
+----+----------+----------+
| id | settype | enumtype |
+----+----------+----------+
| 1 | we,周,钱 | 南海 |
| 3 | we,赵 | 南海 |
+----+----------+----------+
2 rows in set

mysql> SELECT * FROM setenum WHERE enumtype = '海';
Empty set

--------------------UPDATE 语法--------------------

set('we','周','李','孙','钱','赵')=111111=63

mysql> select settype,settype+0,bin(settype+0),enumtype,enumtype+0,bin(enumtype+0) from setenum where id=1;
+----------+-----------+----------------+----------+------------+-----------------+
| settype | settype+0 | bin(settype+0) | enumtype | enumtype+0 | bin(enumtype+0) |
+----------+-----------+----------------+----------+------------+-----------------+
| we,周,钱 | 19 | 10011 | 南海 | 2 | 10 |
+----------+-----------+----------------+----------+------------+-----------------+
1 row in set

mysql> update setenum set settype = 2 where id=1;
Query OK, 1 row affected
Rows matched: 1 Changed: 1 Warnings: 0

mysql> select settype,settype+0,bin(settype+0),enumtype,enumtype+0,bin(enumtype+0) from setenum where id=1;
+---------+-----------+----------------+----------+------------+-----------------+
| settype | settype+0 | bin(settype+0) | enumtype | enumtype+0 | bin(enumtype+0) |
+---------+-----------+----------------+----------+------------+-----------------+
| 周 | 2 | 10 | 南海 | 2 | 10 |
+---------+-----------+----------------+----------+------------+-----------------+
1 row in set

--修改settype让其'we'、'周'、'李' 成员为真
mysql> update setenum set settype =settype|1|4|6 where id=1;
Query OK, 1 row affected
Rows matched: 1 Changed: 1 Warnings: 0

--|1|4|6 表示 二进制的OR运算 1 or 100 or 110 = 111 = 7
--实际与 1|6 结果是一样的:1 or 110 = 111 = 7

we
1 1 1 0 0 0

从右到左排:000111=7

mysql> select settype,settype+0,bin(settype+0),enumtype,enumtype+0,bin(enumtype+0) from setenum where id=1;
+----------+-----------+----------------+----------+------------+-----------------+
| settype | settype+0 | bin(settype+0) | enumtype | enumtype+0 | bin(enumtype+0) |
+----------+-----------+----------------+----------+------------+-----------------+
| we,周,李 | 7 | 111 | 南海 | 2 | 10 |
+----------+-----------+----------------+----------+------------+-----------------+
1 row in set

--实际与 1|6 结果是一样的:1 or 110 = 111 = 7
mysql> update setenum set settype =settype|1|6 where id=1;
Query OK, 0 rows affected
Rows matched: 1 Changed: 0 Warnings: 0

mysql> select settype,settype+0,bin(settype+0),enumtype,enumtype+0,bin(enumtype+0) from setenum where id=1;
+----------+-----------+----------------+----------+------------+-----------------+
| settype | settype+0 | bin(settype+0) | enumtype | enumtype+0 | bin(enumtype+0) |
+----------+-----------+----------------+----------+------------+-----------------+
| we,周,李 | 7 | 111 | 南海 | 2 | 10 |
+----------+-----------+----------------+----------+------------+-----------------+
1 row in set

--settype|1|6 的settype 可以省略,结果一样
mysql> update setenum set settype = 1|6 where id=1;
Query OK, 1 row affected
Rows matched: 1 Changed: 1 Warnings: 0

mysql> select settype,settype+0,bin(settype+0),enumtype,enumtype+0,bin(enumtype+0) from setenum where id=1;
+----------+-----------+----------------+----------+------------+-----------------+
| settype | settype+0 | bin(settype+0) | enumtype | enumtype+0 | bin(enumtype+0) |
+----------+-----------+----------------+----------+------------+-----------------+
| we,周,李 | 7 | 111 | 南海 | 2 | 10 |
+----------+-----------+----------------+----------+------------+-----------------+
1 row in set

-- &表示与运算,1 and 110 = 0,注意0与NULL不同
mysql> update setenum set settype = 1 & 6 where id=1;
Query OK, 1 row affected
Rows matched: 1 Changed: 1 Warnings: 0

mysql> select settype,settype+0,bin(settype+0),enumtype,enumtype+0,bin(enumtype+0) from setenum where id=1;
+---------+-----------+----------------+----------+------------+-----------------+
| settype | settype+0 | bin(settype+0) | enumtype | enumtype+0 | bin(enumtype+0) |
+---------+-----------+----------------+----------+------------+-----------------+
| | 0 | 0 | 南海 | 2 | 10 |
+---------+-----------+----------------+----------+------------+-----------------+
1 row in set

-- &表示与运算,~表示反运算,6=110,~6=001,1 and 001 = 1
mysql> update setenum set settype = null where id=1;
Query OK, 1 row affected
Rows matched: 1 Changed: 1 Warnings: 0

mysql> select settype,settype+0,bin(settype+0),enumtype,enumtype+0,bin(enumtype+0) from setenum where id=1;
+---------+-----------+----------------+----------+------------+-----------------+
| settype | settype+0 | bin(settype+0) | enumtype | enumtype+0 | bin(enumtype+0) |
+---------+-----------+----------------+----------+------------+-----------------+
| NULL | NULL | NULL | 南海 | 2 | 10 |
+---------+-----------+----------------+----------+------------+-----------------+
1 row in set

mysql> update setenum set settype = 1 & ~6 where id=1;
Query OK, 1 row affected
Rows matched: 1 Changed: 1 Warnings: 0

mysql> select settype,settype+0,bin(settype+0),enumtype,enumtype+0,bin(enumtype+0) from setenum where id=1;
+---------+-----------+----------------+----------+------------+-----------------+
| settype | settype+0 | bin(settype+0) | enumtype | enumtype+0 | bin(enumtype+0) |
+---------+-----------+----------------+----------+------------+-----------------+
| we | 1 | 1 | 南海 | 2 | 10 |
+---------+-----------+----------------+----------+------------+-----------------+
1 row in set

-- 5 and ~1 = 101 and ~1 = 101 and 111110 = 100 = 4
mysql> update setenum set settype = null where id=1;
Query OK, 1 row affected
Rows matched: 1 Changed: 1 Warnings: 0

mysql> update setenum set settype = 5 & ~1 where id=1;
Query OK, 1 row affected
Rows matched: 1 Changed: 1 Warnings: 0

we
0 0 1 0 0 0

从右到左排:000100=4

mysql> select settype,settype+0,bin(settype+0),enumtype,enumtype+0,bin(enumtype+0) from setenum where id=1;
+---------+-----------+----------------+----------+------------+-----------------+

settype settype+0 bin(settype+0) enumtype enumtype+0 bin(enumtype+0)

+---------+-----------+----------------+----------+------------+-----------------+
| 李 | 4 | 100 | 南海 | 2 | 10 |
| ---- | ---- | ---- | ---- | ---- | ---- |
| | | | | | |
+---------+-----------+----------------+----------+------------+-----------------+
1 row in set

枚举和集合的简单运用
# 枚举与集合:为某一个字段提供选项的 - 枚举只能单选(1个),集合可以多选(0-n个)

# 建表
# enum、set默认值为NULL
mysql>: create table tc1 (name varchar(20), sex enum('男', '女', '哇塞'), hobbies set('男', '女', '哇塞'));
mysql>: insert into tc1 values('ruakei', '哇塞哇塞', '未知');  
    
# enum、set手动设置默认值 '男' 与 '哇塞'
mysql>: create table tc2 (name varchar(20), sex enum('男', '女', '哇塞') default '男', hobbies set('男', '女', '哇塞') default '哇塞');
mysql>: insert into tc2 values('ruakei', '哇塞哇塞', '未知');  
mysql>: insert into tc2(name) values('ruakei');
    
# 对sex、hobbies两个字段赋值错误,系统默认用空字符串填充(非安全模式),安全模式抛异常
# 如果对出sex、hobbies两个字段外的其他字段进行赋值,这两个字段会才有默认值

# 注:对set类型的字段进行赋值,用一个字符串,字符串内部用,将多个选项隔开,且不能添加空格等其他额外字符
mysql>: insert into tc2 values('ruakei_1', '女', '男,女,哇塞');  

约束

sql约束:约束用于限制加入表的数据的类型。是一种限制,它通过对表的行或列的数据做出限制,来确保表数据的完整性、唯一性可以在创建表时规定约束(通过 CREATE TABLE 语句),或者在表创建之后也可以(通过 ALTER TABLE 语句)。

数据类型的属性

MySQL关键字 含义
NULL 数据列可包含NULL值
NOT NULL 数据列不允许包含NULL值
DEFAULT 默认值
PRIMARY KEY 主键
AUTO_INCREMENT 自动递增,适用于整数类型
UNSIGNED 无符号
CHARACTER SET name 指定一个字符集
1、not null 约束:强制列不接受 NULL 值,强制字段始终包含值。这意味着,如果不向字段添加值,就无法插入新记录或者更新记录。
例:create table table_name(id int not null,name varchar(255) not null); # 强制id和name不能为空

2、unique 约束:唯一标识数据库表中的每条记录,确保表中的一列数据没有相同的值
UNIQUE 和 PRIMARY KEY 约束均为列或列集合提供了唯一性的保证。
PRIMARY KEY 拥有自动定义的 UNIQUE 约束。
每个表可以有多个 UNIQUE 约束,但是每个表只能有一个 PRIMARY KEY 约束
例:create table时在id列创建unique约束
create table table_name(id int not null,name varchar(255) not null, unique (id));
例2:为多个列定义unique
create table table_name(id int not null,name varchar(255) not null, constraint uc_personID unique (id,name))
例3:表已创建情况下,创建unique约束
alter table table_name add unique(id);
多个:alter table table_name add constraint uc_personid unique(id,name);
例4:撤销unique
alter table table_name drop index uc_personid;

3、PRIMARY KEY 约束:PRIMARY KEY 约束唯一标识数据库表中的每条记录。
主键必须包含唯一的值。
主键列不能包含 NULL 值。
每个表都应该有一个主键,并且每个表只能有一个主键。 primary key = unique +  not null 
例:create table时在id列创建PRIMARY KEY约束
create table table_name(id int not null,name varchar(255) not null, PRIMARY KEY(id));
例2:为多个列定义PRIMARY KEY
create table table_name(id int not null,name varchar(255) not null, constraint pk_personID PRIMARY KEY (id,name))
例3:表已创建情况下,创建PRIMARY KEY约束
alter table table_name add PRIMARY KEY(id);
多个:alter table table_name add constraint pk_personid PRIMARY KEY(id,name);
例4:撤销PRIMARY KEY
alter table table_name drop index uc_personid;

4、FOREIGN KEY 约束:一个表中的 FOREIGN KEY 指向另一个表中的 PRIMARY KEY
外键
外键用来在两个表数据之间建立链接,它可以是一列或多列。一个表可以有一个或多个外键
外键对应得是参照完整性,一个表得外键可以为空值,若不为空值,则每一个外键值必须等于另一个表中主键得某个值。
FOREIGN KEY 约束用于预防破坏表之间连接的动作。
FOREIGN KEY 约束也能防止非法数据插入外键列,因为它必须是它指向的那个表中的值之一。
例:create table时在id列创建 FOREIGN KEY 约束
create table table_name1(id int not null,name varchar(255) not null,Id_P int, PRIMARY KEY(id),FOREIGN KEY (Id_P) REFERENCES table_name2(Id_P));
例2:为多个列定义 FOREIGN KEY 
create table table_name1(id int not null,name varchar(255) not null,Id_P int, PRIMARY KEY(id) constraint fk_pertb2 FOREIGN KEY (Id_P)
REFERENCES table_name2(Id_P) )
例3:表已创建情况下,创建 FOREIGN KEY 约束
alter table table_name add ADD FOREIGN KEY (Id_P) REFERENCES table_name1(Id_P);
多个:alter table table_name add constraint pk_personid PRIMARY KEY(id,name);
例4:撤销 FOREIGN KEY 
alter table table_name drop FOREIGN KEY fk_pertb2;

外键约束
mysql> create table bookcategory(
    -> category_id int primary key,
    -> category varchar(20),
    -> parent_id int);
mysql> create table bookinfo(
    -> book_id int primary key,
    -> book_category_id int,
    -> constraint fk_cid foreign key(book_category_id) references bookcategory(category_id));

5、CHECK 约束:用于限制列中的值的范围
如果对单个列定义 CHECK 约束,那么该列只允许特定的值。
如果对一个表定义 CHECK 约束,那么此约束会在特定的列中对值进行限制。
例:create table时在id列创建 CHECK 约束
create table table_name(id int not null,name varchar(255) not null,CHECK(id>0));
例2:为多个列定义 CHECK 约束
create table table_name(id int not null,name varchar(255) not null, constraint chk_tbname CHECK(id>0 and name='xxx'));
例3:表已创建情况下,创建 CHECK 约束
alter table table_name ADD constraint chk_tbname CHECK (Id_P>0 AND name='xxx'); 
例4:撤销 CHECK
alter table table_name drop constraint chk_tbname;

6、DEFAULT 约束:用于向列中插入默认值
如果没有规定其他的值,那么会将默认值添加到所有的新记录。
例:create table时在id列创建 DEFAULT 约束,类似 GETDATE() 这样的函数,DEFAULT 约束也可以用于插入系统值
create table table_name(id int not null,name varchar(255) not null DEFAULT 'lxq',timedate date DEFAULT GETDATE());
例2:表已创建情况下,创建 DEFAULT 约束
alter table table_name alter name set DEFAULT 'lxq2';
例3:撤销 DEFAULT
alter table table_name ALTER name DROP DEFAULT;
约束条件的简单运用
"""
primary key:主键,唯一标识,表都会拥有,不设置为默认找第一个 不空,唯一 字段,未标识则创建隐藏字段
foreign key:外键
unique:唯一性数据, 该条字段的值需要保证唯一,不能重复

auto_increment:自增,只能加给key的int类型字段辅助修饰

not null:不为空 - 针对一些字段,如注册时的用户名,出生人的性别等,这些需求下的字段,只不能设置为Null,必须要对其赋值
null 可为空
default:默认值 - 对有默认值意外的字段进行赋值时,有默认值的字段会被赋默认值

unsigned:无符号 - 存储的数字从0开始
zerofill:0填充 - 存整数时数据长度小于取值范围长度,会在数字左方用0填充
"""

mysql>: create table td1 (x int, y int default 0, z int default 100);
    
#清空表,并清空主键自增记录
truncate table 数据库.表名;

注:
1.键是用来讲的io提供存取效率
2.联合唯一
create table web (
    ip char(16),
    port int,
    unique(ip,port)
);
3.联合主键
create table web (
    ip char(16),
    port int,
    primary key(ip,port)
);

# eg:1
# 单列唯一
create table t20 (
    id int unique
);
# 联合唯一
create table web (
    ip char(16),
    port int,
    unique(ip,port)
);
# 如果联合两个字段,两个字段全相同才相同,否则为不同
insert into web values ('10.10.10.10', 3306), ('10.10.10.10', 3306);

# 注: 
# 1.表默认都有主键, 且只能拥有一个主键字段(单列主键 | 联合主键)
# 2.没有设置主键的表, 数据库系统会自上而下将第一个规定为unique not null字段自动提升为primary key主键
# 3.如果整个表都没有unique not null字段且没有primary key字段, 系统会默认创建一个隐藏字段作为主键
# 4.通常必须手动指定表的主键, 一般用id字段, 且id字段一般类型为int, 因为int类型可以auto_increment

# eg:2
create table t21(id int auto_increment); # 自增约束必须添加给key的字段
# eg:3
create table t21(id int primary key auto_increment); # 自增要结合key,不赋值插入,数据会自动自增, 且自增的结果一直被记录保留
# eg:4
# 联合主键
create table t22(
    ip char(16),
    port int,
    primary key(ip,port)
);
# 如果联合两个字段,两个字段全相同才相同,否则为不同
insert into web values ('10.10.10.10', 3306), ('10.10.10.10', 3306);

表中字段的详细操作

create table t2(
    id int primary key auto_increment,
    x int,
    y int
);
insert into t2(x, y) values(10, 20), (100, 200), (1000, 2000);

'''
1.修改字段信息
alter table 表名 modify 字段名 类型[(宽度) 约束];
alter table t2 modify x bigint default 0;  # 模式不同, 涉及精度问题

2.修改字段名及信息
alter table 表名 change 旧字段名 新字段名 类型[(宽度) 约束];
alter table t2 change y c char(10) not null; # 模式不同, 涉及类型转换问题

3.添加字段名
# 末尾添加
alter table 表名 add 字段名 类型[(宽度) 约束], ..., add 字段名 类型[(宽度) 约束];
alter table t2 add age int, add gender enum("male", "female", "wasai") default "wasai";

# t头部添加
alter table 表名 add 字段名 类型[(宽度) 约束] first;

# 指定位添加:指定字段后
alter table 表名 add 字段名 类型[(宽度) 约束] after 旧字段名;
alter table t2 add y int after x;

4.删除字段名
alter table 表名 drop 字段名;
alter table t2 drop y;
'''

扩展

目前关系数据库有六种范式:第一范式(1NF)、第二范式(2NF)、第三范式(3NF)、巴斯-科德范式(BCNF)、第四范式(4NF)和第五范式(5NF,又称完美范式)。一般说来,数据库只需满足第三范式(3NF)就行了。下面用画图方式简单介绍下前三范式

三范式

1NF:字段不可分;
2NF:有主键,非主键字段依赖主键;
3NF:非主键字段不能相互依赖;

解释:
1NF:原子性 字段不可再分,否则就不是关系数据库;
2NF:唯一性 一个表只说明一个事物;
3NF:每列都与主键有直接关系,不存在传递依赖;

1NF:无重复的列,每一列都是不可分割的基本数据项,同一 列中不能有多个值,即实体中的某个属性不能有多个值或者不 能有重复的属性。除去同类型的字段,就是无重复的列
说明:第一范式(1NF)是对关系模式的基本要求,不满足第 一范式(1NF)的数据库就不是关系数据库
mysql 三范式介绍


2NF:属性完全依赖于主键,第二范式必须先满足第一范式, 要求表中的每个行必须可以被唯一地区分。通常为表加上一个 列,以存储各个实例的唯一标识PK,非PK的字段需要与整个 PK有直接相关性
mysql 三范式介绍


3NF:属性不依赖于其它非主属性,满足第三范式必须先满足 第二范式。第三范式要求一个数据库表中不包含已在其它表中 已包含的非主关键字信息,非PK的字段间不能有从属关系
mysql 三范式介绍

4、BCNF(BC范式)

它构建在第三范式的基础上,如果关系模型R是第一范式,且每个属性都不传递依赖于R的候选键,那么称R为BCNF的模式。

假设仓库管理关系表(仓库号,存储物品号,管理员号,数量),满足一个管理员只在一个仓库工作;一个仓库可以存储多种物品,则存在如下关系:

(仓库号,存储物品号)——>(管理员号,数量)

(管理员号,存储物品号)——>(仓库号,数量)

所以,(仓库号,存储物品号)和(管理员号,存储物品号)都是仓库管理关系表的候选码,表中唯一非关键字段为数量,它是符合第三范式的。但是,由于存在如下决定关系:

(仓库号)——>(管理员号)

(管理员号)——>(仓库号)

即存在关键字段决定关键字段的情况,因此其不符合BCNF。把仓库管理关系表分解为两个关系表仓库管理表(仓库号,管理员号)和仓库表(仓库号,存储物品号,数量),这样这个数据库表是符合BCNF的,并消除了删除异常、插入异常和更新异常。

5、4NF(第四范式)

设R是一个关系模型,D是R上的多值依赖集合。如果D中存在凡多值依赖X->Y时,X必是R的超键,那么称R是第四范式的模式。

例如,职工表(职工编号,职工孩子姓名,职工选修课程),在这个表中,同一个职工可能会有多个职工孩子姓名,同样,同一个职工也可能会有多个职工选修课程,即这里存在着多值事实,不符合第四范式。如果要符合第四范式,只需要将上表分为两个表,使它们只有一个多值事实,例如职工表一(职工编号,职工孩子姓名),职工表二(职工编号,职工选修课程),两个表都只有一个多值事实,所以符合第四范式。

拓展:各范式的关系图如下所示:

img

表关系

社会中存储需要可以构建成表的数据, 它们形成的表,往往之间存储某种或某些社会关系,

mysql数据库建立表结构就是社会中产生的各种数据, 分门别类管理

但mysql建立的(代码层次的)表之间, 同样需要处理表与表之间的关系

形成了 一对一|一对多 | 多对多 三种关系

外键

一、基本概念

1、MySQL中“键”和“索引”的定义相同,所以外键和主键一样也是索引的一种。不同的是MySQL会自动为所有表的主键进行索引,但是外键字段必须由用户进行明确的索引。用于外键关系的字段必须在所有的参照表中进行明确地索引,InnoDB不能自动地创建索引。

2、外键可以是一对一的,一个表的记录只能与另一个表的一条记录连接,或者是一对多的,一个表的记录与另一个表的多条记录连接。

3、如果需要更好的性能,并且不需要完整性检查,可以选择使用MyISAM表类型,如果想要在MySQL中根据参照完整性来建立表并且希望在此基础上保持良好的性能,最好选择表结构为innoDB类型。

4、外键的使用条件

① 两个表必须是InnoDB表,MyISAM表暂时不支持外键

② 外键列必须建立了索引,MySQL 4.1.2以后的版本在建立外键时会自动创建索引,但如果在较早的版本则需要显式建立;

③ 外键关系的两个表的列必须是数据类型相似,也就是可以相互转换类型的列,比如int和tinyint可以,而int和char则不可以;

5、外键的好处:可以使得两张表关联,保证数据的一致性和实现一些级联操作。保持数据一致性,完整性,主要目的是控制存储在外键表中的数据。 使两张表形成关联,外键只能引用外表中的列的值!可以使得两张表关联,保证数据的一致性和实现一些级联操作;

二、使用方法

1、创建外键的语法:

外键的定义语法:

[CONSTRAINT symbol] FOREIGN KEY [id] (index_col_name, ...)

REFERENCES tbl_name (index_col_name, ...)

[ON DELETE {RESTRICT | CASCADE | SET NULL | NO ACTION | SET DEFAULT}]

[ON UPDATE {RESTRICT | CASCADE | SET NULL | NO ACTION | SET DEFAULT}]

该语法可以在 CREATE TABLE 和 ALTER TABLE 时使用,如果不指定CONSTRAINT symbol,MYSQL会自动生成一个名字。

ON DELETE、ON UPDATE表示事件触发限制,可设参数:

① RESTRICT(限制外表中的外键改动,默认值)

② CASCADE(跟随外键改动)

③ SET NULL(设空值)

④ SET DEFAULT(设默认值)

⑤ NO ACTION(无动作,默认的)

2、示例

1)创建表1

create table repo_table(

repo_id char(13) not null primary key,

repo_name char(14) not null)

type=innodb;

创建表2

mysql> create table busi_table(

-> busi_id char(13) not null primary key,

-> busi_name char(13) not null,

-> repo_id char(13) not null,

-> foreign key(repo_id) references repo_table(repo_id))

-> type=innodb;

2)插入数据

insert into repo_table values("12","sz"); //success

insert into repo_table values("13","cd"); //success

insert into busi_table values("1003","cd", "13"); //success

insert into busi_table values("1002","sz", "12"); //success

insert into busi_table values("1001","gx", "11"); //failed,提示:

ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint fails (smb_man.busi_table, CONSTRAINT busi_table_ibfk_1 FOREIGN KEY (repo_id) REFERENCES repo_table (repo_id))

3)增加级联操作

mysql> alter table busi_table

-> add constraint id_check

-> foreign key(repo_id)

-> references repo_table(repo_id)

-> on delete cascade

-> on update cascade;

-----

ENGINE=InnoDB DEFAULT CHARSET=gb2312; //另一种方法,可以替换type=innodb;

3、相关操作

外键约束(表2)对父表(表1)的含义:

在父表上进行update/delete以更新或删除在子表中有一条或多条对应匹配行的候选键时,父表的行为取决于:在定义子表的外键时指定的on update/on delete子句。

关键字 含义
CASCADE 删除包含与已删除键值有参照关系的所有记录
SET NULL 修改包含与已删除键值有参照关系的所有记录,使用NULL值替换(只能用于已标记为NOT NULL的字段)
RESTRICT 拒绝删除要求,直到使用删除键值的辅助表被手工删除,并且没有参照时(这是默认设置,也是最安全的设置)
NO ACTION 啥也不做

4、其他

在外键上建立索引:

index repo_id (repo_id),

foreign key(repo_id) references repo_table(repo_id))

外键约束使用最多的两种情况无外乎:

1)父表更新时子表也更新,父表删除时如果子表有匹配的项,删除失败;

2)父表更新时子表也更新,父表删除时子表匹配的项也删除。

前一种情况,在外键定义中,我们使用ON UPDATE CASCADE ON DELETE RESTRICT;后一种情况,可以使用ON UPDATE CASCADE ON DELETE CASCADE。

InnoDB允许你使用ALTER TABLE在一个已经存在的表上增加一个新的外键:

ALTER TABLE tbl_name ADD [CONSTRAINT [symbol]] FOREIGN KEY [index_name] (index_col_name, ...) REFERENCES tbl_name (index_col_name,...) [ON DELETE reference_option] [ON UPDATE reference_option]

InnoDB也支持使用ALTER TABLE来删除外键:

ALTER TABLE tbl_name DROP FOREIGN KEY fk_symbol;

为什么说外键能保持数据的一致性、完整性

你想想,你的图中的第一章表切割成了表1和表2,表2的学号引用了表1的学号字段作为外键,假设不建立外键。仅仅是和表1一样单纯性 地设立一个学号字段。那么和建立外键有什么差别呢?

比方表1中张三的学号为20140900001,那么我在表2中插数据的时候在学号字段插20140900001来记录张三的成绩不也是做到了表 的解耦了吗?

这里存在的问题是。在不设置外键的情况下。表2的学号字段和表1的学号字段是没有关联的。仅仅是你自己觉得他们有关系而已。数据库并 不觉得它俩有关系。也就是说,你在表2的学号字段插了一个值(比方20140999999),可是这个值在表1中并没有,这个时候,数据库还是允 许你插入的,它并不会对插入的数据做关系检查。然而在设置外键的情况下。你插入表2学号字段的值必需要求在表1的学号字段能找到。 同一时候。假设你要删除表1的某个学号字段。必须保证表2中没有引用该字段值的列,否则就没法删除。

这就是所谓的保持数据的一致性和完整性。你想。如 果表2还引用表1的某个学号,你却把表1中的这个学号删了,表2就不知道这个学号相应的学生是哪个学生。

数据的一致性还包含数据类型的一致性(这 个见以下就知道了)。

外键的使用规范

  1. 从表的字段必须与外键类型同样(如上。分数表 stu 的类型必须和学生表 sid 的类型同样,比方都是 int(10) 类型)
  2. 外键必须是主表的唯一键(如上。学生表 sid 是主键,而主键是唯一的。所以能够作为分数表 stu 的外键)
  3. 有关联的字段(如上,分数表之所以使用学生表的 sid 是由于两者有关联,分数表记录的是学生的分数,而学生能够用 sid 来唯 一标识)
  4. 避免使用复合键(也就是说从表能够同一时候引用多个外表的字段作为一个外键,一般不推荐这样的做法)
# 作者(author):id,name,sex,age,mobile, detail_id
# 作者详情(author_detail): id,info,address

# 1、外键的 字段名 可以自定义(名字随意),通常命名规范(关联表_关联字段)

# 2、外键要通过 foreign key 语法建立表与表之间的关联

# 3、foreign key(所在表的外键字段) references 关联表(关联字段)
# eg:foreign key(detail_id) references author_detail(id)

# 4、级联关系
#   级联更新 on update cascade
#   级联删除 on delete cascade

# 重点:外键字段本身可以唯一或不唯一,但是外键关联的字段一定唯一

多表关系

一对一关系

一对一关系是最好理解的一种关系,在数据库建表的时候可以将人表的主键放置与身份证表里面,也可以将身份证表的主键放置于人表里面

img

一对多关系

班级是1端,学生是多端,结合面向对象的思想,1端是父亲,多端是儿子,所以多端具有1端的属性,也就是说多端里面应该放置1端的主键,那么学生表里面应该放置班级表里面的主键

img

多对多关系

对于多对多关系,需要转换成1对多关系,那么就需要一张中间表来转换,这张中间表里面需要存放学生表里面的主键和课程表里面的主键,此时学生与中间表示1对多关系,课程与中间表是1对多关系,学生与课程是多对多关系

img

'''
一对一:丈夫-妻子,用户-身份证,作者-作者详情
建表规则:
未存放外键的表被依赖,称之为左表;存放外键的表示依赖表,称之为右表;先操作左边再操作右表
# 一对一:外键在任何一方都可以,此时外键要设置 唯一键 


一对多:部门-员工,班级-学生,书-出版社
建表规则:
先建立主表,再建立从表,在从表中设置主表的唯一字段(通常为主键)作为外键,外键必须放在多的一方,此时外键值不唯一

多对多:老师-班级,课程-学生,出版社-作者
建表规则:
一定要创建第三张表(关系表),每一个外键值不唯一,看可以多个外键建立联合唯一,一定要创建第三张表(关系表),每一个外 键值不唯一,看可以多个外键建立联合唯一
'''

一对一:无级联关系

# 作者详情(author_detail): id,info,address
create table author_detail(
    id int primary key auto_increment,
    info varchar(256),
    address varchar(256)
);

# 作者表id,name,sex,age,mobile, detail_id
create table author(
    id int primary key auto_increment,
    name varchar(64) not null,
    mobile char(11) unique not null,
    sex enum('男', '女') default '男',
    age int default 0,
    detail_id int unique not null,
    foreign key(detail_id) references author_detail(id)
);

# 必须先创建被关联表数据,有关联表外键关联的记录后,关联表才可以创建数据
mysql>: insert into author_detail(info,address) values('Tom_info','Tom_address');
mysql>: insert into author(name,mobile,detail_id) values('Tom','13344556677', 1);
mysql>: insert into author_detail(info,address) values('Bob_info','Bob_address');
mysql>: insert into author(name,mobile,detail_id) values('Bob','15666882233', 2);

# 修改关联表 author
mysql>: insert into author_detail(info,address) values('Tom_info_sup','Tom_address_sup');
mysql>: update author set detail_id=3 where detail_id=2; # 有未被其他数据关联的数据,就可以修改
# 删除关联表 author
mysql>: delete from author where detail_id=3;  # 直接删除

# 修改被关联表 author_detail
mysql>: update author_detail set id=10 where id=1;  # 无法修改
# 删除被关联表 author_detail
mysql>: delete from author_detail where id=1;  # 无法删除

# 没有级联关系下:
# 增加:先增加被关联表记录,再增加关联表记录
# 删除:先删除关联表记录,再删除被关联表记录
# 更新:关联与被关联表都无法完成 关联的外键和主键 数据更新 - (如果被关联表记录没有被绑定,可以修改)

一对一:有级联关系

mysql>: drop table author;
mysql>: drop table author_detail;


# 作者详情(author_detail): id,info,address
create table author_detail(
    id int primary key auto_increment,
    info varchar(256),
    address varchar(256)
);

# 作者表id,name,sex,age,mobile, detail_id
create table author(
    id int primary key auto_increment,
    name varchar(64) not null,
    mobile char(11) unique not null,
    sex enum('男', '女') default '男',
    age int default 0,
    detail_id int unique not null,
    foreign key(detail_id) references author_detail(id)
    on update cascade 
    on delete cascade
);



# 必须先创建被关联表数据,有关联表外键关联的记录后,关联表才可以创建数据
mysql>: insert into author(name,mobile,detail_id) values('Tom','13344556677', 1);  # 错误
mysql>: insert into author_detail(info,address) values('Tom_info','Tom_address');
mysql>: insert into author(name,mobile,detail_id) values('Tom','13344556677', 1);
mysql>: insert into author_detail(info,address) values('Bob_info','Bob_address');
mysql>: insert into author(name,mobile,detail_id) values('Bob','15666882233', 2);

# 修改关联表 author
mysql>: update author set detail_id=3 where detail_id=2;  # 失败,3详情不存在
mysql>: update author set detail_id=1 where detail_id=2;  # 失败,1详情已被关联
mysql>: insert into author_detail(info,address) values('Tom_info_sup','Tom_address_sup');
mysql>: update author set detail_id=3 where detail_id=2; # 有未被其他数据关联的数据,就可以修改
# 删除关联表 author
mysql>: delete from author where detail_id=3;  # 直接删除

# 修改被关联表 author_detail
mysql>: update author_detail set id=10 where id=1;  # 级联修改,同步关系关联表外键

# 删除被关联表 author_detail
mysql>: delete from author where detail_id=10;  # 可以删除对被关联表无影响
mysql>: insert into author(name,mobile,detail_id) values('Tom','13344556677', 10);
mysql>: delete from author_detail where id=10;  # 可以删除,将关联表的记录级联删除掉

一对多

# 一对多:外键必须放在多的一方,此时外键值不唯一

# 出版社(publish): id,name,address,phone
create table publish(
    id int primary key auto_increment,
    name varchar(64),
    address varchar(256),
    phone char(20)
);

# 书(book):id,name,price,publish_id, author_id
create table book(
    id int primary key auto_increment,
    name varchar(64) not null,
    price decimal(5, 2) default 0,
    publish_id int,  # 一对多的外键不能设置唯一
    foreign key(publish_id) references publish(id)
    on update cascade
    on delete cascade
);

# 增:先增加被关联表(publish)的数据,再增加关联表(book)的数据
mysql>: insert into publish(name, address, phone) values
('人民出版社', '北京', '010-110'),
('西交大出版社', '西安', '010-119'),
('老男孩出版社', '上海', '010-120');

mysql>: insert into book(name, price, publish_id) values
('西游记', 6.66, 1),
('东游记', 8.66, 1),
('python从入门到入土', 2.66, 2),
('轮程序员修养之道', 3.66, 3),
('好好活着', 88.88, 3);
# 没有被关联的字段,插入依旧错误
mysql>: insert into book(name, price, publish_id) values ('打脸之道', 0.3, 4);  # 失败


# 更新:直接更新被关联表的(publish) 主键,关联表(book) 外键 会级联更新
mysql>: update publish set id=10 where id=1;
# 更新:直接更新关联表的(book) 外键,修改的值对应被关联表(publish) 主键 如果存在,可以更新成功,反之失败
mysql>: update book set publish_id=2 where id=4;  # 成功
mysql>: update book set publish_id=1 where id=4;  # 失败


# 删:
#   删被关联表,关联表会被级联删除
mysql>: delete from publish where id = 2;

#   删关联表,被关联表不会发生变化
mysql>: delete from book where publish_id = 3;
# 假设:书与作者也是 一对多 关系,一个作者可以出版多本书
create table book(
    id int primary key auto_increment,
    name varchar(64) not null,
    price decimal(5, 2) default 0,
    publish_id int,  # 一对多的外键不能设置唯一
    foreign key(publish_id) references publish(id)
    on update cascade
    on delete cascade
    
    # 建立与作者 一对多 的外键关联
    author_id int,  
    foreign key(author_id) references author(id)
    on update cascade
    on delete cascade
);

多对多

# 多对多:一定要创建第三张表(关系表),每一个外键值不唯一,看可以多个外键建立联合唯一

mysql>: drop table author;
mysql>: drop table author_detail;
mysql>: drop table book;
mysql>: drop table publish;

# 作者(author):id, name, age
create table author(
    id int primary key auto_increment,
    name varchar(64),
    age int unsigned default 0
);

# 出版社(publish):id, name, address
create table publish(
    id int primary key auto_increment,
    name varchar(64),
    address varchar(256)
);

# 作者与出版社关系表:id, author_id, publish_id
create table author_publish(
    id int primary key auto_increment,
    # 关系表一定有多个外键,关联着多张表
    # 关联作者表
    author_id int,
    foreign key(author_id) references author(id)
    on update cascade
    on delete cascade,
    # 关联出版社表
    publish_id int,
    foreign key(publish_id) references publish(id)
    on update cascade
    on delete cascade,
    # 建立两个字段的联合唯一
    unique(author_id, publish_id)
);

# 注:关系表 关联着 作者 和 出版社 两张表,在表结构上 作者 与 出版社 两表键没有任何关系


# 增:两张被关联表,没有前后关系,但关系表必须在两个表都提供数据后才能进行 关系匹配
mysql>: insert into author(name, age) values('ruakei', 67),('engo', 76),('Lxx', 3);
mysql>: insert into publish(name, address) values('老男孩出版社', '上海'),('小女孩出版社', '北京');

# 操作关系表:
mysql>: insert into author_publish(author_id, publish_id) values(1,1),(1,2),(2,1),(2,2),(3,1);

# 关系表操作:增、删、改,只要两张被关系表有提供对应的操作数据,都可以操作成功,且对两张被关系表没有影响


# 操作两张被关系表:
#       增:不会影响关系表
mysql>: insert into publish(name, address) values('西交大出版社', '西安');
#       改:关系表都会级联更新
mysql>: update publish set id=10 where id=1;
#       删:关系表都会级联删除
mysql>: delete from author where name='ruakei';

单表查询

"""
增:
insert [into] 
    [数据库名.]表名[(字段1[, ..., 字段n])] 
values 
    (数据1[, ..., 数据n])[, ..., (数据1[, ..., 数据n])];

删:
delete from [数据库名.]表名 [条件];

改:
updata [数据库名.]表名 set 字段1=值1[, ..., 字段n=值n] [条件];

查:
select [distinct] 字段1 [[as] 别名1],...,字段n [[as] 别名n] from [数据库名.]表名 [条件];
"""

# 条件:from、where、group by、having、distinct、order by、limit => 层层筛选后的结果
# 注:一条查询语句,可以拥有多种筛选条件,条件的顺序必须按照上方顺序进行逐步筛选,distinct稍有特殊(书写位置),条件的种类可以不全
# 可以缺失,但不能乱序

去重:distinct

mysql>: 
create table t1(
    id int,
    x int,
    y int
);

mysql>: insert into t1 values(1, 1, 1), (2, 1, 2), (3, 2, 2), (4, 2, 2);

mysql>: select distinct * from t1;  # 全部数据

mysql>: select distinct x, y from t1;  # 结果 1,1  1,2  2,2

mysql>: select distinct y from t1;  # 结果 1  2

# 总结:distinct对参与查询的所有字段,整体去重(所查的全部字段的值都相同,才认为是重复数据)

数据准备

CREATE TABLE `emp`  ( 
  `id` int(0) NOT NULL AUTO_INCREMENT,
  `name` varchar(10) NOT NULL,
  `gender` enum('男','女','未知') NULL DEFAULT '未知',
  `age` int(0) NULL DEFAULT 0,
  `salary` float NULL DEFAULT 0,
  `area` varchar(20) NULL DEFAULT '中国',
  `port` varchar(20) DEFAULT '未知',
  `dep` varchar(20),
  PRIMARY KEY (`id`)
);

INSERT INTO `emp` VALUES 
    (1, 'yangsir', '男', 42, 10.5, '上海', '浦东', '教职部'),
    (2, 'engo', '男', 38, 9.4, '山东', '济南', '教学部'),
    (3, 'jerry', '女', 30, 3.0, '江苏', '张家港', '教学部'),
    (4, 'tank', '女', 28, 2.4, '广州', '广东', '教学部'),
    (5, 'jiboy', '男', 28, 2.4, '江苏', '苏州', '教学部'),
    (6, 'zero', '男', 18, 8.8, '中国', '黄浦', '咨询部'),
    (7, 'owen', '男', 18, 8.8, '安徽', '宣城', '教学部'),
    (8, 'jason', '男', 28, 9.8, '安徽', '巢湖', '教学部'),
    (9, 'ying', '女', 36, 1.2, '安徽', '芜湖', '咨询部'),
    (10, 'kevin', '男', 36, 5.8, '山东', '济南', '教学部'),
    (11, 'monkey', '女', 28, 1.2, '山东', '青岛', '教职部'),
    (12, 'san', '男', 30, 9.0, '上海', '浦东', '咨询部'),
    (13, 'san1', '男', 30, 6.0, '上海', '浦东', '咨询部'),
    (14, 'san2', '男', 30, 6.0, '上海', '浦西', '教学部'),
    (15, 'ruakei', '女', 67, 2.501, '上海', '陆家嘴', '教学部');

常用函数

"""
拼接:concat() | concat_ws()
大小写:upper() | lower()
浮点型操作:ceil() | floor() | round()
整型:可以直接运算
"""
mysql>: select name,area,port from emp;
mysql>: select name as 姓名, concat(area,'-',port) 地址 from emp;  # 上海-浦东
mysql>: select name as 姓名, concat_ws('-',area,port,dep) 信息 from emp;  # 上海-浦东-教职部

mysql>: select upper(name) 姓名大写,lower(name) 姓名小写 from emp;

mysql>: select id,salary,ceil(salary)上薪资,floor(salary)下薪资,round(salary)入薪资 from emp;

mysql>: select name 姓名, age 旧年龄, age+1 新年龄 from emp;

条件:where

# 多条件协调操作导入:where 奇数 [group by 部门 having 平均薪资] order by [平均]薪资 limit 1

mysql>: select * from emp where id<5 limit 1;  # 正常
mysql>: select * from emp limit 1 where id<5;  # 异常,条件乱序

# 判断规则
"""
比较符合:>  |  <  |  >=  |  <=  |  =  |  !=
区间符合:between 开始 and 结束 |  in(自定义容器)
逻辑符合:and  |  or  |  not
相似符合:like _|%
正则符合:regexp 正则语法
"""
mysql>: select * from emp where salary>5;
mysql>: select * from emp where id%2=0;

mysql>: select * from emp where salary between 6 and 9;

mysql>: select * from emp where id in(1, 3, 7, 20);

# _o 某o | __o 某某o | _o% 某o* (*是0~n个任意字符) | %o% *o*
mysql>: select * from emp where name like '%o%';
mysql>: select * from emp where name like '_o%';  
mysql>: select * from emp where name like '___o%';

# sql只支持部分正则语法
mysql>: select * from emp where name regexp '.*\d';  # 不支持\d代表数字,认为\d就是普通字符串
mysql>: select * from emp where name regexp '.*[0-9]';  # 支持[]语法

分组与筛选:group by | having

where与having
# 表象:在没有分组的情况下,where与having结果相同
# 重点:having可以对 聚合结果 进行筛选
mysql>: select * from emp where salary > 5;
mysql>: select * from emp having salary > 5;

mysql>: select * from emp where id in (5, 10, 15, 20);
mysql>: select * from emp having id in (5, 10, 15, 20);
聚合函数
"""
max():最大值
min():最小值
avg():平均值
sum():和
count():记数
group_concat():组内字段拼接,用来查看组内其他字段
"""
分组查询 group by
# 修改my.ini配置重启mysql服务
sql_mode=ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION

# 在sql_mode没有 ONLY_FULL_GROUP_BY 限制下,可以执行,但结果没有意义
# 有 ONLY_FULL_GROUP_BY 限制,报错
mysql>: select * from emp group by dep;

# 分组后,表中数据考虑范围就不是 单条记录,因为每个分组都包含了多条记录,参照分组字段,对每个分组中的 多条记录 统一处理
# eg: 按部门分组,每个部门都有哪些人、最高的薪资、最低的薪资、平均薪资、组里一共有多少人

# 将多条数据统一处理,这种方式就叫 聚合
# 每个部门都有哪些人、最高的薪资、最低的薪资、平均薪资 都称之为 聚合结果 - 聚合函数操作的结果
# 注:参与分组的字段,也归于 聚合结果

mysql>: 
select 
    dep 部门,
    group_concat(name) 成员,
    max(salary) 最高薪资,
    min(salary) 最低薪资,
    avg(salary) 平均薪资,
    sum(salary) 总薪资,
    count(gender) 人数
from emp group by dep;

# 总结:分组后,查询条件只能为 分组字段 和 聚合函数操作的聚合结果
分组后的having
mysql>: 
select 
    dep 部门,
    group_concat(name) 成员,
    max(salary) 最高薪资,
    min(salary) 最低薪资,
    avg(salary) 平均薪资,
    sum(salary) 总薪资,
    count(gender) 人数
from emp group by dep;

# 最低薪资小于2
mysql>:
select 
    dep 部门,
    group_concat(name) 成员,
    max(salary) 最高薪资,
    min(salary) 最低薪资,
    avg(salary) 平均薪资,
    sum(salary) 总薪资,
    count(gender) 人数
from emp group by dep having min(salary)<2;

# having可以对 聚合结果 再进行筛选,where不可以

排序

排序规则
# order by 主排序字段 [asc|desc], 次排序字段1 [asc|desc], ...次排序字段n [asc|desc]
未分组状态下
mysql>: select * from emp;

# 按年龄升序
mysql>: select * from emp order by age asc;
# 按薪资降序
mysql>: select * from emp order by salary desc;

# 按薪资降序,如果相同,再按年龄降序
mysql>: select * from emp order by salary desc, age desc;
# 按龄降序,如果相同,再按薪资降序
mysql>: select * from emp order by age desc, salary desc;
分组状态下
mysql>:
select 
    dep 部门,
    group_concat(name) 成员,
    max(salary) 最高薪资,
    min(salary) 最低薪资,
    avg(salary) 平均薪资,
    sum(salary) 总薪资,
    count(gender) 人数
from emp group by dep;

# 最高薪资降序
mysql:
select 
    dep 部门,
    group_concat(name) 成员,
    max(salary) 最高薪资,
    min(salary) 最低薪资,
    avg(salary) 平均薪资,
    sum(salary) 总薪资,
    count(gender) 人数
from emp group by dep
order by 最高薪资 desc;

限制 limit

# 语法:limit 条数  |  limit 偏移量,条数
mysql>: select name, salary from emp where salary<8 order by salary desc limit 1;

mysql>: select * from emp limit 5,3;  # 先偏移5条满足条件的记录,再查询3条

连表查询

连接

# 连接:将有联系的多张表通过关联(有联系就行,不一定是外键)字段,进行连接,形参一张大表
# 连表查询:在大表的基础上进行查询,就称之为连表查询

# 将表与表建立连接的方式有四种:内连接、左连接、右连接、全连接

一对多数据准备

mysql>: create database db3;
mysql>: use db3;

mysql>: 
create table dep(
    id int primary key auto_increment,
    name varchar(16),
    work varchar(16)
);
create table emp(
    id int primary key auto_increment,
    name varchar(16),
    salary float,
    dep_id int
);
insert into dep values(1, '市场部', '销售'), (2, '教学部', '授课'), (3, '管理部', '开车');
insert into emp(name, salary, dep_id) values('egon', 3.0, 2),('yanghuhu', 2.0, 2),('sanjiang', 10.0, 1),('owen', 88888.0, 2),('liujie', 8.0, 1),('yingjie', 1.2, 0);

笛卡尔积

# 笛卡尔积: 集合 X{a, b} * Y{o, p, q} => Z{{a, o}, {a, p}, {a, q}, {b, o}, {b, p}, {b, q}}

mysql>: select * from emp, dep;

# 总结:是两张表 记录的所有排列组合,数据没有利用价值

内连接

# 关键字:inner join on
# 语法:from A表 inner join B表 on A表.关联字段=B表.关联字段

mysql>: 
select 
    emp.id,emp.name,salary,dep.name,work 
from emp inner join dep on emp.dep_id = dep.id 
order by emp.id;

# 总结:只保留两个表有关联的数据

左连接

# 关键字:left join on
# 语法:from 左表 left join 右表 on 左表.关联字段=右表.关联字段

mysql>: 
select 
    emp.id,emp.name,salary,dep.name,work 
from emp left join dep on emp.dep_id = dep.id 
order by emp.id;

# 总结:保留左表的全部数据,右表有对应数据直接连表显示,没有对应关系空填充

右连接

# 关键字:right join on
# 语法:from A表 right join B表 on A表.关联字段=B表关联字段

mysql>: 
select 
    emp.id,emp.name,salary,dep.name,work 
from emp right join dep on emp.dep_id = dep.id 
order by emp.id;

# 总结:保留右表的全部数据,左表有对应数据直接连表显示,没有对应关系空填充

左右可以相互转化

mysql>: 
select 
    emp.id,emp.name,salary,dep.name,work 
from emp right join dep on emp.dep_id = dep.id 
order by emp.id;

mysql>: 
select 
    emp.id,emp.name,salary,dep.name,work 
from dep left join emp on emp.dep_id = dep.id 
order by emp.id;

# 总结:更换一下左右表的位置,相对应更换左右连接关键字,结果相同

全连接

mysql>: 
select 
    emp.id,emp.name,salary,dep.name,work 
from emp left join dep on emp.dep_id = dep.id 

union

select 
    emp.id,emp.name,salary,dep.name,work 
from emp right join dep on emp.dep_id = dep.id 

order by id;

# 总结:左表右表数据都被保留,彼此有对应关系正常显示,彼此没有对应关系均空填充对方

一对一与一对多情况一致

# 创建一对一 作者与作者详情 表
create table author(
    id int,
    name varchar(64),
    detail_id int
);
create table author_detail(
    id int,
    phone varchar(11)
);
# 填充数据
insert into author values(1, 'Bob', 1), (2, 'Tom', 2), (3, 'ruakei', 0);
insert into author_detail values(1, '13344556677'), (2, '14466779988'), (3, '12344332255');

# 内连
select author.id,name,phone from author join author_detail on author.detail_id = author_detail.id order by author.id;

# 全连
select author.id,name,phone from author left join author_detail on author.detail_id = author_detail.id
union
select author.id,name,phone from author right join author_detail on author.detail_id = author_detail.id
order by id;

多对多:两表两表建立连接

```mysql

在一对一基础上,建立 作者与书 的多对多关系关系

利用之前的作者表

create table author(
id int,
name varchar(64),
detail_id int
);
insert into author values(1, 'Bob', 1), (2, 'Tom', 2), (3, 'ruakei', 0);

创建新的书表

create table book(
id int,
name varchar(64),
price decimal(5,2)
);
insert into book values(1, 'python', 3.66), (2, 'Linux', 2.66), (3, 'Go', 4.66);

创建 作者与书 的关系表

create table author_book(
id int,
author_id int,
book_id int
);

数据:author-book:1-1,2 2-2,3 3-1,3

insert into author_book values(1,1,1),(2,1,2),(3,2,2),(4,2,3),(5,3,1),(6,3,3);

将有关联的表一一建立连接,查询所以自己所需字段

select book.name, book.price, author.name, author_detail.phone from book
join author_book on book.id = author_book.book_id
join author on author_book.author_id = author.id
left join author_detail on author.detail_id = author_detail.id;

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!