1. 程式人生 > 其它 >學習筆記2021.12.10

學習筆記2021.12.10

2021.12.10

DML之增刪改

更新資料

  • 使用 UPDATE 語句更新資料。語法如下:
UPDATE table_name
SET column1=value1, column2=value2, … , column=valuen
[WHERE condition]
  • 可以一次更新多條資料。

  • 如果需要回滾資料,需要保證在DML前,進行設定:SET AUTOCOMMIT = FALSE;


  • 使用 WHERE 子句指定需要更新的資料。
  • 要連續修改只需要在set後面加逗號繼續修改就可以了。
UPDATE emp1
SET hire_date = CURDATE()
WHERE id =70;
  • 如果省略 WHERE 子句,則表中的所有資料都將被更新。
UPDATE 	emp1
SET    	id = 110;
  • 更新中的資料完整性錯誤
UPDATE employees
SET    department_id = 55
WHERE  department_id = 110;

說明:不存在 110 號部門

刪除資料

  • 使用 DELETE 語句從表中刪除資料
DELETE FROM table_name [WHERE <condition>];

table_name指定要執行刪除操作的表;“[WHERE ]”為可選引數,指定刪除條件,如果沒有WHERE子句,DELETE語句將刪除表中的所有記錄。

  • 使用 WHERE 子句刪除指定的記錄。
DELETE FROM emp1
WHERE id =103;
  • 如果省略 WHERE 子句,則表中的全部資料將被刪除
DELETE FROM  copy_emp;
  • 刪除中的資料完整性錯誤
DELETE FROM departments
WHERE       department_id = 60;

mysql8的新特性:計算列

這裡先知道這個概念即可,具體的實踐先略了。

什麼叫計算列呢?簡單來說就是某一列的值是通過別的列計算得來的。例如,a列值為1、b列值為2,c列不需要手動插入,定義a+b的結果為c的值,那麼c就是計算列,是通過別的列計算得來的。

在MySQL 8.0中,CREATE TABLE 和 ALTER TABLE 中都支援增加計算列。下面以CREATE TABLE為例進行講解。

舉例:定義資料表tb1,然後定義欄位id、欄位a、欄位b和欄位c,其中欄位c為計算列,用於計算a+b的值。
首先建立測試表tb1,語句如下:

CREATE TABLE tb1(id INT,a INT,b INT,c INT GENERATED ALWAYS AS (a + b) VIRTUAL);

插入演示資料,語句如下:

INSERT INTO tb1(a,b) VALUES (100,200);

查詢資料表tb1中的資料,結果如下:

mysql> SELECT * FROM tb1;+------+------+------+------+| id   | a    | b    | c    |+------+------+------+------+| NULL |  100 |  200 |  300 |+------+------+------+------+1 row in set (0.00 sec)

更新資料中的資料,語句如下:

mysql> UPDATE tb1 SET a = 500;Query OK, 0 rows affected (0.00 sec)Rows matched: 1  Changed: 0  Warnings: 0

DDL和DML的綜合案例

也是個練手的東西,有興趣再去具體的實現,見課件的第11章。

mysql的資料型別

常見的資料型別:見前面建立表那章的內容。

這裡額外講了個字符集設定相關的內容,即character set,這在建立資料庫,建立表或者新增欄位的時候都可以跟上。具體的使用時有需求就注意點即可。

整數型別:

整數型別一共有 5 種,包括 TINYINT、SMALLINT、MEDIUMINT、INT(INTEGER)和 BIGINT。

它們的區別如下表所示:

整數型別 位元組 有符號數取值範圍 無符號數取值範圍
TINYINT 1 -128~127 0~255
SMALLINT 2 -32768~32767 0~65535
MEDIUMINT 3 -8388608~8388607 0~16777215
INT、INTEGER 4 -2147483648~2147483647 0~4294967295
BIGINT 8 -9223372036854775808~9223372036854775807 0~18446744073709551615

可選屬性

整數型別的可選屬性有三個:

M

M: 表示顯示寬度,M的取值範圍是(0, 255)。例如,int(5):當資料寬度小於5位的時候在數字前面需要用字元填滿寬度。該項功能需要配合“ZEROFILL”使用,表示用“0”填滿寬度,否則指定顯示寬度無效。

如果設定了顯示寬度,那麼插入的資料寬度超過顯示寬度限制,會不會截斷或插入失敗?

答案:不會對插入的資料有任何影響,還是按照型別的實際寬度進行儲存,即顯示寬度與型別可以儲存的值範圍無關從MySQL 8.0.17開始,整數資料型別不推薦使用顯示寬度屬性。

整型資料型別可以在定義表結構時指定所需要的顯示寬度,如果不指定,則系統為每一種型別指定預設的寬度值。

舉例:

CREATE TABLE test_int1 ( x TINYINT, y SMALLINT, z MEDIUMINT, m INT, n BIGINT );

TINYINT有符號數和無符號數的取值範圍分別為-128127和0255,由於負號佔了一個數字位,因此TINYINT預設的顯示寬度為4。同理,其他整數型別的預設顯示寬度與其有符號數的最小值的寬度相同。

舉例:

CREATE TABLE test_int2(f1 INT,f2 INT(5),f3 INT(5) ZEROFILL)DESC test_int2;INSERT INTO test_int2(f1,f2,f3)VALUES(1,123,123);INSERT INTO test_int2(f1,f2)VALUES(123456,123456);INSERT INTO test_int2(f1,f2,f3)VALUES(123456,123456,123456);
mysql> SELECT * FROM test_int2;+--------+--------+--------+| f1     | f2     | f3     |+--------+--------+--------+|      1 |    123 |  00123 || 123456 | 123456 |   NULL || 123456 | 123456 | 123456 |+--------+--------+--------+3 rows in set (0.00 sec)

UNSIGNED

UNSIGNED: 無符號型別(非負),所有的整數型別都有一個可選的屬性UNSIGNED(無符號屬性),無符號整數型別的最小取值為0。所以,如果需要在MySQL資料庫中儲存非負整數值時,可以將整數型別設定為無符號型別。

int型別預設顯示寬度為int(11),無符號int型別預設顯示寬度為int(10)。

反正就是以有符號數和無符號數來表示的區別,區別於數字新增時候的範圍。

CREATE TABLE test_int3(f1 INT UNSIGNED);mysql> desc test_int3;+-------+------------------+------+-----+---------+-------+| Field | Type             | Null | Key | Default | Extra |+-------+------------------+------+-----+---------+-------+| f1    | int(10) unsigned | YES  |     | NULL    |       |+-------+------------------+------+-----+---------+-------+1 row in set (0.00 sec)

ZEROFILL

ZEROFILL: 0填充,(如果某列是ZEROFILL,那麼MySQL會自動為當前列新增UNSIGNED屬性),如果指定了ZEROFILL只是表示不夠M位時,用0在左邊填充,如果超過M位,只要不超過資料儲存範圍即可。

原來,在 int(M) 中,M 的值跟 int(M) 所佔多少儲存空間並無任何關係。 int(3)、int(4)、int(8) 在磁碟上都是佔用 4 bytes 的儲存空間。也就是說,int(M),必須和UNSIGNED ZEROFILL一起使用才有意義。如果整數值超過M位,就按照實際位數儲存。只是無須再用字元 0 進行填充。

浮點型別:

浮點數和定點數型別的特點是可以處理小數,你可以把整數看成小數的一個特例。因此,浮點數和定點數的使用場景,比整數大多了。 MySQL支援的浮點數型別,分別是 FLOAT、DOUBLE、REAL。

  • FLOAT 表示單精度浮點數;
  • DOUBLE 表示雙精度浮點數;
  • REAL預設就是 DOUBLE。如果你把 SQL 模式設定為啟用“REAL_AS_FLOAT”,那 麼,MySQL 就認為 REAL 是 FLOAT。如果要啟用“REAL_AS_FLOAT”,可以通過以下 SQL 語句實現:

    SET sql_mode = “REAL_AS_FLOAT”;
    

問題1:FLOAT 和 DOUBLE 這兩種資料型別的區別是啥呢?

FLOAT 佔用位元組數少,取值範圍小;DOUBLE 佔用位元組數多,取值範圍也大。

問題2:為什麼浮點數型別的無符號數取值範圍,只相當於有符號數取值範圍的一半,也就是隻相當於有符號數取值範圍大於等於零的部分呢?

MySQL 儲存浮點數的格式為:符號(S)尾數(M)階碼(E)。因此,無論有沒有符號,MySQL 的浮點數都會儲存表示符號的部分。因此, 所謂的無符號數取值範圍,其實就是有符號數取值範圍大於等於零的部分。

資料精度說明

對於浮點型別,在MySQL中單精度值使用4個位元組,雙精度值使用8個位元組。

  • MySQL允許使用非標準語法(其他資料庫未必支援,因此如果涉及到資料遷移,則最好不要這麼用):FLOAT(M,D)DOUBLE(M,D)。這裡,M稱為精度,D稱為標度。(M,D)中 M=整數位+小數位,D=小數位。 D<=M<=255,0<=D<=30。

    例如,定義為FLOAT(5,2)的一個列可以顯示為-999.99-999.99。如果超過這個範圍會報錯。

  • FLOAT和DOUBLE型別在不指定(M,D)時,預設會按照實際的精度(由實際的硬體和作業系統決定)來顯示。

  • 說明:浮點型別,也可以加UNSIGNED,但是不會改變資料範圍,例如:FLOAT(3,2) UNSIGNED仍然只能表示0-9.99的範圍。

  • 不管是否顯式設定了精度(M,D),這裡MySQL的處理方案如下:

    • 如果儲存時,整數部分超出了範圍,MySQL就會報錯,不允許存這樣的值

    • 如果儲存時,小數點部分若超出範圍,就分以下情況:

      • 若四捨五入後,整數部分沒有超出範圍,則只警告,但能成功操作並四捨五入刪除多餘的小數位後儲存。例如在FLOAT(5,2)列內插入999.009,近似結果是999.01。
      • 若四捨五入後,整數部分超出範圍,則MySQL報錯,並拒絕處理。如FLOAT(5,2)列內插入999.995和-999.995都會報錯。
  • 從MySQL 8.0.17開始,FLOAT(M,D) 和DOUBLE(M,D)用法在官方文件中已經明確不推薦使用,將來可能被移除。另外,關於浮點型FLOAT和DOUBLE的UNSIGNED也不推薦使用了,將來也可能被移除。

在程式設計中,如果用到浮點數,要特別注意誤差問題,因為浮點數是不準確的,所以我們要避免使用“=”來判斷兩個數是否相等。同時,在一些對精確度要求較高的專案中,千萬不要使用浮點數,不然會導致結果錯誤,甚至是造成不可挽回的損失。那麼,MySQL 有沒有精準的資料型別呢?當然有,這就是定點數型別:DECIMAL

定點數

  • MySQL中的定點數型別只有 DECIMAL 一種型別。

    資料型別 位元組數 含義
    DECIMAL(M,D),DEC,NUMERIC M+2位元組 有效範圍由M和D決定

    使用 DECIMAL(M,D) 的方式表示高精度小數。其中,M被稱為精度,D被稱為標度。0<=M<=65,0<=D<=30,D<M。例如,定義DECIMAL(5,2)的型別,表示該列取值範圍是-999.99~999.99。

  • DECIMAL(M,D)的最大取值範圍與DOUBLE型別一樣,但是有效的資料範圍是由M和D決定的。DECIMAL 的儲存空間並不是固定的,由精度值M決定,總共佔用的儲存空間為M+2個位元組。也就是說,在一些對精度要求不高的場景下,比起佔用同樣位元組長度的定點數,浮點數表達的數值範圍可以更大一些。

  • 定點數在MySQL內部是以字串的形式進行儲存,這就決定了它一定是精準的。

  • 當DECIMAL型別不指定精度和標度時,其預設為DECIMAL(10,0)。當資料的精度超出了定點數型別的精度範圍時,則MySQL同樣會進行四捨五入處理。

  • 浮點數 vs 定點數

    • 浮點數相對於定點數的優點是在長度一定的情況下,浮點型別取值範圍大,但是不精準,適用於需要取值範圍大,又可以容忍微小誤差的科學計算場景(比如計算化學、分子建模、流體動力學等)
    • 定點數型別取值範圍相對小,但是精準,沒有誤差,適合於對精度要求極高的場景 (比如涉及金額計算的場景)

日期和時間型別:

日期與時間是重要的資訊,在我們的系統中,幾乎所有的資料表都用得到。原因是客戶需要知道資料的時間標籤,從而進行資料查詢、統計和處理。

MySQL有多種表示日期和時間的資料型別,不同的版本可能有所差異,MySQL8.0版本支援的日期和時間型別主要有:YEAR型別、TIME型別、DATE型別、DATETIME型別和TIMESTAMP型別。

  • YEAR型別通常用來表示年
  • DATE型別通常用來表示年、月、日
  • TIME型別通常用來表示時、分、秒
  • DATETIME型別通常用來表示年、月、日、時、分、秒
  • TIMESTAMP型別通常用來表示帶時區的年、月、日、時、分、秒
型別 名稱 位元組 日期格式 最小值 最大值
YEAR 1 YYYY或YY 1901 2155
TIME 時間 3 HH:MM:SS -838:59:59 838:59:59
DATE 日期 3 YYYY-MM-DD 1000-01-01 9999-12-03
DATETIME 日期時間 8 YYYY-MM-DD HH:MM:SS 1000-01-01 00:00:00 9999-12-31 23:59:59
TIMESTAMP 日期時間 4 YYYY-MM-DD HH:MM:SS 1970-01-01 00:00:00 UTC 2038-01-19 03:14:07UTC

可以看到,不同資料型別表示的時間內容不同、取值範圍不同,而且佔用的位元組數也不一樣,你要根據實際需要靈活選取。

為什麼時間型別 TIME 的取值範圍不是 -23:59:59~23:59:59 呢?原因是 MySQL 設計的 TIME 型別,不光表示一天之內的時間,而且可以用來表示一個時間間隔,這個時間間隔可以超過 24 小時。

更多的細節都是一些記憶的東西,也沒有任何的實踐價值,有需求再去直接瞭解即可。

開發中的經驗

用得最多的日期時間型別,就是 DATETIME。雖然 MySQL 也支援 YEAR(年)、 TIME(時間)、DATE(日期),以及 TIMESTAMP 型別,但是在實際專案中,儘量用 DATETIME 型別。因為這個資料型別包括了完整的日期和時間資訊,取值範圍也最大,使用起來比較方便。畢竟,如果日期時間資訊分散在好幾個欄位,很不容易記,而且查詢的時候,SQL 語句也會更加複雜。

此外,一般存註冊時間、商品釋出時間等,不建議使用DATETIME儲存,而是使用時間戳,因為DATETIME雖然直觀,但不便於計算。

mysql> SELECT UNIX_TIMESTAMP();+------------------+| UNIX_TIMESTAMP() |+------------------+|       1635932762 |+------------------+1 row in set (0.00 sec)

文字字串型別:

在實際的專案中,我們還經常遇到一種資料,就是字串資料。

MySQL中,文字字串總體上分為CHARVARCHARTINYTEXTTEXTMEDIUMTEXTLONGTEXTENUMSET等型別。

CHAR與VARCHAR型別

CHAR和VARCHAR型別都可以儲存比較短的字串。

字串(文字)型別 特點 長度 長度範圍 佔用的儲存空間
CHAR(M) 固定長度 M 0 <= M <= 255 M個位元組
VARCHAR(M) 可變長度 M 0 <= M <= 65535 (實際長度 + 1) 個位元組

CHAR型別:

  • CHAR(M) 型別一般需要預先定義字串長度。如果不指定(M),則表示長度預設是1個字元。
  • 如果儲存時,資料的實際長度比CHAR型別宣告的長度小,則會在右側填充空格以達到指定的長度。當MySQL檢索CHAR型別的資料時,CHAR型別的欄位會去除尾部的空格。
  • 定義CHAR型別欄位時,宣告的欄位長度即為CHAR型別欄位所佔的儲存空間的位元組數。
CREATE TABLE test_char1(c1 CHAR,c2 CHAR(5));DESC test_char1;
INSERT INTO test_char1VALUES('a','Tom');SELECT c1,CONCAT(c2,'***') FROM test_char1;
INSERT INTO test_char1(c2)VALUES('a  ');SELECT CHAR_LENGTH(c2)FROM test_char1;

VARCHAR型別:

  • VARCHAR(M) 定義時,必須指定長度M,否則報錯。
  • MySQL4.0版本以下,varchar(20):指的是20位元組,如果存放UTF8漢字時,只能存6個(每個漢字3位元組) ;MySQL5.0版本以上,varchar(20):指的是20字元。
  • 檢索VARCHAR型別的欄位資料時,會保留資料尾部的空格。VARCHAR型別的欄位所佔用的儲存空間為字串實際長度加1個位元組。
CREATE TABLE test_varchar1(NAME VARCHAR  #錯誤);
#Column length too big for column 'NAME' (max = 21845);CREATE TABLE test_varchar2(NAME VARCHAR(65535)  #錯誤);
CREATE TABLE test_varchar3(NAME VARCHAR(5));INSERT INTO test_varchar3VALUES('尚矽谷'),('尚矽谷教育');#Data too long for column 'NAME' at row 1INSERT INTO test_varchar3VALUES('尚矽谷IT教育');

哪些情況使用 CHAR 或 VARCHAR 更好

型別 特點 空間上 時間上 適用場景
CHAR(M) 固定長度 浪費儲存空間 效率高 儲存不大,速度要求高
VARCHAR(M) 可變長度 節省儲存空間 效率低 非CHAR的情況

情況1:儲存很短的資訊。比如門牌號碼101,201……這樣很短的資訊應該用char,因為varchar還要佔個byte用於儲存資訊長度,本來打算節約儲存的,結果得不償失。

情況2:固定長度的。比如使用uuid作為主鍵,那用char應該更合適。因為他固定長度,varchar動態根據長度的特性就消失了,而且還要佔個長度資訊。

情況3:十分頻繁改變的column。因為varchar每次儲存都要有額外的計算,得到長度等工作,如果一個非常頻繁改變的,那就要有很多的精力用於計算,而這些對於char來說是不需要的。

情況4:具體儲存引擎中的情況:

  • MyISAM 資料儲存引擎和資料列:MyISAM資料表,最好使用固定長度(CHAR)的資料列代替可變長度(VARCHAR)的資料列。這樣使得整個表靜態化,從而使資料檢索更快,用空間換時間。

  • MEMORY 儲存引擎和資料列:MEMORY資料表目前都使用固定長度的資料行儲存,因此無論使用CHAR或VARCHAR列都沒有關係,兩者都是作為CHAR型別處理的。

  • InnoDB儲存引擎,建議使用VARCHAR型別。因為對於InnoDB資料表,內部的行儲存格式並沒有區分固定長度和可變長度列(所有資料行都使用指向資料列值的頭指標),而且主要影響效能的因素是資料行使用的儲存總量,由於char平均佔用的空間多於varchar,所以除了簡短並且固定長度的,其他考慮varchar。這樣節省空間,對磁碟I/O和資料儲存總量比較好。

還有一種text型別

略了,感覺怎麼見過,有個印象就好。

enum型別

ENUM型別也叫作列舉型別,ENUM型別的取值範圍需要在定義欄位時進行指定。設定欄位值時,ENUM型別只允許從成員中選取單個值,不能一次選取多個值。

其所需要的儲存空間由定義ENUM型別時指定的成員個數決定。

文字字串型別 長度 長度範圍 佔用的儲存空間
ENUM L 1 <= L <= 65535 1或2個位元組
  • 當ENUM型別包含1~255個成員時,需要1個位元組的儲存空間;

  • 當ENUM型別包含256~65535個成員時,需要2個位元組的儲存空間。

  • ENUM型別的成員個數的上限為65535個。

舉例:

建立表如下:

CREATE TABLE test_enum(season ENUM('春','夏','秋','冬','unknow'));

新增資料:

INSERT INTO test_enumVALUES('春'),('秋');# 忽略大小寫INSERT INTO test_enumVALUES('UNKNOW');# 允許按照角標的方式獲取指定索引位置的列舉值INSERT INTO test_enumVALUES('1'),(3);# Data truncated for column 'season' at row 1INSERT INTO test_enumVALUES('ab');# 當ENUM型別的欄位沒有宣告為NOT NULL時,插入NULL也是有效的INSERT INTO test_enumVALUES(NULL);

SET型別

SET表示一個字串物件,可以包含0個或多個成員,但成員個數的上限為64。設定欄位值時,可以取取值範圍內的 0 個或多個值。

當SET型別包含的成員個數不同時,其所佔用的儲存空間也是不同的,具體如下:

成員個數範圍(L表示實際成員個數) 佔用的儲存空間
1 <= L <= 8 1個位元組
9 <= L <= 16 2個位元組
17 <= L <= 24 3個位元組
25 <= L <= 32 4個位元組
33 <= L <= 64 8個位元組

SET型別在儲存資料時成員個數越多,其佔用的儲存空間越大。注意:SET型別在選取成員時,可以一次選擇多個成員,這一點與ENUM型別不同。

舉例:

建立表:

CREATE TABLE test_set(s SET ('A', 'B', 'C'));

向表中插入資料:

INSERT INTO test_set (s) VALUES ('A'), ('A,B');#插入重複的SET型別成員時,MySQL會自動刪除重複的成員INSERT INTO test_set (s) VALUES ('A,B,C,A');#向SET型別的欄位插入SET成員中不存在的值時,MySQL會丟擲錯誤。INSERT INTO test_set (s) VALUES ('A,B,C,D');SELECT *FROM test_set;

舉例:

CREATE TABLE temp_mul(gender ENUM('男','女'),hobby SET('吃飯','睡覺','打豆豆','寫程式碼'));
INSERT INTO temp_mul VALUES('男','睡覺,打豆豆'); #成功# Data truncated for column 'gender' at row 1INSERT INTO temp_mul VALUES('男,女','睡覺,寫程式碼'); #失敗# Data truncated for column 'gender' at row 1INSERT INTO temp_mul VALUES('妖','睡覺,寫程式碼');#失敗INSERT INTO temp_mul VALUES('男','睡覺,寫程式碼,吃飯'); #成功

二進位制字串型別和json型別

還是先略吧,感覺暫時都是用不上的,知道有這麼一個東西就可以了。

簡單做個總結以及預設使用差不多:

在定義資料型別時,如果確定是整數,就用 INT; 如果是小數,一定用定點數型別 DECIMAL(M,D); 如果是日期與時間,就用 DATETIME

這樣做的好處是,首先確保你的系統不會因為資料型別定義出錯。不過,凡事都是有兩面的,可靠性好,並不意味著高效。比如,TEXT 雖然使用方便,但是效率不如 CHAR(M) 和 VARCHAR(M)。

關於字串的選擇,建議參考如下阿里巴巴的《Java開發手冊》規範:

阿里巴巴《Java開發手冊》之MySQL資料庫:

  • 任何欄位如果為非負數,必須是 UNSIGNED
  • 強制】小數型別為 DECIMAL,禁止使用 FLOAT 和 DOUBLE。
    • 說明:在儲存的時候,FLOAT 和 DOUBLE 都存在精度損失的問題,很可能在比較值的時候,得到不正確的結果。如果儲存的資料範圍超過 DECIMAL 的範圍,建議將資料拆成整數和小數並分開儲存。
  • 強制】如果儲存的字串長度幾乎相等,使用 CHAR 定長字串型別。
  • 強制】VARCHAR 是可變長字串,不預先分配儲存空間,長度不要超過 5000。如果儲存長度大於此值,定義欄位型別為 TEXT,獨立出來一張表,用主鍵來對應,避免影響其它欄位索引效率。