MySQL基础 [三] - 数据类型

目录

数据类型分类

​编辑

数值类型

tinyint

bit

浮点类型

float

decimal

字符串类型

char

varchar

varchar和char的比较和选择

日期和时间类型

enum和set

enum类型

set类型

enum和set的类型查找


数据类型分类

数值类型

tinyint

复制代码
TINYINT[(M)] [UNSIGNED]是 MySQL 中最小的整数数据类型。

主要特性:

占用空间:1个字节(8位)

有符号范围:-128 到 127 (-2^7 到 2^7-1)

无符号范围:0 到 255 (0 到 2^8-1)

M 参数:表示显示宽度,不影响存储范围,仅用于格式化显示

UNSIGNED:声明为无符号数,不能存储负数,但正数范围扩大

复制代码
create table t4(num tinyint);

这个num是表的变量名

数值越界情况:

无符号的tinyint,相当于把**[-128,0)**给砍掉了,这个时候在插入负数就会提示超出范围了

和语言的区别

语言定义变量的时候是类型在前,变量名在后,而mysql在表中建立属性列是列名称在前,类型在后,和语言是反过来的!

如果是语言出现这种数据问题的不合法问题可能会发生截断或者是隐式类型转换,比如说char a = 1234567,但是mysql他为了保证数据的完整性是不会做这种截断的操作的,而是会直接把你给拦截不让你做对应的操作!!

也就是说凡是在mysql中已经插入的数据,它都是合法的。所以,在mysql中,一般而言数据类型本身也是一种:约束。约束的是使用者,如果你不是一个很好的使用者,mysql也能保证数据插入的合法性。同时也能保证数据库中的数据是可预期,完整的

注意

尽量不使用unsigned,对于int类型可能存放不下的数据,int unsigned同样可能存放不下,与其如此, 还不如设计时,将int类型提升为bigint类型。

当然也要看具体的场景,比如说年龄不可能有负数,所以用unsigned int本身也是很合理的。

为什么mysql要有这么多大小不一的数据类型呢??

为了满足不同的应用场景中和节省资源中做一个平衡,所以也不能无脑选最大的!

bit

复制代码
bit[(M)] : 位字段类型。M表示每个值的位数,范围从1到64。如果M被忽略,默认为1。  

下面我们使用一下

复制代码
create table if not exists t3(id int, online bit(1));

BIT类型的数据在MySQL中以二进制形式存储,默认是按照ascii码值的形式显示出来。 在MySQL老版本下,二进制默认是不显示的,因为我这个用的是8.0版本,进行了优化

复制代码
select id, hex(online) from t3;

当我们定义了bit(1),说明只有一个bit位,如果插入的不是0,1,数据库就会约束

当我们修改bit位数后,然后插入97,我们就会发现显示的是'a' (默认显示的是ascii值,可以用特定的内建函数去按照我们想要的进制去显示)

但是如果插入的是130,ascii无法显示,就表示不出来了。

需要使用特定的方法才能将其转换为可读的格式。在select语句中无法直接查看其值。要查看bit字段的值,我们可以使用MyL内置函数 BIN() 二进制 或 OCT() 八进制 或HEX() 十六进制

转换可读格式的结果如下:

当我们不给bit参数的时候,默认创建的是1个比特位

如果我们有这样的值,比如性别,这时可以定义bit(1),只存放0或1。这样可以节省空间。

浮点类型

float

复制代码
float[(m, d)] [unsigned] : M指定显示长度,d指定小数位数,占用空间4个字节 (精度是6-7) 

要注意M必须>=D

使用

复制代码
create table if not exists t6(id int, salary float(4,2));

关于m和d的探究:

-99.999插入不进去倒还好理解,可我们会发现-10.0插入后变成了-10.00,小数位用0补齐了,但是-100.0也是4位数却不可以,这是因为他小数部分需要2位,所以如果补0了就变成5位了就不符合要求了!!所以我们会发现**(4,2)的范围是-99.99----99.99**

我们会发现刚才99.999不行,但是99.991却可以,这是因为MySQL在保存值时对多余的位不会无脑拦截,而是会先进行四舍五入,只有四舍五入后会导致位数变多的情况才会拦截

总结上面的情况 ,我们会发现其实mysql并不是无脑拦截,而是对于小数部分缺的会先尝试补0,多的会先尝试四舍五入,如果之后还不符合要求,才会拦截。

比如100.0 99.95 所以更详细的范围应该是-99.994444444444......---- 99.994444444444......。(即使我们用的是unsigned 他的上限也是不会变的,而负数部分会直接变成非法数据)

注意:浮点数存的时候也是转化成科学计数法然后进行存储的,有符号位、指数位、精度位,任何一个浮点数在转化的过程中不一定都能很精确地转化(因为精度位可能会存不下)。其次将浮点数转化成二进制的时候对于小数部分进行乘2取整也不一定所有的部分都能转化成0。 所以是很有可能会有部分精度丢失

double是比float精度还大的浮点数(15-16),占8个字节。

使用float的默认值

当数据比较大,或者小数点位数比较多,此时float存储的数据就不太准了。这个是跟浮点数存储的原理是有关的。我们的数据是十进制的,但是要按照二进制去存储,当转成二进制的时候,整数部分是要%2取余的,小数点部分是*2取整的,所以会丢失精度

decimal

decimal可以规避float的缺点

格式

复制代码
decimal(m, d) [unsigned] : 定点数m指定长度,d表示小数点的位数
  • decimal(5,2) 表示的范围是 -999.99 ~ 999.99
  • decimal(5,2) unsigned 表示的范围 0 ~ 999.99 decimal和float很像

但是有区别: float和decimal表示的精度不一样

使用

复制代码
create table if not exists t9(f1 float(10,8),f2 decimal(4,2));
  • float表示的精度大约是7位。
  • decimal整数最大位数m为65。支持小数最大位数d是30。如果d被省略,默认为0.如果m被省略,默认是10。

建议:如果希望小数的精度高,推荐使用decimal。

未来如果对精度要求不高,就用float,精度特别高,就用decimal

字符串类型

char

复制代码
char(L): 固定长度字符串,L是可以存储的长度,单位为字符,最大长度值可以为255

**注意:**char的单位是 字符

cpp 复制代码
create table if not exists t10(
    -> id int,
    -> name char(2)
    -> );

char(2) 表示可以存放两个字符,可以是字母或汉字,但是不能超过2个, 最多只能是255

char的单位是 字符mysql的字符和我们以往语言的字符 (1个字符占1个字节,utf8中按道理一个汉字要占3个字节)概念不一样而是真的就是一个字符!无论是123 abc还是汉字 其实每个都算一个字符!

超过最大长度会报错(最大长度为:255)

varchar

cpp 复制代码
 varchar(L): 可变长度字符串,L表示字符长度,最大长度65535个字节

注意:最大长度65535个字节

cpp 复制代码
create table if not exists t11(
    -> id int,
    -> name varchar(6)
    -> );

表面上看起来好像和char没啥区别,但是实际上是有区别的!!如下你会发现max竟然变成了21845!!这是为啥呢,因为varchar最大长度65536的单位是字节!

这个varchar(len),len到底是多大呢?

这个len值,和表的编码密切相关:

varchar长度可以指定为0到65535之间的值,但是有1 - 3 个字节用于记录数据大小,所以说有效字节数是65532。

  • 当我们的表的编码是utf8时,varchar(n)的参数n最大值是65532/3=21844(因为utf中,一个字符占用3个字节)
  • 如果编码是gbk,varchar(n)的参数n最大是65532/2=32766(因为gbk中,一个字符占用2字节)。

这个21845也是错误的,它没有考虑 记录数据大小****的那1-3个字节,实际utf8最大应该是21844.

关于变长和定长的理解

char类型就有点像静态数组, 你定义多少我就给你多少空间,这就是定长的含义!!**而varchar就有点像string类型,**string会有一个char*指向该字符串,一个size表示该字符串有多少字符,一个capacity表示该字符串的上限,你需要多少空间就开辟多少空间,但是你一定不可以超过capacity,所以varchar括号后面表示的是他的字符上限,并不代表他真的开了这么多空间,他也会像string一样会有一部分空间来维护相关的信息,所以这就是变长的含义!!!

utf8最大字节数是21844,那为什么t12可以创建,但是t11不让创建呢??

**因为表的存储是一行一行存的,而一行存储最大的字节数也是65536,**t12只有varchar类型所以可以一行都用来存varchar,因此可以达到最大的21844,但是t11还有一个int类型,所以要分出一部分空间给int,因此不能达到21844. 但如果我们减到21842,也是可以创建的

varchar和char的比较和选择

如果我们存储的是短字符串且大小比较均匀, 那么使用char其实会更好一点(因为还需要额外一部分字节来保存有效长度),但是其他情况(字符串较长且大小不均匀)的话 使用varchar会好些。

varchar具体需要多少个字节来存有效长度(1-3)是要根据实际情况动态调整的,像上图的短字符串其实只要一个字节就够了

如何选择定长或变长字符串?

  • **如果数据确定长度都一样,就使用定长(char),**比如:身份证,手机号,md5
  • **如果数据长度有变化,就使用变长(varchar),**比如:名字,地址,但是你要保证最长的能存的进去。
  • 定长的磁盘空间比较浪费,但是效率高。
  • 变长的磁盘空间比较节省,但是效率低。(需要维护一些字段,而且动态开辟空间耗时)

**定长的意义是,**直接开辟好对应的空间

**变长的意义是,**在不超过自定义范围的情况下,用多少,开辟多少。

日期和时间类型

常用的日期有如下三个:

  • date :日期 'yyyy-mm-dd' ,占用三字节**(存储一个不需要时间的日期,比如生日、各种节日)**
  • datetime:时间日期格式 'yyyy-mm-dd HH:ii:ss' 表示范围从 1000 到 9999 ,占用八字节**(存储一个静态的、不会随修改而更新的时间,比如入职时间、身份证过期时间)**
  • timestamp :时间戳,从1970年开始的 yyyy-mm-dd HH:ii:ss 格式和 datetime 完全一致,占用四字节**(存储一个需要随着修改而更新的时间,比如评论、修改文章)**
cpp 复制代码
CREATE TABLE t13(
    t1 date,
    t2 datetime,
    t3 timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

在老版本中,**timestamp默认的是当前的时间戳,**而当你进行增加或者更改的时候,他会立马更新成当前的时间戳!(动态)而前两种需要程序员自己设定格式且是静态的!!

因为timestamp会自动更新时间戳t3,不需要我们插入,因此我们只需要插入t1和t2, **这个时候我们的括号就不能省略了,**因为省略的话默认是全都要插入

但是从MySQL 5.6.5开始**,**默认的TIMESTAMP自动初始化和自动更新行为发生了变化。在较新版本中,这种行为必须明确指定。

cpp 复制代码
insert into t13 (t1,t2) values ('2000-10-01','1949-10-01 08:00:00');

当我们改掉t1的时候,t3也跟着变了

timestamp的典型应用场景:评论

enum和set

enum类型

ENUM是枚举类型,允许从预定义的值列表中选择一个值存储。

特点

  • 只能从预定义列表中选择一个值
  • 存储效率高,内部使用整数表示
  • 最多可以有65,535个不同的元素
  • 区分大小写

该设定只是提供了若干个选项的值,最终一个单元格中,实际只存储了其中一个值;而且出于效率考虑,这些值实际 存储的是"数字",因为这些选项的每个选项值依次对应如下数字:1,2,3,....最多65535个;当我们添加枚举值时,也可 以添加对应的数字编号。

set类型

SET类型允许从预定义列表中选择零个、一个或多个值存储。

特点

  • 可以选择多个值(以逗号分隔)
  • 最多可以有64个不同的成员
  • 内部使用位字段存储,非常高效
  • 区分大小写

该设定只是提供了若干个选项的值,最终一个单元格中,设计可存储了其中任意多个值;而且出于效率考虑,这些值 实际存储的是"数字",因为这些选项的每个选项值依次对应如下数字:1,2,4,8,16,32,.... 最多64个。

案例: 有一个调查表votes,需要调查人的喜好

  • (代码、羽毛球、乒乓球、足球、游泳)中去选择(可以多选)
  • (男,女)[单选]
cpp 复制代码
create table if not exists votes (
    username varchar(30),
    gender enum('男', '女'),
    hobby set('代码', '羽毛球', '乒乓球', '足球', '游泳')
);

**只允许插入枚举常量的字符,也就是男,女。也可以写对应常量的下标,**不过要注意,下标是从1开始,而不是0开始。不能插入其他的任何字符

爱好是多选的,一个人可以用多个爱好

NULL vs ' ' 的区别:NULL是什么都没有,而' '是有只不过是空串 (前者就是你没有银行卡,后者就是你有银行卡但是里面没钱)

我们发现也可以用数字,去表示爱好

但是,当我们发现插入3的时候

3难道不是乒乓球吗?为啥给的结果是代码,羽毛球呢?

这说明其实多选每个选项代表的是一个bit位 比方说我们有五个多选,就是00000 每一位是0还是1代表这个爱好是否选择,比如3就是00011,说明有两个被选了,也就是代码,羽毛球。如果是7的话,就是00111,也就是3个被选了

所以单选是下标,多选是位图!

enum和set的类型查找

enum的查找

我想查找兴趣爱好有羽毛球的,该怎么查找呢?

下面方法是固定查找的方法,对于多选就不适用了,****比如我想找到会所有会打羽毛球的人,****但是这样只会给我筛选出只会打羽毛球的人。

或者是给我查找出指定爱好

我想要的不是只有羽毛球的,是有羽毛球的,那该怎么呢?

对于集合查找,我们可以使用 find_ in_ set函数

cpp 复制代码
 find_in_set(sub,str_list) 

如果 sub 在 str_list 中,则返回下标;如果不在,返回0; str_list 用逗号分隔的字符串。

select find_in_set('a', 'a,b,c')

查找会打羽毛球的人 select * from votes where find_in_set('羽毛球', hobby);

也就是说只要hobby里面有羽毛球,那么也就是有返回值,也就意味着结果是true,所以就可以把这些选出来了。

如果我们想查找会代码和羽毛球的,直接写到find_in_set里面是不可以的,因为他其实只能判断其中一个是否在集合里,不能判断两个

但是**因为where是一个条件判断,所以如果我们想要两个都满足的话,**我们只需要用一个逻辑与就可以解决这个问题了

数据类型其实就是一种约束,将来表中插入的数据是有预期的。

相关推荐
鸠摩智首席音效师几秒前
MySQL ERROR 1114 (HY000): The table is full
数据库·mysql
数据大魔方5 分钟前
【期货量化实战】豆粕期货量化交易策略(Python完整代码)
开发语言·数据库·python·算法·github·程序员创富
Codeking__25 分钟前
Redis的value类型介绍——zset
数据库·redis·缓存
muddjsv27 分钟前
SQLite3 核心命令全解析 (从入门到精通)
数据库
難釋懷31 分钟前
认识NoSQL
数据库·nosql
亿坊电商34 分钟前
利于SEO优化的CMS系统都有哪些特点?
前端·数据库
阿阿阿安34 分钟前
MySQL(一)数据库风险操作场景总结
数据库·mysql
计算机程序设计小李同学1 小时前
平价药店销售与管理系统
java·mysql·spring·spring cloud·ssm
心丑姑娘1 小时前
使用ClickHouse时的劣质SQL样例
数据库·sql·clickhouse
什么都不会的Tristan1 小时前
redis篇
数据库·redis·缓存