MYSQL進階(二):
目錄
一、REDMS術語:
開始學習MySQL 資料庫前,先了解下RDBMS的一些術語:
-
- 資料庫: 資料庫是一些關聯表的集合。.
- 資料表: 表是資料的矩陣。在一個數據庫中的表看起來像一個簡單的電子表格。
- 列: 一列(資料元素) 包含了相同的資料, 例如郵政編碼的資料。
- 行:一行(=元組,或記錄)是一組相關的資料,例如一條使用者訂閱的資料。
- 冗餘:儲存兩倍資料,冗餘可以使系統速度更快。
- 主鍵:主鍵是唯一的。一個數據表中只能包含一個主鍵。你可以使用主鍵來查詢資料。
- 外來鍵:外來鍵用於關聯兩個表。
- 複合鍵:複合鍵(組合鍵)將多個列作為一個索引鍵,一般用於複合索引。
- 索引:使用索引可快速訪問資料庫表中的特定資訊。索引是對資料庫表中一列或多列的值進行排序的一種結構。類似於書籍的目錄。
- 參照完整性: 參照的完整性要求關係中不允許引用不存在的實體。與實體完整性是關係模型必須滿足的完整性約束條件,目的是保證資料的一致性。
二、MYSQL約束
1、約束用於限制加入表的資料型別
常用的幾種約束
- - NOT NULL 不能 為NULL值
- - UNIQUE 唯一值
- - PRIMARY KEY 主鍵約束
- - FOREIGN KEY 外來鍵約束
- - CHECK CHECK約束
- - DEFAULT 預設約束值
2、NOT NULL 約束
NOT NULL 約束強制列不接受 NULL(空) 值。
NOT NULL 約束強制欄位始終包含值。這意味著,如果不向欄位新增值,就無法插入新記錄或者更新記錄。
CREATE TABLE 表名 (欄位名1 型別 NOT NULL)
3、UNIQUE約束
UNIQUE 約束唯一標識資料庫表中的每條記錄。
UNIQUE 和 PRIMARY KEY 約束均為列或列集合提供了唯一性的保證。
PRIMARY KEY 擁有自動定義的 UNIQUE 約束。
請注意,每個表可以有多個 UNIQUE 約束,但是每個表只能有一個 PRIMARY KEY 約束。
CREATE TABLE 表名 (欄位名1 型別 UNIQUE)
4、主鍵約束
PRIMARY KEY 約束唯一標識資料庫表中的每條記錄。
主鍵必須包含唯一的值。
主鍵列不能包含 NULL 值。
每個表都應該有一個主鍵,並且每個表只能有一個主鍵。
主鍵類似身份證號
CREATE TABLE 表名 (欄位名 型別 PRIMARY KEY );
如需要設定主鍵自增長則在PRIMARY KEY AUTO_INCREMENT
CREATE TABLE 表名 (欄位名 型別 PRIMARY KEY AUTO_INCREMENT);
二、使用者管理
建立使用者
create user '使用者名稱'@'IP地址' identified by '密碼';
刪除使用者
drop user '使用者名稱'@'IP地址';
修改使用者
rename user '使用者名稱'@'IP地址'; to '新使用者名稱'@'IP地址';;
修改密碼
set password for '使用者名稱'@'IP地址' = Password('新密碼')
PS:使用者許可權相關資料儲存在mysql資料庫的user表中,所以也可以直接對其進行操作(不建議)
授權管理
show grants for '使用者'@'IP地址' -- 檢視許可權
grant 許可權 on 資料庫.表 to '使用者'@'IP地址' -- 授權
revoke 許可權 on 資料庫.表 from '使用者'@'IP地址' -- 取消許可權
常用許可權:
all privileges 除grant外的所有許可權
select 僅查許可權
select,insert 查和插入許可權
usage 無訪問許可權
對於目標資料庫以及內部其他:
資料庫名.* 資料庫中的所有
資料庫名.表 指定資料庫中的某張表
資料庫名.儲存過程 指定資料庫中的儲存過程
*.* 所有資料庫
對於使用者和IP:
使用者名稱@IP地址 使用者只能在改IP下才能訪問
使用者名稱@192.168.1.% 使用者只能在改IP段下才能訪問(萬用字元%表示任意)
使用者名稱@% 使用者可以再任意IP下訪問(預設IP地址為%)
示例
grant all privileges on db1.tb1 TO '使用者名稱'@'IP'
grant select on db1.* TO '使用者名稱'@'IP'
grant select,insert on *.* TO '使用者名稱'@'IP'
revoke select on db1.tb1 from '使用者名稱'@'IP'
授權區域網內主機遠端連線資料庫
#百分號匹配法
grant all on *.* to 'test'@'192.168.200.%' identified by 'test123';
#子網掩碼配置法
grant all on *.* to 'test'@'192.168.200.0/255.255.255.0' identified by 'test123';
#重新整理許可權
flush privileges;
#遠端登陸連線
mysql -utest -ptest123 -h 192.168.200.96
flush privileges,將資料讀取到記憶體中,從而立即生效。
忘記密碼
# 啟動免授權服務端
mysqld --skip-grant-tables
# 客戶端
mysql -u root -p
# 修改使用者名稱密碼
update mysql.user set authentication_string=password('666') where user='root';
flush privileges;
三、查詢SELECT
A、條件
select * from 表 where id > 1 and name != 'alex' and num = 12; -- 篩選id>1且name不為alex且num為12的資料
select * from 表 where id between 5 and 16; -- 篩選id值在5到16範圍內的資料
select * from 表 where id in (11,22,33) -- 篩選id值為11或22或33的資料
select * from 表 where id not in (11,22,33) -- 反之
select * from 表 where id in (select nid from 表)
B、SQL萬用字元
select * from 表 where name like '%le%' -- 選取name包含有le的所有資料
select * from 表 where name like 'ale_' -- ale開頭的所有(一個字元)
select * from 表 where name regexp "^[awv]"; -- 選取name以'a'、'w'或'v'開始的所有資料
select * from tb where name regexp "^[a-c]"; -- 選取name以a到c開頭範圍內的所有的資料
select * from tb where name regexp "^[^a-c]";-- 選取name非以a到c開頭的所有資料
C、限制
select * from tb limit 2; -- 前2行
select * from tb limit 2,2; -- 從第2行開始的後2行
select * from tb limit 2 offset 2; -- 從第2行開始的後2行
D、排序
select * from tb order by name asc; -- 按照name升序排列
select * from tb order by name desc; -- 按照name降序排列
E、分組
select * from tb group by name; -- 根據名字分組
select * from tb group by 2; -- 根據第2個欄位分組
select * from employee_tbl group by name having id>4; -- 根據名字分組且id大於4的資料
-- where作用於分組前,having作用於分組後且可以用聚合函式,在where中就不行
F、聚合函式(經常作用於分組查詢配合使用)
SUM(欄位) -- 求和
COUNT(欄位) -- 次數統計
AVG(欄位) -- 平均值
MAX(欄位) -- 最大
MIN(欄位) -- 最小
WHERE子句中的條件:
= -- 等於
<> -- 不等於。註釋:在 SQL 的一些版本中,該操作符可被寫成 !=
> -- 大於
< -- 小於
>= -- 大於等於
<= -- 小於等於
BETWEEN-- 在某個範圍內
LIKE -- 搜尋某種模式
IN -- 指定針對某個列的多個可能值
四、外來鍵約束
外來鍵可以理解為一種約束,它有以下限制(FOREIGN KEY為建立外來鍵的關鍵詞)
注意:作為外來鍵一定要和關聯主鍵的資料型別保持一致
- FOREIGN KEY 約束用於預防破壞表之間連線的行為。
- FOREIGN KEY 約束也能防止非法資料插入外來鍵列,因為它必須是它指向的那個表中的值之一。
1、建立外來鍵
-- 1、建立從表的時候就和主表建立外來鍵
CREATE TABLE TABLE_NAME(
'欄位' 型別,
'欄位' 型別,
... ...
FOREIGN KEY (從表字段) REFERENCES 主表(欄位)
);
-- 2、建表完成之後,也可以通過sql語句和主表建立聯絡
ALTER TABLE 從表 ADD CONSTRAINT 外來鍵名稱(形如:FK_從表_主表) FOREIGN KEY (從表字段) REFERENCES 主表(欄位);
2、刪除外來鍵
ALTER TABLE 表名 DROP FOREIGN KEY 外來鍵名稱
例項:
-- 建立班級表 (主表)
CREATE TABLE class(
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(20),
priece INT
);
DESC class;
INSERT INTO class(name,priece) VALUES
("python",15800),
("linux",14800),
("go",16800),
("java",18800);
-- 建立學生表(子表)
CREATE TABLE student (sid INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(20),
age INT,
sex TINYINT(1),
class_id INT,
FOREIGN KEY (class_id) REFERENCES class(id));
(解釋下表資訊中的這些內容
mysql> SHOW CREATE TABLE student;
| student | CREATE TABLE `student` (
`sid` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(20) DEFAULT NULL,
`sex` tinyint(1) DEFAULT NULL,
`class_id` int(11) DEFAULT NULL,
PRIMARY KEY (`sid`), //主鍵
KEY `class_id` (`class_id`),//這裡的class_id是外來鍵欄位
CONSTRAINT `student_ibfk_1` // 這是外來鍵名
FOREIGN KEY (`class_id`) REFERENCES `class` (`id`)// 以哪個表的(欄位) 做為參考,做關聯)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci |
mysql> select * from class;
+----+--------+--------+
| id | name | priece |
+----+--------+--------+
| 1 | python | 88 |
| 2 | linux | 99 |
| 3 | go | 188 |
| 4 | java | 78 |
+----+--------+--------+
mysql> select * from student;
+-----+-------+----------+------+------+
| sid | name | class_id | age | sex |
+-----+-------+----------+------+------+
| 1 | Aaron | 2 | 22 | 0 |
| 2 | Lunti | 2 | 18 | 1 |
| 3 | Pint | 4 | 20 | 1 |
| 4 | tonly | 3 | 20 | 0 |
+-----+-------+----------+------+------+
-- 建表之後新增外來鍵 (abc為指定外來鍵名)
ALTER TABLE student ADD CONSTRAINT abc FOREIGN KEY (class_id) REFERENCES class(id);
SHOW CREATE TABLE student;
-- 這樣繫結的class_id超出class的id則不讓插入
INSERT INTO student(name, age, sex, class_id) VALUES
("tony",17,0,1);
-- 刪除外來鍵
ALTER TABLE student DROP FOREIGN KEY abc;
---- 注:是外來鍵名不是外來鍵欄位
-- 刪除外來鍵之後,則可以插入超出class id範圍的資料
INSERT INTO student(name, age, sex, class_id) VALUES
("tony",17,0,5);
表裡面有相應的一些記錄,也做了外來鍵約束,可以嘗試對主表做修改,看看有什麼不同
主表:被外來鍵所繫結的表
子表:綁定了主鍵的表
嘗試刪除主表的記錄:
mysql> DELETE FROM class where id=2;
ERROR 1451 (23000): Cannot delete or update a parent row: a foreign key constraint fails (`samp_db`.`student`, CONSTRAINT `student_ibfk_1` FOREIGN KEY (`class_id`) REFERENCES `class` (`id`))
提示做了外來鍵約束,不能刪除
如果想要修改,則在字表做級聯操作
3、什麼是級聯(CASCADED)了
cascade 表示級聯操作,就是說,如果主鍵表中被參考欄位更新,外來鍵表中也更新,主鍵表中的記錄被刪除,外來鍵表中改行也相應刪除
語法
ALTER TABLE 子表 ADD FOREIGN KEY(外來鍵) REFERENCES 主表(主鍵)
ON DELETE CASCADE;
例如級聯刪除:
mysql> ALTER TABLE student ADD FOREIGN KEY (class_id) REFERENCES class(id)
-> ON DELETE CASCADE;
4、INOODB下支援的ON語句
--外來鍵約束對子表的含義: 如果在父表中找不到候選鍵,則不允許在子表上進行insert/update
--外來鍵約束對父表的含義: 在父表上進行update/delete以更新或刪除在子表中有一條或多條對
-- 應匹配行的候選鍵時,父表的行為取決於:在定義子表的外來鍵時指定的
-- on update/on delete子句
-----------------innodb支援的四種方式---------------------------------------
-----cascade方式 在父表上update/delete記錄時,同步update/delete掉子表的匹配記錄
-----外來鍵的級聯刪除:如果父表中的記錄被刪除,則子表中對應的記錄自動被刪除--------
FOREIGN KEY (charger_id) REFERENCES ClassCharger(id)
ON DELETE CASCADE
------set null方式 在父表上update/delete記錄時,將子表上匹配記錄的列設為null
-- 要注意子表的外來鍵列不能為not null
FOREIGN KEY (charger_id) REFERENCES ClassCharger(id)
ON DELETE SET NULL
------Restrict方式 :拒絕對父表進行刪除更新操作(瞭解)
------No action方式 在mysql中同Restrict,如果子表中有匹配的記錄,則不允許對父表對應候選鍵
-- 進行update/delete操作(瞭解)
五、多表查詢
新建庫
再新建兩張表
建庫:
mysql> create database company;
進庫:
mysql>use company;
建員工表:
mysql> create table emp(
-> emp_id int auto_increment primary key not null,
-> emp_name varchar(50),
-> age int,
-> dept_id int
-> );
建部門表
mysql> create table dep(
-> dept_id int,
-> dept_name varchar(100)
-> );
往員工表插入記錄
mysql> insert into emp(emp_name,age,dept_id) values
-> ('A',19,200),
-> ('B',26,201),
-> ('C',30,201),
-> ('D',24,202),
-> ('E',20,200),
-> ('F',38,204);
往部門表插入記錄
mysql> insert into dep values
-> (200,'人事部'),
-> (201,'技術部'),
-> (202,'銷售部'),
-> (203,'財政部');
MYSQL常用的幾種連線查詢方法
INNER JOIN 內連線
LEFT JOIN 左連線
RIGHT JOIN 右連線
MYSQL 沒有全連線(FULL JOIN)
內連線查詢
笛卡爾積查詢:
假如一張表示M表,另一張表示N表
查詢的總記錄是:M*N記錄
M的每一條記錄都會匹配N的每一條記錄
笛卡爾積,查詢示例
select * from emp inner join dep; 和 select * from emp,dep查詢結果都一樣
mysql> select * from emp,dep;
+--------+----------+------+---------+---------+-----------+
| emp_id | emp_name | age | dept_id | dept_id | dept_name |
+--------+----------+------+---------+---------+-----------+
| 1 | A | 19 | 200 | 200 | 人事部 |
| 1 | A | 19 | 200 | 201 | 技術部 |
| 1 | A | 19 | 200 | 202 | 銷售部 |
| 1 | A | 19 | 200 | 203 | 財政部 |
| 2 | B | 26 | 201 | 200 | 人事部 |
| 2 | B | 26 | 201 | 201 | 技術部 |
| 2 | B | 26 | 201 | 202 | 銷售部 |
| 2 | B | 26 | 201 | 203 | 財政部 |
| 3 | C | 30 | 201 | 200 | 人事部 |
| 3 | C | 30 | 201 | 201 | 技術部 |
| 3 | C | 30 | 201 | 202 | 銷售部 |
| 3 | C | 30 | 201 | 203 | 財政部 |
| 4 | D | 24 | 202 | 200 | 人事部 |
| 4 | D | 24 | 202 | 201 | 技術部 |
| 4 | D | 24 | 202 | 202 | 銷售部 |
| 4 | D | 24 | 202 | 203 | 財政部 |
| 5 | E | 20 | 200 | 200 | 人事部 |
| 5 | E | 20 | 200 | 201 | 技術部 |
| 5 | E | 20 | 200 | 202 | 銷售部 |
| 5 | E | 20 | 200 | 203 | 財政部 |
| 6 | F | 38 | 204 | 200 | 人事部 |
| 6 | F | 38 | 204 | 201 | 技術部 |
| 6 | F | 38 | 204 | 202 | 銷售部 |
| 6 | F | 38 | 204 | 203 | 財政部 |
+--------+----------+------+---------+---------+-----------+
24 rows in set (0.06 sec)
內連線查詢示例
mysql> select emp.dept_id,emp.emp_name,dep.dept_name from emp inner join dep on emp.dept_id=dep.dept_id;
+---------+----------+-----------+
| dept_id | emp_name | dept_name |
+---------+----------+-----------+
| 200 | A | 人事部 |
| 201 | B | 技術部 |
| 201 | C | 技術部 |
| 202 | D | 銷售部 |
| 200 | E | 人事部 |
+---------+----------+-----------+
5 rows in set (0.00 sec)
左連線查詢
mysql> select emp.dept_id,emp.emp_name,dep.dept_name from emp left join dep on emp.dept_id=dep.dept_id;
+---------+----------+-----------+
| dept_id | emp_name | dept_name |
+---------+----------+-----------+
| 200 | A | 人事部 |
| 200 | E | 人事部 |
| 201 | B | 技術部 |
| 201 | C | 技術部 |
| 202 | D | 銷售部 |
| 204 | F | NULL |
+---------+----------+-----------+
6 rows in set (0.00 sec)
右連線查詢
mysql> select emp.dept_id,emp.emp_name,dep.dept_name from emp right join dep on emp.dept_id=ddep.dept_id;
+---------+----------+-----------+
| dept_id | emp_name | dept_name |
+---------+----------+-----------+
| 200 | A | 人事部 |
| 201 | B | 技術部 |
| 201 | C | 技術部 |
| 202 | D | 銷售部 |
| 200 | E | 人事部 |
| NULL | NULL | 財政部 |
+---------+----------+-----------+
6 rows in set (0.00 sec)
多表查詢之複合條件查詢
查詢員工年齡大於等於25歲的部門
SELECT DISTINCT department.dept_name
FROM employee,department
WHERE employee.dept_id = department.dept_id
AND age>25;
全外連線(基本不會用到):
六、多表查詢之子查詢
- - 子查詢是將一個查詢語句巢狀在另一個查詢語句中。
- - 內層查詢語句的查詢結果,可以為外層查詢語句提供查詢條件。
- - 內層查詢語句的查詢結果,可以為外層查詢語句提供查詢條件。
- - 還可以包含比較運算子:= 、 !=、> 、<等
1. 帶IN關鍵字的子查詢
查詢employee表,但dept_id必須在department表中出現過:
mysql> select * from emp
-> where dept_id IN
-> (select dept_id from dep);
+--------+----------+------+---------+
| emp_id | emp_name | age | dept_id |
+--------+----------+------+---------+
| 1 | A | 19 | 200 |
| 2 | B | 26 | 201 |
| 3 | C | 30 | 201 |
| 4 | D | 24 | 202 |
| 5 | E | 20 | 200 |
+--------+----------+------+---------+
5 rows in set (0.00 sec)
2. 帶比較運算子的子查詢
=、!=、>、>=、<、<=、<>
查詢員工年齡大於等於25歲的部門
mysql> select dept_id,dept_name from dep
-> where dept_id IN
-> (select DISTINCT dept_id from emp where age>=25);
+---------+-----------+
| dept_id | dept_name |
+---------+-----------+
| 201 | 技術部 |
+---------+-----------+
1 row in set (0.00 sec)
3. 帶EXISTS關鍵字的子查詢
EXISTS關字鍵字表示存在。在使用EXISTS關鍵字時,內層查詢語句不返回查詢的記錄。
而是返回一個真假值。Ture或False
當返回Ture時,外層查詢語句將進行查詢;當返回值為False時,外層查詢語句不進行查詢
EXISTS 返回為Ture,進行查詢emp表
mysql> select * from emp
-> WHERE EXISTS
-> (SELECT dept_name from dep where dept_id=203);
+--------+----------+------+---------+
| emp_id | emp_name | age | dept_id |
+--------+----------+------+---------+
| 1 | A | 19 | 200 |
| 2 | B | 26 | 201 |
| 3 | C | 30 | 201 |
| 4 | D | 24 | 202 |
| 5 | E | 20 | 200 |
| 6 | F | 38 | 204 |
+--------+----------+------+---------+
6 rows in set (0.00 sec)
EXISTS 返回為False,進行查詢emp,則查詢結果為空
mysql> select * from emp
-> WHERE EXISTS
-> (SELECT dept_name from dep where dept_id=205);
Empty set (0.00 sec)
複製表的內容
create table emp_bak (select * from emp)
提示:這個複製表的,只能表的欄位 型別和記錄,不能複製表的約束條件(比如:主鍵,外來鍵)
七、索引
1、概述
MySQL索引的建立對於MySQL的高效執行是很重要的,索引可以大大提高MySQL的檢索速度。
雖然索引大大提高了查詢速度,同時卻會降低更新表的速度,如對錶進行INSERT、UPDATE和DELETE。因為更新表時,MySQL不僅要儲存資料,還要儲存一下索引檔案。
建立索引會佔用磁碟空間的索引檔案。
索引特點:建立與維護索引會消耗很多時間與磁碟空間,但查詢速度大大提高!一般索引是針對的大表的,來建立的
2、分類
- 普通索引 (INDEX):僅加速查詢
- 唯一索引(UNIQUE INDEX):加速查詢 + 列值唯一(可以有null)
- 主鍵索引:加速查詢 + 列值唯一 + 表中只有一個(不可以有null)
- 組合索引(SPATIAL INDEX):多列值組成一個索引,專門用於組合搜尋,其效率大於索引合併
- 全文索引(FULLTEXT INDEX):對文字的內容進行分詞,進行搜尋
索引合併:使用多個單列索引組合查詢搜尋
覆蓋索引:select的資料列只用從索引中就能夠取得,不必讀取資料行,換句話說查詢列要被所建的索引覆蓋
建立表時建立索引
語法
CREATE TABLE 表名 (
欄位名1 資料型別 [完整性約束條件…],
欄位名2 資料型別 [完整性約束條件…],
索引型別 索引名 (欄位名),
);
1.建立普通索引
CREATE TABLE emp1 (
id INT,
name VARCHAR(30) ,
resume VARCHAR(50),
INDEX index_emp_name (name),
);
2.建立唯一索引
CREATE TABLE emp2 (
id INT,
name VARCHAR(30) ,
bank_num CHAR(18) UNIQUE ,
resume VARCHAR(50),
UNIQUE INDEX index_emp_name (name)
);
3.建立全文索引
CREATE TABLE emp3 (
id INT,
name VARCHAR(30) ,
resume VARCHAR(50),
FULLTEXT INDEX index_resume (resume)
);
4.建立多列索引示例:
CREATE TABLE emp4 (
id INT,
name VARCHAR(30) ,
resume VARCHAR(50),
INDEX index_name_resume (name, resume)
);
5、對已有表新增索引
第一種語法:
CREATE 索引型別 索引名 ON 表名 (欄位名)
第二種語法:
ALTER TABLE 表名 ADD 索引型別 索引名 (欄位名)
示例:
CREATE INDEX index_emp_name on emp1(name);
ALTER TABLE emp2 ADD UNIQUE INDEX index_bank_num(band_num);
6、刪除索引
語法:
DROP INDEX 索引名 on 表名
DROP INDEX index_emp_name on emp1;
DROP INDEX bank_num on emp2;
小結
在SQL 連線查詢中 on,where,having的區別
1、on、where、having這三個都可以加條件的子句中,on是最先執行,where次之,having最後。有時候如果這先後順序不影響中間結果的話,那最終結果是相同的。但因為on是先把不符合條件的記錄過濾後才進行統計,它就可以減少中間運算要處理的資料,按理說應該速度是最快的。
2、where應該比having快點的,因為它過濾資料後才進行sum,所以having是最慢的。但也不是說having沒用,因為有時在步驟3還沒出來都不知道那個記錄才符合要求時,就要用having了。
3、在兩個表聯接時才用on的,所以在一個表的時候,就剩下where跟having比較了。在這單表查詢統計的情況下,如果要過濾的條件沒有涉及到要計算欄位,那它們的結果是一樣的,只是where可以使用rushmore技術,而having就不能,在速度上後者要慢。
4、如果要涉及到計算的欄位,就表示在沒計算之前,這個欄位的值是不確定的,根據上篇寫的工作流程,where的作用時間是在計算之前就完成的,而having就是在計算後才起作用的,所以在這種情況下,兩者的結果會不同。
5、在多表聯接查詢時,on比where更早起作用。系統首先根據各個表之間的聯接條件,把多個表合成一個臨時表後,再由where進行過濾,然後再計算,計算完後再由having進行過濾。由此可見,要想過濾條件起到正確的作用,首先要明白這個條件應該在什么時候起作用,然後再決定放在那裡
7、查詢的順序如下
FROM ,ON , WHERE, SELECT , GROUP BY , ORDER BY ,HAVING
八、事務
1、概述
MySQL 事務主要用於處理操作量大,複雜度高的資料。比如說,在人員管理系統中,你刪除一個人員,你即需要刪除人員的基本資料,也要刪除和該人員相關的資訊,如信箱,文章等等,這樣,這些資料庫操作語句就構成一個事務,但是一旦有某一個出現錯誤,即可回滾到原來的狀態,從而保證資料庫資料完整性。!
- 在MySQL中只有使用了Innodb資料庫引擎的資料庫或表才支援事務
- 事務處理可以用來維護資料庫的完整性,保證成批的SQL語句要麼全部執行,要麼全部不執行
- 事務用來管理insert,update,delete語句
一般來說,事務是必須滿足4個條件(ACID): Atomicity(原子性)、Consistency(穩定性)、Isolation(隔離性)、Durability(可靠性)
- 1、事務的原子性:一組事務,要麼成功;要麼撤回。
- 2、穩定性 : 有非法資料(外來鍵約束之類),事務撤回。
- 3、隔離性:事務獨立執行。一個事務處理後的結果,影響了其他事務,那麼其他事務會撤回。事務的100%隔離,需要犧牲速度。
- 4、可靠性:軟、硬體崩潰後,InnoDB資料表驅動會利用日誌檔案重構修改。可靠性和高速度不可兼得, innodb_flush_log_at_trx_commit選項 決定什麼時候吧事務儲存到日誌裡。
2、為什麼要有事務
事務廣泛的運用於訂單系統、銀行系統等多種場景。如果有以下一個場景:A使用者和B使用者是銀行的儲戶。現在A要給B轉賬500元。那麼需要做以下幾件事:
1. 檢查A的賬戶餘額>500元;
2. A賬戶扣除500元;
3. B賬戶增加500元;
正常的流程走下來,A賬戶扣了500,B賬戶加了500,皆大歡喜。那如果A賬戶扣了錢之後,系統出故障了呢?A白白損失了500,而B也沒有收到本該屬於他的500。以上的案例中,隱藏著一個前提條件:A扣錢和B加錢,要麼同時成功,要麼同時失敗。事務的需求就在於此。
3、事務的特性
<1> 原子性(Atomicity):原子性是指事務是一個不可分割的工作單位,事務中的操作要麼都發生,要麼都不發生。
<2> 一致性(Consistency):事務前後資料的完整性必須保持一致。在事務執行之前資料庫是符合資料完整性約束的,無論事務是否執行成功,事務結束後的資料庫中的資料也應該是符合完整性約束的。在某一時間點,如果資料庫中的所有記錄都能保證滿足當前資料庫中的所有約束,則可以說當前的資料庫是符合資料完整性約束的。
比如刪部門表前應該刪掉關聯員工(已經建立外來鍵),如果資料庫伺服器發生錯誤,有一個員工沒刪掉,那麼此時員工的部門表已經刪除,那麼就不符合完整性約束了,所以這樣的資料庫也就效能太差啦!
<3>隔離性(Isolation):事務的隔離性是指多個使用者併發訪問資料庫時,一個使用者的事務不能被其它使用者的事務所幹擾,多個併發事務之間資料要相互隔離。
<4>永續性(Durability):永續性是指一個事務一旦被提交,它對資料庫中資料的改變就是永久性的,接下來即使資料庫發生故障也不應該對其有任何影響。
三、隔離性:
將資料庫設計為序列化程的資料庫,讓一張表在同一時間內只能有一個執行緒來操作。如果將資料庫設計為這樣,那資料庫的效率太低了。所以資料庫的設計這沒有直接將資料庫設計為序列化,而是為資料庫提供多個隔離級別選項,使資料庫的使用者可以根據使用情況自己定義到底需要什麼樣的隔離級別。
不考慮隔離性可能出現的問題:
4、事務的命令
事務操作
- 開啟事務 start transaction
- 回滾事務 rollback
- 提交事務 commit
- 保留點 savepoint
開啟事務
start transaction;
回滾事務
回滾事務,即撤銷指定的sql語句(只能回退insert delete update語句),回滾到上一次commit的位置
Rollback;
提交事務
提交事務,提交未儲存的事務
Commit;
保留點
事務處理中設定的臨時佔位符 你可以對它釋出回退(與整個事務回退不同)
savepoint
轉賬例項:
建立一個銀行庫
create database bank;
use bank;
建立使用者表
create table user_info
(id int PRIMARY KEY auto_increment,
name VARCHAR(20),
balance INT);
插入兩條使用者賬戶記錄A和B
INSERT INTO user_info (name,balance) VALUE
("A",1000),
("B",1000);
select * from user_info;
開啟事務
start transaction;
假如沒有提交事務,那麼轉賬完全失敗
UPDATE user_info set balance=balance-500 WHERE name=”A”;
UPDATE user_info set balance=balance+500 WHERE name=”B";
commit;
提交事務則轉賬成功
回滾只是回到commit提交之後的位置
rollback;
事務中所有sql語句執行正確則需要自己手動提交commit;否則有任何一條執行錯誤,需要自己提交一條rollback,這時會回滾所有操作,而不是commit會給你自動判斷和回滾。
mysql解決辦法:設定一個保留點(插入總要的資料記錄)
savepoint 保留點名字;
使用
ROLLBACK TO 保留點名字;
回滾到一個儲存點。使用RELEASE 保留點名字;
刪除一個儲存點,但是保留該儲存點建立後執行的命令的效果。
儲存點只能在一個事務塊裡面建立。在一個事務裡面可以定義多個儲存點。
注:事務中所有sql語句執行正確則需要自己手動提交commit;否則有任何一條執行錯誤,需要自己提交一條rollback,這時會回滾所有操作,而不是commit會給你自動判斷和回滾。
mysql解決辦法:設定一個保留點(插入總要的資料記錄)
儲存點只能在一個事務塊裡面建立。在一個事務裡面可以定義多個儲存點。
delimiter \\ -- 此條可更改SQL語句結束符
create PROCEDURE p1(
OUT p_return_code tinyint
)
BEGIN
DECLARE exit handler for sqlexception
BEGIN
-- ERROR
set p_return_code = 1;
rollback;
END;
DECLARE exit handler for sqlwarning
BEGIN
-- WARNING
set p_return_code = 2;
rollback;
END;
START TRANSACTION;
DELETE from tb1;
insert into tb2(name)values('seven');
COMMIT;
-- SUCCESS
set p_return_code = 0;
END\\
delimiter ;
九、py連線MySQL
pymsql是Python中操作MySQL的模組,其使用方法和MySQLdb幾乎相同
1、下載安裝
pip3 install pymysql
2、操作使用
執行SQL
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import pymysql
# 建立連線
conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='123', db='t1')
# 建立遊標
cursor = conn.cursor()
# 執行SQL,並返回收影響行數
effect_row = cursor.execute("update hosts set host = '1.1.1.2'")
# 執行SQL,並返回受影響行數
#effect_row = cursor.execute("update hosts set host = '1.1.1.2' where nid > %s", (1,))
# 執行SQL,並返回受影響行數
#effect_row = cursor.executemany("insert into hosts(host,color_id)values(%s,%s)", [("1.1.1.11",1),("1.1.1.11",2)])
# 提交,不然無法儲存新建或者修改的資料
conn.commit()
# 關閉遊標
cursor.close()
# 關閉連線
conn.close()
3、獲取查詢資料
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import pymysql
conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='123', db='t1')
cursor = conn.cursor()
cursor.execute("select * from hosts")
# 獲取第一行資料
row_1 = cursor.fetchone()
# 獲取前n行資料 注意遊標是動的
# row_2 = cursor.fetchmany(3)
# 獲取所有資料
# row_3 = cursor.fetchall()
conn.commit()
cursor.close()
conn.close()
注:在fetch資料時按照順序進行,可以使用cursor.scroll(num,mode)來移動遊標位置,如:
- cursor.scroll(1,mode='relative') # 相對當前位置移動
- cursor.scroll(2,mode='absolute') # 相對絕對位置移動
fetch資料型別
關於預設獲取的資料是元祖型別,如果想要或者字典型別的資料,即:
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import pymysql
conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='123', db='t1')
# 遊標設定為字典型別
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
r = cursor.execute("call p1()")
result = cursor.fetchone()
conn.commit()
cursor.close()
conn.close()
十、儲存過程
儲存過程是一個SQL語句集合,類似函式,需要主動呼叫。
1、建立儲存過程
# 無引數儲存過程
# 建立儲存過程
delimiter //
create procedure p1()
BEGIN
select * from t1;
END//
delimiter ;
# 執行儲存過程
call p1()
都說了類似函式,那必須得可以接收引數,且引數有三類:
- in 僅用於傳入引數用
- out 僅用於返回值用
- inout 既可以傳入又可以當作返回值
# 有引數儲存過程
# 建立儲存過程
delimiter \\ # 結尾分號改為\\
create procedure p1(
in i1 int,
in i2 int,
inout i3 int,
out r1 int
)
BEGIN
DECLARE temp1 int; # 建立申明區域性變數
DECLARE temp2 int default 0;
set temp1 = 1;
set r1 = i1 + i2 + temp1 + temp2;
set i3 = i3 + 100;
end\\
delimiter ;
# 執行儲存過程
DECLARE @t1 INT default 3;
DECLARE @t2 INT;
CALL p1 (1, 2 ,@t1, @t2);
SELECT @t1,@t2;
2、刪除儲存過程
drop procedure proc_name;
3、執行儲存過程
執行為函式名加括號;
DECLARE代表建立一個區域性變數
# 無引數
call proc_name()
# 有引數,全in
call proc_name(1,2)
# 有引數,有in,out,inout
DECLARE @t1 INT;
DECLARE @t2 INT default 3;
call proc_name(1,2,@t1,@t2)
pymysql執行儲存過程
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import pymysql
conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='123', db='t1')
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
# 執行儲存過程
cursor.callproc('p1', args=(1, 22, 3, 4))
# 獲取執行完儲存的引數
cursor.execute("select @_p1_0,@_p1_1,@_p1_2,@_p1_3")
result = cursor.fetchall()
conn.commit()
cursor.close()
conn.close()
print(result)