MySQL中關於數據類型指定寬度以後的狀況

概述

  MySQL有不少種數據類型,最經常使用的就是int,char,varchar,這些類型在建立表的時候均可以指定該字段的寬度,方法是在類型後面加一個括號,括號中寫寬度就能夠了。php

  可是,在指定寬度以後,有時候,咱們能夠看到插入的數據有一些被截斷了有一些並無截斷,而是四捨五入了甚至什麼操做都沒有,原樣插入了mysql

  下面對於每一種數據類型單獨測試:sql

  

數字型(int、tinyint...)

mysql> create table t (id int(5));
mysql> insert into t values(1234567),(123),(12345);
mysql> select * from t;
+---------+
| id      |
+---------+
| 1234567 |
|     123 |
|   12345 |
+---------+

  從上面的例子中能夠看到,對於int而言,雖然指定了寬度,可是當插入的數據寬度大於指定的寬度時,並不會截斷。數據庫

  其實對於int而言,要指定寬度,那麼就一定要指定zerofill,但一樣,zerofill只是在寬度不夠的時候用0填充,可是寬度大於指定寬度時,數據仍然不會被截取。測試

mysql> create table t (id int(5) zerofill);
mysql> insert into t values(1234567),(123),(12345);
mysql> select * from t;
+---------+
| id      |
+---------+
| 1234567 |
|   00123 |
|   12345 |
+---------+

  

  

字符串型(char、varchar)

mysql> create table t (fields_1 char(5),fields_2 varchar(5));
Query OK, 0 rows affected (0.01 sec)

mysql> insert into t values("123","123"),("12345","12345"),("1234567","1234567");
Query OK, 3 rows affected, 2 warnings (0.00 sec)
Records: 3  Duplicates: 0  Warnings: 2

mysql> show Warnings;
+---------+------+-----------------------------------------------+
| Level   | Code | Message                                       |
+---------+------+-----------------------------------------------+
| Warning | 1265 | Data truncated for column 'fields_1' at row 3 |
| Warning | 1265 | Data truncated for column 'fields_2' at row 3 |
+---------+------+-----------------------------------------------+
2 rows in set (0.00 sec)

mysql> select fields_1,length(fields_1),fields_2,length(fields_2) from t;
+----------+------------------+----------+------------------+
| fields_1 | length(fields_1) | fields_2 | length(fields_2) |
+----------+------------------+----------+------------------+
| 123      |                3 | 123      |                3 |
| 12345    |                5 | 12345    |                5 |
| 12345    |                5 | 12345    |                5 |
+----------+------------------+----------+------------------+

  能夠看到,對於char和varchar,若是制定了寬度,若是要插入的字符串的寬度超過了指定的寬度,則會截取掉超出的部分。fetch

   簡單來講,varchar的可變長度,這個可變,前提是存入的字符串長度不超過定義該字段時指定的長度,若是長度超過了指定長度,即便是可變長度字符串類型,數據仍會出現截斷。編碼

  能夠簡單記爲:可縮不可擴。spa

  而固定長度的char類型,在存儲效率比varchar高,可是,會存在空間浪費的狀況,因此空間利用率沒有varchar高,而varchar是可變長度的,就意味着,在讀數據的時候,效率沒有char類型高,由於在讀數據的時候要判斷是否讀到結尾。code

  

 

拓展1

  前面已經提到,對於數值類型的字段後面的寬度來講,只有在指定zerofill的時候,後面指定的寬度纔有意義,不然,既不會出現截斷,也不會出現0填充。那麼,不會出現截斷,是否是說,向一個int(5)字段的插入一個值,這個值是12345678912345678912345678...(由100位數字長度),那麼還能存進去嗎?server

  看下面示例:

mysql> create table t (id int(5) zerofill);                                 
Query OK, 0 rows affected (0.10 sec)                                        
                                                                            
mysql> insert into t values (9999999999999999999999999999999999999999999);  
Query OK, 1 row affected, 2 warnings (0.00 sec)                             
                                                                            
mysql> show warnings;                                                       
+---------+------+---------------------------------------------+            
| Level   | Code | Message                                     |            
+---------+------+---------------------------------------------+            
| Warning | 1264 | Out of range value for column 'id' at row 1 |            
| Warning | 1264 | Out of range value for column 'id' at row 1 |            
+---------+------+---------------------------------------------+            
2 rows in set (0.00 sec)                                                    
                                                                            
mysql> select * from t;                                                     
+------------+                                                              
| id         |                                                              
+------------+                                                              
| 4294967295 |                                                              
+------------+                                                              
1 row in set (0.00 sec)

  能夠從警告信息和執行結果中看出,當嘗試向指定寬度字段插入一個很大的數據,大到遠超該數據類型的上限,執行雖然會出現警告,可是,數據確實插入了,只不過存儲的數據不是插入的數據,而是存了一個該類型的最大值。

  因此能夠得出結論:對一個數字類型的字段而言,其數據類型已經限定了它的數據範圍,當嘗試插入一個超過數據範圍的值時,會觸發警告,同時,存入該數據類型的最大值。

 

 

拓展2

  前面也提到了字符串(char和varchar)後面指定的寬度,這個寬度就不像數字類型的寬度了,由於,若是是字符串類型,那麼,一旦超過字符串後面指定的寬度,那麼必定會出現截斷。

  這裏有個問題,字符串後面指定的寬度,好比char(5),varchar(5),這個5是指5個字符,仍是指5個字節呢,或者說是5個bit(位)呢?

  前面的示例中,很顯然看出,這個5不多是bit(位),畢竟一個字節就有8位,在測試中,一個字符都插不進去。

    那麼,要麼是5個字符,或者5個字節。可能你會疑惑,5個字符和5個字節有什麼區別嗎?abc,是3個字符,同時也是3個字節,何須去區分呢?

  那你想一下,我們的漢字,一個漢字,通過不一樣的編碼(GBK,GB2312,lantin1,UTF-8,UTF-8mb4)以後,所佔的字節數是不必定相同的呀。

mysql> create table t (field char(5));                                                                
Query OK, 0 rows affected (0.40 sec)                                                                  
                                                                                                      
mysql> show create table t\G                                                                          
*************************** 1. row ***************************                                        
       Table: t                                                                                       
Create Table: CREATE TABLE `t` (                                                                      
  `field` char(5) DEFAULT NULL                                                                        
) ENGINE=MyISAM DEFAULT CHARSET=latin1                                                                
1 row in set (0.00 sec)                                                                               
                                                                                                      
mysql> insert into t values ('abcde');                                                                
Query OK, 1 row affected (0.00 sec)                                                                   
                                                                                                      
mysql> insert into t values ('中國你好啊');                                                           
Query OK, 1 row affected, 2 warnings (0.00 sec)                                                       
                                                                                                      
mysql> show warnings;                                                                                 
+---------+------+-----------------------------------------------------------------------------------+
| Level   | Code | Message                                                                           |
+---------+------+-----------------------------------------------------------------------------------+
| Warning | 1300 | Invalid utf8 character string: 'D6D0B9'                                           |
| Warning | 1366 | Incorrect string value: '\xD6\xD0\xB9\xFA\xC4\xE3...' for column 'field' at row 1 |
+---------+------+-----------------------------------------------------------------------------------+
2 rows in set (0.00 sec)

  從上面的實例,很明顯能夠看出答案,char(5)後面的5,是指的5字節,而不是5個字符。

  能夠查看一下,存入的內容是什麼:

mysql> select * from t;                     
+-------+                                   
| field |                                   
+-------+                                   
| abcde |                                   
| ????? |                                   
+-------+                                   
2 rows in set (0.03 sec)                    
                                            
mysql> set names utf8;                      
Query OK, 0 rows affected (0.02 sec)        
                                            
mysql> select * from t;                     
+-------+                                   
| field |                                   
+-------+                                   
| abcde |                                   
| ????? |                                   
+-------+                                   
2 rows in set (0.00 sec)

  能夠看出,後面雖然插入的「中國你好啊」,可是存的時候,已經出現亂碼了,即便強制指定字符集,也是顯示亂碼。

  我們通常使用的都會utf8或者utf8mb4,能夠在建立表格的時候,指定default charset=utf8。

 

 

拓展3 

  若是一個漢字使用某種編碼方式(好比utf8),在存儲的時候佔3字節,那麼兩個漢字,就須要6個字節來存。

  那麼,若是char(5)類型的字段,能存入「中國」兩個字嗎?中國兩個字編碼以後是6字節。

  首先解決一個問題:

mysql> create table t ( field char(5)) default charset=utf8;                         
Query OK, 0 rows affected (0.11 sec)                                                 
                                                                                     
mysql> insert into t values ('abcde');                                               
Query OK, 1 row affected (0.00 sec)                                                  
                                                                                     
mysql> insert into t values ('中');                                                   
Query OK, 1 row affected, 2 warnings (0.00 sec)                                      
                                                                                     
mysql> show warnings;                                                                
+---------+------+----------------------------------------------------------------+  
| Level   | Code | Message                                                        |  
+---------+------+----------------------------------------------------------------+  
| Warning | 1300 | Invalid utf8 character string: 'D6D0'                          |  
| Warning | 1366 | Incorrect string value: '\xD6\xD0' for column 'field' at row 1 |  
+---------+------+----------------------------------------------------------------+  
2 rows in set (0.00 sec)                                                             
                                                                                     
mysql> select * from t;                                                              
+-------+                                                                            
| field |                                                                            
+-------+                                                                            
| abcde |                                                                            
|       |                                                                            
+-------+                                                                            
2 rows in set (0.00 sec)                                                             
                                                                                     
mysql> set names utf8;                                                               
Query OK, 0 rows affected (0.00 sec)                                                 
                                                                                     
mysql> select * from t;                                                              
+-------+                                                                            
| field |                                                                            
+-------+                                                                            
| abcde |                                                                            
|       |                                                                            
+-------+                                                                            
2 rows in set (0.00 sec)

  能夠看到,即便單個漢字「中」編碼以後3字節(未超過5字節範圍,仍舊未存入),這時能夠看一下數據庫的字符集:

mysql> show variables like '%char%';
+--------------------------+-----------------------------------+
| Variable_name            | Value                             |
+--------------------------+-----------------------------------+
| character_set_client     | utf8                              |
| character_set_connection | utf8                              |
| character_set_database   | latin1                            |
| character_set_filesystem | binary                            |
| character_set_results    | utf8                              |
| character_set_server     | utf8                              |
| character_set_system     | utf8                              |
| character_sets_dir       | E:\phpStudy\MySQL\share\charsets\ |
+--------------------------+-----------------------------------+
8 rows in set (0.00 sec)

  能夠看到,character_set_database設定的仍是latin1字符集編碼,可使用下面的命令修改:

set character_set_database='utf8';

 

    接下來,測試過程當中,即便插入單獨的一個漢字,也會出現警告,而且查看插入的值,只出現一個?或者一些亂碼,甚至沒有值(空的)。

  我嘗試在php中執行插入和查看操做:

<?php
    $mysqli = new Mysqli();
    $mysqli->connect("localhost","root","root","test");
    $mysqli->set_charset("utf8");

    $mysqli->query("truncate table t");
    $mysqli->query("insert into t values ('abcde')");
    $mysqli->query("insert into t values ('你')");
    $mysqli->query("insert into t values ('你好')");

    $sql = "select * from t";
    $mysqli_result = $mysqli->query($sql);

    $res = $mysqli_result->fetch_all();//一次性去的全部數據
    print_r($res);

  執行以後,結果以下:

λ php index.php          
Array                    
(                        
    [0] => Array         
        (                
            [0] => abcde 
        )                
                         
    [1] => Array         
        (                
            [0] => ?     
        )                
                         
    [2] => Array         
        (                
            [0] => ???   
        )                
                         
)

  至於爲何會這樣,如今還沒找到問題根源。以前好像也沒遇到過這種狀況呀,等有時間在其餘機器上試一下。

相關文章
相關標籤/搜索