この記事のディレクトリ:
1.1 データ型の概要
1.2 保存の仕組みと操作方法
1.2.1 整数の保存方法
1.2.2文字型の格納方法
1.2.3 日時の格納方法
1.2.4 ENUMデータ型
1.2.5 SETデータ型
1.3. : unsigned
1.3. データ型属性: zerofill
データ型は、各フィールドに格納できるデータの種類と格納できるデータ量を制限するフィールド制約です。保存できる形式など。 MySQL/MariaDB には、整数型、浮動小数点型、文字列型、日時型、特殊な ENUM 型と SET 型の 5 種類のデータ型があります。
これら 5 つのデータ型の意味、制限、および関連する指示は、以下の図に示すとおりです。
各データ型が占有するバイト数については、mariadb 公式マニュアルを参照してください。
データ型によってフィールドのデータ格納長が制限されるのは、テーブル作成時にメモリ内でアドレス空間が厳密に区切られており、それに応じて何バイト格納できるかが決まるためです。アドレス空間の長さまで。もちろん、これは非常に大まかな概念です。より具体的な保存方法については、以下の説明を参照してください。
データ型の範囲を制限するには 2 つの方法があります。1 つはスペースを厳密に制限し、スペースを分割した分だけ保存できるデータを制限し、余分なデータは切り捨てます。もう 1 つは、追加のバイト ビットを使用して、特定のアドレス空間のバイトにデータが格納されているかどうかをマークする方法です。格納されている場合はマークされ、格納されていない場合はマークされません。
ここでは整数の格納方法を中心に説明します 浮動小数点データ型の格納方法については、考慮すべき点が多すぎます。
整数データ型の場合、スペースは厳密に制限されますが、文字とは異なります。分割された各バイトのビットの 0 と 1 で値を直接計算できるため、その範囲はビット数によって計算されます。 。 1 バイトは 8 ビットで、2^8=256 個の値を構成できます。同様に、4 バイトの int は合計 2^16=65536 個の値を占め、範囲は 0 になります。 -2^32。つまり、0 ~ 255 の数値は 1 バイトのみを占有し、256 ~ 65535 の数値は 2 バイトを必要とします。
MySQL/mariadb の整数データ型はパラメーター M を使用できることに注意してください。M は、INT(M)、tinyint(M) などの正の整数です。この M は表示長を表します。たとえば、int(4) は、出力時に実際の値の桁数が表示値の幅より小さい場合、スペースを使用して表示されることを意味します。デフォルトでは左側。結果桁数が制限値を超えても、表示結果には影響しません。通常、この関数はスペースを 0 で埋めるために zerofill 属性とともに使用されますが、zerofill を使用すると、列は自動的に符号なしフィールドになります。例:
CREATE TABLE test3(id INT(2) ZEROFILL NOT NULL);INSERT INTO test3 VALUES(1),(2),(11),(111);SELECT id FROM test3; +-----+ | id | +-----+ | 01 | | 02 | | 11 | | 111 | +-----+ 4 rows in set (0.00 sec)
唯一注意すべき点は、表示幅は表示効果にのみ影響し、保存、比較、長さの計算などの操作には影響しないことです。
ここでは主に保存方法とcharとvarcharの違いについて説明します。
Char型は「固定長文字列型」と呼ばれることが多く、スペースの長さは厳密に制限されていますが、バイト数ではなく文字数が制限されています。ただし、古いバージョンではバイト数が制限されていました。限定 。したがって、char(M) は厳密に M 文字を格納し、足りない部分はスペースで埋められ、M 文字を超えた部分は直接切り捨てられます。
char型には「短い場合はスペースで埋める」機能があるため、データの信頼性を反映するため、アドレス空間からデータを取得する際には末尾のスペースが自動的に削除されます。これは char の特殊な機能であり、手動で保存した末尾のスペースも自動的に完成したものとみなされ、取得中に削除されます。 つまり、where ステートメントの name='gaoxiaofang '
和name='gaoxiaofang'
の結果は同じです。
例:
create table test2(a char(4) charset utf8mb4);insert into test2 values('恭喜你'),('恭喜你成功晋级'),('hello'),('he ');select concat(a,'x') from test2;+---------------+| concat(a,'x') | +---------------+| 恭喜你x | | 恭喜你成x | | hellx | | hex | +---------------+4 rows in set
上記の結果からわかるように、char(4) は 4 文字のみを格納でき、末尾のスペースは削除されます。
varchar常被称为"变长字符串类型",它存储数据时使用额外的字节的bit位来标记某个字节是否存储了数据。每存储一个字节(不是字符)占用一个bit位进行记录,因此一个额外的字节可以标记共256个字节,2个额外的字节可以标记65536个字节。但MySQL/mariadb限制了最大能存储65536个字节。这表示,如果是单字节的字符,它最多能存储65536个字符,如果是多字节字符,如UTF8的每个字符占用3个字节,它最多能存储65536/3=21845个utf8字符。
因此,varchar(M)存储时除了真实数据占用空间长度,还要额外计算1或2个字节的Bit位长度,即对于单字节字符实际占用的空间为M+1
或M+2
个字节,对于多字节字符(如3字节)实际占用的空间为M*3+1
或M*3+2
个字节。
由于varchar存储时需要采用额外的bit位记录每一个字节,短了的数据不会自动使用补齐,因此显式存储的尾随空格也会被存储并在Bit位上进行标记,也就是说不会删除尾随空格。
和char(M)一样,当指定varchar(2)时,只能存储两个字节的字符,如果超出了,则切断。
关于char、varchar以及text字符串类型,它们在比较时不会考虑尾随空格,但做like匹配或正则匹配时会考虑空格,因为匹配时字符是精确的。例如:
create table test4(a char(4),b varchar(5));insert into test4 values('ab ','ab ');select a='ab ',b='ab ',a=b from test4;+-----------+--------------+-----+| a='ab ' | b='ab ' | a=b | +-----------+--------------+-----+| 1 | 1 | 1 | +-----------+--------------+-----+1 row in setselect a like 'ab ' from test4;+-------------------+| a like 'ab ' | +-------------------+| 0 | +-------------------+1 row in set
最后需要说明的是,数值在存储(或调入内存)时,以数值型方式存储比字符型或日期时间类型更节省空间。因为整数值存储时是直接通过bit计算数值的,0-255之间的任意整数都只占一个字节,256-65535之间的任意整数都占2个字节,而占用4个字节时便可以代表几十亿个整数之间的任意一个,这显然比字符型存储时每个字符占用一个字节节省空间的多。例如值"100"存储为字符型时占用三个字节,而存储为数值型将只占用一个字节。因此数据库默认将不使用引号包围的值当作数值型,如果明确要存储为字符型或日期时间型则应该使用引号包围以避免歧义。
日期时间性数据存储时需要使用引号包围,避免和数值类型的数据产生歧义。关于日期时间的输入方式是非常宽松的,以下几种方式都是被允许的:任意允许的分隔符,建议使用4位的年份。
20110101 2011-01-01 18:40:20 2011/01/01 18-40-20 20110101184020
ENUM数据类型是枚举型。定义方式为ENUM('value1','value2','value3',...),在向该类型的字段中插入数据时只能插入value中的某一个或NULL,插入其他值或空(即'')时都将截断为空数据。存储时会忽略大小写(将转换为ENUM中的字符),且会截断尾随空格。
mysql> create table test6(id int auto_increment primary key,name char(20),gender enum('Mail','f')); mysql> insert into test6(name,gender) values('malongshuai','Mail'),('gaoxiaofang','F'),('wugui','x'),('tuner',null),('woniu',''); Query OK, 5 rows affected Records: 5 Duplicates: 0 Warnings: 2mysql> show warnings; +---------+------+---------------------------------------------+ | Level | Code | Message | +---------+------+---------------------------------------------+ | Warning | 1265 | Data truncated for column 'gender' at row 3 | | Warning | 1265 | Data truncated for column 'gender' at row 5 | +---------+------+---------------------------------------------+2 rows in setmysql> select * from test6; +----+-------------+--------+ | id | name | gender | +----+-------------+--------+ | 1 | malongshuai | Mail | | 2 | gaoxiaofang | f | | 3 | wugui | | | 4 | tuner | NULL | | 5 | woniu | | +----+-------------+--------+5 rows in set
ENUM类型的数据存储时是通过index数值进行存储的,相比于字符串类型,它只需要1或2个字节进行存储即可。理论上,当value的数量少于256个时只需一个字节,超出256个但少于65536个时使用2个字节存储。MySQL/MariaDB限制最多只能存储65536个value。当然,这是理论上的限制,实际存储时要考虑的因素有很多,例如NULL也会占用bit位,所以实际存储时可能250个value就需要2个字节。
ENUM的每个value都通过index号码进行编号,无论是检索还是操作该字段时都会通过index的值来操作。value1的index=1,value2的index=2,依次类推。但需要注意有两个特殊的index值:NULL值的index=NULL,空数据的index=0。
例如ENUM('a','b','c')
,向该字段依次插入'','b','a','c',NULL,'xxx'
时,由于第一个和最后一个都会截断为空数据,所以它们的index为0,插入的NULL的index为NULL,插入的'b','a','c'的index值分别为2,1,3。所以index号码和值的对应关系为:
index | value |
---|---|
NULL | NULL |
0 | '' |
0 | '' |
1 | 'a' |
2 | 'b' |
3 | 'c' |
使用ENUM的index进行数据检索:
mysql> select * from test6 where gender=2; +----+-------------+--------+ | id | name | gender | +----+-------------+--------+ | 2 | gaoxiaofang | f | +----+-------------+--------+1 row in set
特别建议,不要使用ENUM存储数值,因为无论是排序还是检索或其他操作,都是根据index值作为条件的,这很容易产生误解。例如,下面是用ENUM存储两个数值,然后进行检索和排序操作。
mysql> create table test7(id enum('3','1','2')); mysql> insert into test7 values('1'),('2'),('3');# 检索时id=2,但结果查出来却为1,因为id=2的2是enum的index值,在enum中index=2的值为1mysql> select * from test7 where id=2; +----+ | id | +----+ | 1 | +----+1 row in set# 按照id进行排序时,也是通过index大小进行排序的mysql> select * from test7 order by id asc; +----+ | id | +----+ | 3 | | 1 | | 2 | +----+3 rows in set
因此,强烈建议不要在ENUM中存放数值,即使是浮点型数值也很容易出现歧义。
对于SET类型,和enum类似,不区分大小写,存储时删除尾随空格,null也是有效值。但不同的是可以组合多个给出的值。如set('a','b','c','d')
可以存储'a,b','d,b'
等,多个成员之间使用逗号隔开。所以,使用多个成员的时候,成员本身的值中不能出现逗号。如果要存储的内容不在set列表中,则截断为空值。
SET数据类型占用的空间大小和SET成员数量M有关,计算方式为(M+7)/8取整。所以: 1-8个成员占用1个字节;
9-16个成员占用2个字节;
17-24个成员占用3字节;
25-32个成员占用4个字节;
33-64个成员占用8字节。
MySQL/MariaDB限制最多只能有64个成员。
存储SET数据类型的数据时忽略重复成员并按照枚举时的顺序存储。如set('b','b','a')
,存储'a,b,a','b,a,b'
的结果都是'b,a'。
mysql> create table test8(a set('d','b','a')); mysql> insert into test8 values('b,b,a'),('b,a,b'),('bab'); Query OK, 3 rows affected Records: 3 Duplicates: 0 Warnings: 1mysql> select * from test8; +-----+ | a | +-----+ | b,a | | b,a | | | +-----+3 rows in set
使用find_in_set(set_value,set_column_name)
可以检索出包含指定set值set_value的行。例如检索a字段中包含成员b的行:
mysql> select * from test8 where find_in_set('b',a); +-----+ | a | +-----+ | b,a | | b,a | +-----+2 rows in set
unsigned属性就是让数值类型的数据变得无符号化。使用unsigned属性将会改变数值数据类型的范围,例如tinyint类型带符号的范围是-128到127,而使用unsigned时范围将变成0到255。同时unsigned也会限制该列不能插入负数值。
create table t(a int unsigned,b int unsigned);insert into t select 1,2;insert into t select -1,-2;
上面的语句中,在执行第二条语句准备插入负数时将会报错,提示超出范围。
使用unsigned在某些情况下确有其作用,例如一般的ID主键列不会允许使用负数,它相当于实现了一个check约束。但是使用unsigned有时候也会出现些不可预料的问题:在进行数值运算时如果得到负数将会报错。例如上面的表t中,字段a和b都是无符号的列,且有一行a=1,b=2。
mysql> select * from t; +---+---+ | a | b | +---+---+ | 1 | 2 | +---+---+1 row in set
此时如果计算a-b
将会出错,不仅如此,只要是unsigned列参与计算并将得到负数都会出错。
mysql> select a-b from t;1690 - BIGINT UNSIGNED value is out of range in '(`test`.`t`.`a` - `test`.`t`.`b`)'mysql> select a-2 from t;1690 - BIGINT UNSIGNED value is out of range in '(`test`.`t`.`a` - 2)'
如果计算结果不是负数时将没有影响。
mysql> select 2-a,a*3 from t; +-----+-----+ | 2-a | a*3 | +-----+-----+ | 1 | 3 | +-----+-----+1 row in set
这并不是MySQL/MariaDB中的bug,在C语言中的unsigned也一样有类似的问题。这个问题在MySQL/MariaDB中设置set sql_mode='no_unsigned_subtraction'
即可解决。
所以个人建议不要使用unsigned属性修饰字段。
zerofill修饰字段后,不足字段显示部分将使用0来代替空格填充,启用zerofill后将自动设置unsigned。zerofill一般只在设置了列的显示宽度后一起使用。关于列的显示宽度在上文已经介绍过了。
mysql> create table t1(id int(4) zerofill); mysql> select * from t1; +-------+ | id | +-------+ | 0001 | | 0002 | | 0011 | | 83838 | +-------+4 rows in set (0.00 sec)
zerofill只是修饰显示结果,不会影响存储的数据值。
以上がMySQL のデータ型と保存メカニズムの詳細な説明の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。