MySql數據類型
Enum類型和SET類型
假如我們有一列是性別只能填男或者女,如果使用Int或者varchar非常尷尬,我們提出了一個叫ENUM的類型,也稱爲枚舉類型,它的格式如:ENUM('str1', 'str2', 'str3' ⋯)它表示在給定的字符串列表裏選擇一個。比如我們的性別一列可以定義成ENUM('男', '女')類型。這個的意思就是性別一列只能在'男'或者'女'這兩個字符串之間選擇一個,相當於一個單選框~
有的時候某一列的值可以在給定的字符串列表中挑選多個,假設學生的基本信息加了一列興趣屬性,這個屬性的值可以從給定的興趣列表中挑選多個,那我們可以使用SET類型,它的格式如下:
SET('str1', 'str2', 'str3' ⋯)它表示可以在給定的字符串列表裏選擇多個。我們的興趣一列就可以定義成SET('打球', '畫畫', '扯犢子', '玩遊戲')類型。這個的意思就是興趣一列可以在給定的這幾個字符串中選擇一個或多個,相當於一個多選框~
綜上所述,ENUM和SET類型都是一種特殊的字符串類型,在從字符串列表中單選或多選元素的時候會用得到它們。
主鍵和UNIQUE約束的區別
主鍵和unique約束都能保證某個列或者列組合的唯一性,但是:
- 1.一張表中只能定義一個主鍵,卻可以定義多個unique約束
- 2.規定:主鍵列不允許存放null,而聲明瞭unique屬性的列可以存放null,而且null可以重複出現在多條記錄中。
如果A表中的某個列或者某些列依賴與B表中的某個列或者某些列,那麼就稱A表爲子表,B表爲父表。子表和父表可以使用外鍵來關聯起來,上邊例子中student_score表的number列依賴於student_info的number列,所以student_info就是一個父表,student_score就是子表。
查詢通配符匹配:
- %:代表任意一個字符串。
- _:代表任意一個字符。_只能代表一個字符(%是代表任意一個字符串)
轉義通配符
如果待匹配的字符串中本身就包含普通字符'%'或者'_'該咋辦,怎麼區分它是一個通配符還是一個普通字符呢?
答:如果匹配字符串中需要普通字符'%'或者'_'的話,需要在它們前邊加一個反斜槓\來和通配符區分開來,也就是說:
'\%'代表普通字符'%'
'\_'代表普通字符'_' 比方說這樣:
COUNT函數
COUNT函數使用來統計行數的,它有下邊兩種使用方式:
- 1.COUNT(*):對錶中行的數目進行計數,不管列的值是不是NULL。
- 2.COUNT(列名):對特定的列進行計數,會忽略掉該列爲NULL的行。
兩者的區別是會不會忽略統計列的值爲NULL的行!兩者的區別是會不會忽略統計列的值爲NULL的行!兩者的區別是會不會忽略統計列的值爲NULL的行!
學生信息表
DROP TABLE IF EXISTS `student_info`;
CREATE TABLE `student_info` (
`number` int(11) NOT NULL,
`name` varchar(5) DEFAULT NULL,
`sex` enum('男','女') DEFAULT NULL,
`id_number` char(18) DEFAULT NULL,
`department` varchar(30) DEFAULT NULL,
`major` varchar(30) DEFAULT NULL,
`enrollment_time` date DEFAULT NULL,
PRIMARY KEY (`number`),
UNIQUE KEY `uk_id_number` (`id_number`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `student_info` VALUES ('20180101', '杜子騰', '男', '158177199901044792', '計算機學院', '計算機科學與工程', '2018-09-01');
INSERT INTO `student_info` VALUES ('20180102', '杜琦燕', '女', '151008199801178529', '計算機學院', '計算機科學與工程', '2018-09-01');
INSERT INTO `student_info` VALUES ('20180103', '範統', '男', '17156319980116959X', '計算機學院', '軟件工程', '2018-09-01');
INSERT INTO `student_info` VALUES ('20180104', '史珍香', '女', '141992199701078600', '計算機學院', '軟件工程', '2018-09-01');
INSERT INTO `student_info` VALUES ('20180105', '範劍', '男', '181048199308156368', '航天學院', '飛行器設計', '2018-09-01');
INSERT INTO `student_info` VALUES ('20180106', '朱逸羣', '男', '197995199501078445', '航天學院', '電子信息', '2018-09-01');
學生成績表
DROP TABLE IF EXISTS `student_score`;
CREATE TABLE `student_score` (
`number` int(11) NOT NULL,
`subject` varchar(30) NOT NULL,
`score` tinyint(4) DEFAULT NULL,
PRIMARY KEY (`number`,`subject`),
CONSTRAINT `student_score_ibfk_1` FOREIGN KEY (`number`) REFERENCES `student_info` (`number`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `student_score` VALUES ('20180101', '母豬的產後護理', '78');
INSERT INTO `student_score` VALUES ('20180101', '論薩達姆的戰爭準備', '88');
INSERT INTO `student_score` VALUES ('20180102', '母豬的產後護理', '100');
INSERT INTO `student_score` VALUES ('20180102', '論薩達姆的戰爭準備', '98');
INSERT INTO `student_score` VALUES ('20180103', '母豬的產後護理', '59');
INSERT INTO `student_score` VALUES ('20180103', '論薩達姆的戰爭準備', '61');
INSERT INTO `student_score` VALUES ('20180104', '母豬的產後護理', '55');
INSERT INTO `student_score` VALUES ('20180104', '論薩達姆的戰爭準備', '46');
having查詢理解
查詢最高分超過98的科目的平均分
SELECT subject, AVG(score) FROM student_score GROUP BY subject HAVING MAX(score) > 98;
與where子句的區別:where子句在分組前進行過濾,作用於每一條記錄,where子句過濾掉的記錄將不包含在分組中。而having子句在數據分組後進行過濾,作用於分組
子查詢
- 1.標量子查詢
SELECT * FROM student_score WHERE number = (SELECT number FROM student_info WHERE name = '杜琦燕');
- 2.列子查詢
SELECT * FROM student_score WHERE number IN (SELECT number FROM student_info WHERE major = '計算機科學與工程');
- 3.表子查詢
SELECT * FROM student_score WHERE (number, subject) IN (SELECT number, '母豬的產後護理' FROM student_info WHERE major = '計算機科學與工程');
- 4.exists和not子查詢
有時候外層查詢並不關心子查詢中的結果是什麼,而只關心子查詢的結果集是不是爲空集,這時可以用到下邊這兩個操作符
SELECT * FROM student_score WHERE EXISTS (SELECT * FROM student_info WHERE number = 20180108);
其中子查詢的意思是在student_info表中查找學號爲20180108的學生信息,很顯然並沒有學號爲20180108的學生,所以子查詢的結果集是一個空集,於是EXISTS表達式的結果爲FALSE,所以外層查詢也就不查了,直接返回了一個Empty set,表示沒有結果。你可以自己試一下NOT EXISTS的使用。
- 5.相關子查詢
有時候我們需要在子查詢語句中引用到外層查詢的值,這樣的子查詢就不能當作一個獨立的語句執行,這種子查詢被稱爲相關子查詢
SELECT number, name, id_number, major FROM student_info WHERE EXISTS (SELECT * FROM student_score WHERE student_score.number = student_info.number);
這條查詢語句含義爲:查詢在student_score表有成績的學生信息。
查詢流程分析:
1.先執行外層查詢獲得到student_info表的第一條記錄,發現它的number值是20180101。把20180101當作參數傳入到子查詢,此時子查詢的意思是判斷student_score表的number字段是否有20180101這個值存在,子查詢的結果是該值存在,所以整個EXISTS表達式的值爲TRUE,那麼student_info表的第一條記錄可以被加入到結果集。
2.再執行外層查詢獲得到student_info表的第二條記錄,發現它的number值是20180102,與上邊的步驟相同,student_info表的第二條記錄也可以被加入到結果集。
3.與上邊類似,student_info表的第三條記錄也可以被加入到結果集
4.與上邊類似,student_info表的第四條記錄也可以被加入到結果集。
5.再執行外層查詢獲得到student_info表的第五條記錄,發現它的number值是20180105,把20180105當作參數傳入到它的子查詢,此時子查詢的意思是判斷student_score表的number字段是否有20180105這個值存在,子查詢的結果是該值不存在,所以整個EXISTS表達式的值爲FALSE,那麼student_info表的第五條記錄就不被加入結果集中。
6.與上一步驟類似,student_info表的第六條記錄也不被加入結果集中。
7.student_info表沒有更多的記錄了,結束查詢。
exists和in的查詢方式
#對B查詢涉及id,使用索引,故B表效率高,可用大表 -->外小內大
select * from A where exists (select * from B where A.id=B.id);
#對A查詢涉及id,使用索引,故A表效率高,可用大表 -->外大內小
select * from A where A.id in (select id from B);
exists是對外表進行loop循環,每次loop循環在對內表(子查詢)進行查詢,那麼因爲對內表的查詢使用的索引(內表效率高,故可用大表),而外表有多大都需要遍歷,不可避免(儘量用小表),故內表大的使用exists,可加快效率;
in是把外表和內表做hash連接,先查詢內表,再把內表結果與外表匹配,對外表使用索引(外表效率高,可用大表),而內表多大都需要查詢,不可避免,故外表大的使用in,可加快效率。
笛卡爾積
select * from t1,t2;
上述sql查詢出來的結果集爲笛卡爾積:表裏數據任意組合比如t16條記錄,t2中8條記錄,他們的笛卡爾積數量爲6*8=48。
Join,Inner Join,Cross Join
select * from t1,t2;
select * from t1 INNER JOIN t2;
select * from t1 CROSS JOIN t2;
select * from t1 JOIN t2;
上述幾種寫法均是等價的
Union和Union All
Union和Union All的每個子查詢加入Order By語句作用和沒有加入一樣:組合查詢並不保證最後彙總起來的大結果集中的順序是按照各個小查詢的結果集中的順序排序的
Insert Ignore Into
使用場景:對於那些是主鍵或者具有UNIQUE約束的列或者列組合來說,如果表中已存在的記錄中沒有與待插入記錄在這些列或者列組合上重複的值,那麼就把待插入記錄插到表中,否則忽略此次插入操作
insert on duplicate key update
對於那些是主鍵或者具有UNIQUE約束的列或者列組合來說,如果表中已存在的記錄中沒有與待插入記錄在這些列或者列組合上重複的值,那麼就把待插入記錄插到表中,否則按照規定去更新那條重複的記錄中某些列的值
insert into first_table VALUES(2,'紅牛'),(3,'谷歌') on DUPLICATE key UPDATE second_column = values(second_column)
delete和update
delete和update語句可以使用 order by、limit限制刪除或者更新的條目數量
視圖
創建視圖語句:
CREATE VIEW 視圖名 AS 查詢語句
指定視圖虛擬列名稱:
CREATE VIEW student_info_view(no, n, m) AS SELECT number, name, major FROM student_info;
在對視圖進行查詢時,MySQL服務器將會幫助我們把對視圖的查詢語句轉換爲對底層表的查詢語句然後再執行。
在使用層面,我們完全可以把視圖當作一個表去使用,但是它的實現原理卻是在執行語句時轉換爲對底層表的操作。視圖的優勢:簡化語句的書寫,避免了每次都要寫一遍又臭又長的語句,而且對視圖的操作更加直觀,使用者也不用去考慮它的底層實現細節。
對視圖執行INSERT、DELETE、UPDATE語句的本質上是對該視圖對應的底層表中的數據進行增、刪、改操作
不過並不是可以在所有的視圖上執行更新語句的,在生成視圖的時候使用了下邊這些語句的都不能進行更新:
- 聚集函數(比如SUM(), MIN(), MAX(), COUNT()等等)
- DISTINCT
- GROUP BY
- HAVING
- UNION 或者 UNION ALL
- 某些子查詢
- 某些連接查詢
- 等等等等
自定義變量:規定自定義變量前邊必須加@符號
SET @a = 1;
SELECT @a;
SET @a = '哈哈哈';
SET @b = @a;
select @b;
SET @a = (SELECT m1 FROM t1 LIMIT 1);
SELECT n1 FROM t1 LIMIT 1 INTO @b;
SELECT @a, @b;
SELECT m1, n1 FROM t1 LIMIT 1 INTO @a, @b;
SELECT @a, @b;
Mysql語句結束分隔符
在Mysql客戶端命令交互界面,當我們按下回車鍵,mysql客戶端會檢測輸入內容中是否包含;、\G、\g這三個符號,如果有那麼命令會被髮送到服務器。如果我們想要一次性給服務器發送多個,需要將多條命令寫到一行。比如
mysql> SELECT * FROM t1 LIMIT 1;SELECT * FROM t2 LIMIT 1;SELECT * FROM t3 LIMIT 1;
mysql給我們提供了delimiter命令自定義mysql語句輸入結束符號(比如delimiter EOF )也就是說通過delimiter命令,我們可以將mysql語句寫在多行,直到打出字符EOF 回車後,我們的sql語句纔會被提交給mysql服務器執行。
mysql> delimiter EOF
mysql> SELECT * FROM t1 LIMIT 1;
-> SELECT * FROM t2 LIMIT 1;
-> SELECT * FROM t3 LIMIT 1;
-> EOF
最後在執行delimiter ;切換回常用的分號
存儲函數:
1.一般形式:
CTREATE FUNCTION 存儲函數名稱(參數列表)
RETURNS 返回值類型
BEGIN
函數體內容
END
參數列表跟java不同的是,java類型在前參數名在後,mysql相反參數列表格式比如c
2.查看已定義的存儲函數
SHOW FUNCTION STATUS [LIKE 需要匹配的函數名]
3.查看存儲函數定義
SHOW CREATE FUNCTION 函數名
4.刪除存儲函數
DROP FUNCTION 函數名
5.存儲函數體內的變量定義:
DECLARE 變量名1, 變量名2, ... 數據類型 [DEFAULT 默認值];
比如:DECLARE result INT DEFAULT 0;
6.函數體內的判斷語句
IF 表達式 THEN
處理語句列表
[ELSEIF 表達式 THEN
處理語句列表]
... # 這裏可以有多個ELSEIF語句
[ELSE
處理語句列表]
END IF;
7.1函數體內的循環語句
WHILE 表達式 DO
處理語句列表
END WHILE;
7.2函數體內的循環語句
REPEAT
處理語句列表
UNTIL 表達式 END REPEAT;
7.3函數體內的循環語句
LOOP
處理語句列表
END LOOP;
8.一個簡單的存儲函數
CREATE FUNCTION sum_all(n INT UNSIGNED)
RETURNS INT
BEGIN
DECLARE result INT DEFAULT 0;
DECLARE i INT DEFAULT 1;
REPEAT
SET result = result + i;
SET i = i + 1;
UNTIL i > n END REPEAT;
RETURN result;
END
CREATE FUNCTION sum_all(n INT UNSIGNED)
RETURNS INT
BEGIN
DECLARE result INT DEFAULT 0;
DECLARE i INT DEFAULT 1;
flag:LOOP
IF i > n THEN
LEAVE flag;
END IF;
SET result = result + i;
SET i = i + 1;
END LOOP flag;
RETURN result;
END
8.1調用存儲函數sum_all;
SELECT sum_all(3);
存儲過程
存儲函數一般會執行語句返回一個值,但是存儲過程在於執行一系列sql語句
1一般形式
CREATE PROCEDURE 存儲過程名稱([參數列表])
BEGIN
需要執行的語句
END
2.調用存儲過程
CALL 存儲過程名稱([參數列表])
3.查看已定義的存儲過程
SHOW PROCEDURE STATUS [like 需要匹配的存儲過程名稱]
4.查看存儲過程的定義
SHOW CREATE PROCUDURE 存儲過程名稱
5.刪除存儲過程
DROP PROCEDURE 存儲過程名稱
6.存儲過程也支持像存儲函數變量使用、判斷、循環語句。
7.存儲過程的參數列表支持添加前綴,如下:
[IN | OUT | INOUT] 參數名 數據類型
參數如果默認不加前綴默認爲IN參數
存儲過程與存儲函數的不同點
- 1.存儲函數在定義時候需要顯示用RETURNS語句指定返回的數據類型,而在函數體內必須用RETURN語句顯示指定返回值,存儲過程不需要
- 2.存儲函數只支持IN參數,存儲過程支持IN參數、OUT參數、和INOUT參數
- 3.存儲函數只能返回一個值,而存儲過程可以設置多個OUT參數或者INOUT參數來返回多個結果
- 4.存儲函數執行過程中產生的結果集不會被顯示到客戶端,而存儲過程執行過程中產生的結果集會被顯示到客戶端
- 5.存儲函數直接在表達式中調用,而存儲過程只能通過CALL顯式調用
遊標的使用
1.遊標的意義
遊標的作用就是用於對查詢數據庫所返回的記錄進行遍歷,以便進行相應的操作;遊標既可以用在存儲函數中,也可以用在存儲過程中,還能用在觸發器與事件
2.遊標使用步驟
創建遊標->打開遊標->通過遊標訪問記錄->關閉遊標
3.創建遊標
DECLARE 遊標名稱 CURSOR FOR 查詢語句;
如果存儲程序中也有聲明局部變量的語句,創建遊標的語句一定要放在局部變量聲明後頭。
3.打開關閉遊標
OPEN 遊標名稱;
CLOSE 遊標名稱;
4.使用遊標
FETCH 遊標名 INTO 變量1,變量2...變量n;
5.遍歷時的執行策略
DECLARE CONTINUE HANDLER FOR NOT FOUND 處理語句;
6.完整示例
CREATE PROCEDURE cursor_demo()
BEGIN
DECLARE m_value INT;
DECLARE n_value CHAR(1);
DECLARE not_done INT DEFAULT 1;
DECLARE t1_record_cursor CURSOR FOR SELECT m1, n1 FROM t1;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET not_done = 0;
OPEN t1_record_cursor;
flag: LOOP
FETCH t1_record_cursor INTO m_value, n_value;
IF not_done = 0 THEN
LEAVE flag;
END IF;
SELECT m_value, n_value, not_done;
END LOOP flag;
CLOSE t1_record_cursor;
END
觸發器
我們可以在對錶中記錄做增、刪、改前後讓MySql服務器自動執行一些額外的語句,便可以通過觸發器去實現
1.創建觸發器語句
CREATE TRIGGER 觸發器名
{BEFORE|AFTER}
{INSERT|DELETE|UPDATE}
ON 表名
FOR EACH ROW
BEGIN
觸發器內容
END
ps:由大括號`{}`包裹並且內部用豎線`|`分隔的語句表示必須在給定的選項中選取一個值,比如`{BEFORE|AFTER}`表示必須在`BEFORE`、`AFTER`這兩個之間選取一個。
2.解釋
{INSERT|DELETE|UPDATE}表示具體的語句,MySQL中目前只支持對INSERT、DELETE、UPDATE這三種類型的語句設置觸發器。
FOR EACH ROW BEGIN ... END表示對具體語句影響的每一條記錄都執行我們自定義的觸發器內容:
對於INSERT語句來說,FOR EACH ROW影響的記錄就是我們準備插入的那些新記錄。
對於DELETE語句和UPDATE語句來說,FOR EACH ROW影響的記錄就是符合WHERE條件的那些記錄(如果語句中沒有WHERE條件,那就是代表全部的記錄)。
因爲MySQL服務器會對某條語句影響的所有記錄依次調用我們自定義的觸發器內容,所以針對每一條受影響的記錄,我們需要一種訪問該記錄中的內容的方式,MySQL提供了NEW和OLD兩個單詞來分別代表新記錄和舊記錄,它們在不同語句中的含義不同:
- 對於INSERT語句設置的觸發器來說,NEW代表準備插入的記錄,OLD無效。
- 對於DELETE語句設置的觸發器來說,OLD代表刪除前的記錄,NEW無效。
- 對於UPDATE語句設置的觸發器來說,NEW代表修改後的記錄,OLD代表修改前的記錄。
3.查看觸發器
SHOW TRIGGERS;
4.查看觸發器的具體定義
SHOW CREATE TRIGGER 觸發器名;
5.刪除觸發器
DROP TRIGGER 觸發器名稱;
6.定義一個觸發器
CREATE TRIGGER bi_t1
BEFORE INSERT ON t1
FOR EACH ROW
BEGIN
IF NEW.m1 < 1 THEN
SET NEW.m1 = 1;
ELSEIF NEW.m1 > 10 THEN
SET NEW.m1 = 10;
END IF;
END $
7.觸發器注意事項
7.1觸發器內容中不能有輸出結果集的語句
mysql> delimiter $
mysql> CREATE TRIGGER ai_t1
-> AFTER INSERT ON t1
-> FOR EACH ROW
-> BEGIN
-> SELECT NEW.m1, NEW.n1;
-> END $
ERROR 1415 (0A000): Not allowed to return a result set from a trigger
7.2Update觸發器無法更改舊值
mysql> delimiter $
mysql> CREATE TRIGGER bu_t1
-> BEFORE UPDATE ON t1
-> FOR EACH ROW
-> BEGIN
-> SET OLD.m1 = 1;
-> END $
ERROR 1362 (HY000): Updating of OLD row is not allowed in trigger
7.3AFTER觸發器不能使用SET new.列明=值操作因爲在執行AFTER觸發器的內容時記錄已經被插入完成或者更新完成了。
mysql> delimiter $
mysql> CREATE TRIGGER ai_t1
-> AFTER INSERT ON t1
-> FOR EACH ROW
-> BEGIN
-> SET NEW.m1 = 1;
-> END $
ERROR 1362 (HY000): Updating of NEW row is not allowed in after trigger
事件
有時候我們想讓Mysql服務器在某個時間點或者每隔一段時間自動執行一些語句
1.創建事件語法
CREATE EVENT 事件名
ON SCHEDULE
{
AT 某個確定時間點|
EVERY 期望的時間間隔 [STARTS datetime] [END datetime]
}
DO
BEGIN
具體的語句
END
2.查看事件
SHOW EVENTS;
3.查看具體的事件定義
SHOW CREATE EVENT 事件名;
4.刪除事件
DROP EVENT 事件名;
5.事件使用注意事項
默認情況下,MySQL服務器不會幫助我們執行事件,我們通過以下語句開啓
SET GLOBAL event_scheduler = ON;