Python學習----第五模塊筆記(網絡編程進階之MySQL)

一、數據庫

數據庫(Database)是按照數據結構來組織、存儲和管理數據的倉庫。它產生於距今六十多年前,隨着信息技術和市場的發展,特別是二十世紀九十年代之後,數據管理再也不僅僅是存儲和管理數據,而轉變成用戶所須要的各類數據管理方式。數據庫有不少種類型,從簡單的存儲有各類數據的表格到可以進行海量數據存儲的大型數據庫系統。python

關係型數據庫系統(Relational Database Management System,RDBMS)是創建在關係模型基礎上的數據庫,藉助於集合代數等數學概念和方法來處理數據庫中的數據。現實世界中的各類實體以及實體之間的各類聯繫均用關係模型來表示。mysql

關係型數據庫有如下特色:sql

  1. 數據以表格的形式出現
  2. 每行爲各類記錄名稱
  3. 每列爲記錄名稱所對應的數據域
  4. 許多的行和列組成一張表單
  5. 若干的表單組成一個數據庫

RDBMS術語:數據庫

  • 數據庫:一些關聯表的集合
  • 數據表:數據的矩陣。在一個數據庫中的表看起來像是一個簡單的電子表格
  • 列:一列(數據元素)包含了相同的數據
  • 行:一行(元組或記錄)是一組相關的數據
  • 冗餘:存儲兩倍數據,冗餘可使系統速度更快。(表的規範化程度越高,表與表之間的關係就越多,查詢時可能常常須要在多個表之間進行鏈接查詢,而進行鏈接操做會下降查詢速度。若是須要常常進行鏈接查詢,將會消耗不少時間,因此能夠在一個表中增長一個冗餘字段,使得在查詢時不用每次都進行鏈接操做)
  • 主鍵:一個數據表中只能包含一個主鍵,可使用主鍵來查詢數據
  • 外鍵:用於關聯兩個表
  • 複合鍵:又稱組合鍵,將多個列做爲一個索引鍵,通常用於複合索引
  • 索引:使用索引能夠快速訪問數據庫表中的特定信息。索引是對數據庫表中一列或多列的值進行排序的一種結構
  • 實體完整性:實體完整性要求每個表中的主鍵字段都不能爲空或者重複的值
  • 參照完整性:參照的完整性要求關係中不容許引用不存在的實體。與實體完整性是關係模型必須知足的完整性約束條件,目的是保證數據的一致性

二、MySQL

MySQL是當前最流行的關係型數據庫,在Web應用方面MySQL是最好的關係型數據庫。MySQL由瑞典MySQL AB公司開發,目前屬於Oracle公司。編程

  • MySQL是開源的,因此你不須要支付額外的費用
  • MySQL支持大型數據庫,能夠處理擁有上千萬條記錄的大型數據庫
  • MySQL使用標準的SQL數據語言形式
  • MySQL能夠運行於多個系統上,而且支持多種語言。這些編程語言包括C、C++、Python、Java、Perl、PHP、Eiffel、Ruby和Tcl等
  • MySQL對PHP有很好的支持,PHP是目前最流行的Web開發語言
  • MySQL支持大型數據庫,支持5000萬條記錄的數據倉庫,32位系統表文件最大可支持4GB,64位系統支持最大的表文件爲8TB
  • MySQL能夠定製,採用了GPL協議,能夠修改源碼來開發本身的MySQL系統

猛戳這裏訪問MySQL官網ubuntu

三、MySQL的安裝和使用

安裝MySQL服務器

Windows可在官網下載安裝包安裝,Linux上可經過yum或apt等安裝包管理工具安裝。session

在ubuntu 16.04下安裝MySQL,經過命令sudo apt-get install mysql-server。經過該命令安裝MySQL時會在安裝過程當中提示設置root用戶的密碼。數據結構

本文中使用MySQL的環境均爲ubuntu server 16.04.3。app

驗證MySQL安裝

在成功安裝MySQL後,一些基礎表會初始化,在服務器啓動後,能夠經過mysqladmin工具來獲取服務器狀態。

fangyu@python:~$ mysqladmin --version
mysqladmin  Ver 8.42 Distrib 5.7.20, for Linux on x86_64

若是以上命令執行後未輸出任何信息,則說明MySQL安裝不成功。

啓動及關閉MySQL

#經過如下命令檢查MySQL服務器是否啓動
ps -ef|grep mysqld

若是MySQL已經啓動,以上命令將輸出MySQL進程列表。

#經過如下命令啓動MySQL服務器
 /etc/init.d/mysql start
#經過如下命令能夠關閉正在運行的MySQL服務器
mysqladmin -u root -p shutdown

登陸MySQL

#經過如下命令能夠登陸MySQL服務器
mysql -u root -p
Enter password:
#輸入密碼便可

在ubuntu 16.04上經過apt安裝MySQL時,在安裝過程當中會提示設置root用戶的密碼,因此這裏須要使用密碼登陸。

登陸成功後會出現MySQL提示符,以下

mysql>

說明以成功鏈接上MySQL服務器,能夠在該命令提示符下執行SQL命令。MySQL的SQL語句以;做爲結束標識。

 

MySQL用戶設置

使用root登陸以後能夠設置新用戶併爲新用戶配置權限。

建立新用戶

create user '用戶名'@'host' identified by '密碼';

#命令詳解:
#host - 指定該用戶在哪一個主機上能夠登陸,若是是本地用戶可使用localhost;若是想讓該用戶能夠從任意主機登陸,可使用通配符%;從指定主機登陸可直接使用IP地址

爲新用戶配置權限

grant 權限 on 數據庫.表名 to '用戶名'@'host';

#命令詳解:
#權限 - 用戶的操做權限,如select、insert、update等,若是要受權全部權限,可使用all
#數據庫.表名 - 受權用戶操做指定的數據庫和表,若是要受權用戶對全部數據庫及表的操做,可使用*.*

#注意:使用上面的命令所受權的用戶沒法給其餘用戶受權,想讓該用戶能夠受權,能夠經過如下命令
grant 權限 on 數據庫.表名 to '用戶名'@'host' with grant option;
#經過如下命令查看用戶的受權信息
show grants for '用戶名'@'host';

更改用戶密碼

set password for '用戶名'@'host'=password('密碼');

#爲當前用戶更改密碼
set password=password('密碼');

#password() - 使用MySQL提供的password函數對密碼進行加密

撤銷用戶權限

revoke 權限 on 數據庫.表名 from '用戶名'@'host';

#注意:撤銷權限時必須於添加權限的內容一致

刪除用戶

drop user '用戶名'@'host';

MySQL中的操做權限

mysql

MySQL管理命令

show databases:列出MySQL的數據庫列表

use 數據庫:選擇要操做的數據庫,使用該命令後全部的MySQL命令都只針對該數據庫

show tables:顯示指定數據庫的全部表,使用該命令前須要使用use命令來選擇要操做的數據庫

show columns from 表名:顯示數據表的屬性,屬性類型,主鍵信息,是否爲NULL,默認值等其餘信息,也可以使用desc 表名

show index from 表名:顯示數據表的詳細索引信息,包括PRIMARY KEY(主鍵)

create database 數據庫名 charset 'utf8':建立一個數據庫,並使其支持中文

drop database 數據庫名:刪除數據庫

四、MySQL數據類型

MySQL中定義數據字段的類型對於數據庫的優化是很是重要的。

MySQL支持多種數據類型,大體能夠分爲三類:數值、日期/時間和字符串(字符)類型。

數值類型

MySQL支持全部標準SQL數值數據類型。

這些類型包括嚴格數值數據類型(INTEGER、SMALLINT、DECIMAL和NUMERIC),以及近似數值數據類型(FLOAT、REAL和DOUBLE PRECISION)。

關鍵字INT是INTEGER的同義詞,關鍵字DEC是DECIMAL的同義詞。

BIT數據類型保存位字段值,而且支持MyISAM、MEMORY、InnoDB和BDB表。

做爲SQL標準的擴展,MySQL也支持整數類型TINYINT、MEDIUMINT和BIGINT。下面的表顯示了須要的每一個整數類型的存儲和範圍。

mysql數值

時間和日期類型

表示時間值的日期和時間類型爲DATETIME、DATE、TIMESTAMP、TIME和YEAR。

每一個時間類型有一個有效值範圍和一個"零"值,當指定不合法的MySQL不能表示的值時使用"零"值。

TIMESTAMP類型有專有的自動更新特性。

mysql時間

字符串類型

字符串類型指CHAR、VARCHAR、BINARY、VARBINARY、BLOB、TEXT、ENUM和SET。

CHAR和VARCHAR類型相似,但它們保存和檢索的方式不一樣。它們的最大長度和是否尾部空格被保留等方面也不一樣。在存儲或檢索過程當中不進行大小寫轉換。

BINARY和VARBINARY類相似於CHAR和VARCHAR,不一樣的是它們包含二進制字符串而不是非二進制字符串。也就是說,它們包含字節字符串而不是字符字符串。這說明它們沒有字符集,而且排序和比較基於列值字節的數值值。

BLOB是一個二進制大對象,能夠容納可變數量的數據。有4種BLOB類型:TINYBLOB、BLOB、MEDIUMBLOB和LONGBLOB。它們只是可容納值的最大長度不一樣。

有4種TEXT類型:TINYTEXT、TEXT、MEDIUMTEXT和LONGTEXT。這些對應4種BLOB類型,有相同的最大長度和存儲需求。

mysql字符串

五、MySQL經常使用命令

建立數據表

create table 表名(字段名,字段數據類型);

實例:在testdb中建立一個student表,有id,name,register_date三個字段,其中id爲主鍵

mysql> create table student(
    -> id int not null auto_increment,
    -> name char(32) not null,
    -> register_date date not null,
    -> primary key(id)
    -> );

#命令詳解:
#not null - 字段不爲null,設置該屬性後在輸入該字段的數據爲null時將會報錯
#auto_increment - 自增,通常用於主鍵,數值會自動加1
#primary key - 定義主鍵,可使用多個字段來定義主鍵,字段間以逗號分隔

插入數據

insert into 表名(字段1,字段2,...,字段n) values (值1,值2,...,值n);

查詢數據

select 字段 from 表名 [where 條件] [offset m] [limit n];

#命令詳解:
#查詢語句中可使用一個或多個表,表之間使用逗號分隔,並使用where語句來設定查詢條件
#select命令能夠讀取一條或多條記錄
#可使用*來代替其餘字段,select語句會返回表的全部字段數據
#可使用where語句來包含任何條件
#能夠經過offset指定select語句開始查詢的數據偏移量,默認狀況下偏移量爲0,需配合limit使用
#可使用limit屬性來設定返回的記錄樹

where子句

如下操做符能夠用於where子句中

假定A爲10,B爲20

where

like子句

select 字段 from 表名 where 條件 like 匹配條件;

#實例
select * from student where name like 'g%';

select * from student where name like binary 'G%';    #只匹配大寫

排序

select 字段 from 表名 order by 字段 [asc or desc];

#命令詳解:
#使用asc或desc關鍵字來設置查詢結果按升序或者降序排序,默認狀況下按升序排序

group by分組

select 字段,函數(自定義名稱) from 表名 [where 條件] group by 字段;

#實例
mysql> select * from student;
+----+--------+---------------+
| id | name   | register_date |
+----+--------+---------------+
|  1 | gougou | 2017-12-22    |
|  2 | maomao | 2017-12-25    |
|  3 | guigui | 2017-12-25    |
|  4 | niuniu | 2017-12-29    |
|  5 | tutu   | 2017-12-30    |
+----+--------+---------------+
5 rows in set (0.00 sec)

#統計每一個名字出現的次數並分組
mysql> select name,count('計數') from student group by name;
+--------+-----------------+
| name   | count('計數')   |
+--------+-----------------+
| gougou |               1 |
| guigui |               1 |
| maomao |               1 |
| niuniu |               1 |
| tutu   |               1 |
+--------+-----------------+

#使用with rollup
mysql> select name,count('c') as '計數' from student group by name with rollup;
+--------+--------+
| name   | 計數   |
+--------+--------+
| gougou |      1 |
| guigui |      1 |
| maomao |      1 |
| niuniu |      1 |
| tutu   |      1 |
| NULL   |      5 |
+--------+--------+

#可使用 coalesce 來設置一個能夠取代 NUll 的名稱
mysql> select coalesce(name,'總計數'),count('計數') from student group by name with rollup;
+----------------------------+-----------------+
| coalesce(name,'總計數')    | count('計數')   |
+----------------------------+-----------------+
| gougou                     |               1 |
| guigui                     |               1 |
| maomao                     |               1 |
| niuniu                     |               1 |
| tutu                       |               1 |
| 總計數                         |               5 |
+----------------------------+-----------------+

修改數據

update 表名 set 字段1=值1,字段2=值2 [where 條件];

刪除數據

delete from 表名 [where 條件];

修改表名及字段

#添加字段
alter table 表名 add 字段;
#實例:alter table student add phone int not null;

#刪除字段
alter table 表名 drop 字段;
#實例:alter table student drop phone;


#修改字段類型及名稱

#使用modify子句
alter table 表名 modify 字段 修改後的數據類型;
#實例: alter table student modify name char(48) not null;

#使用change子句
alter table 表名 change 字段名 修改後的字段名 修改後的數據類型;
#實例1:alter table student change name name char(32) not null;    #不修改字段名
#實例2:alter table student change name stu_name char(48) not null;    #修改字段名

#修改字段時添加是否null值及是否設置默認值
alter table 表名 modify 字段 數據類型 not null default 默認值;


#修改表名
alter table 表名 rename to 修改後的表名;

外鍵關聯

外鍵是一個特殊的索引,用於關聯兩個表,用於維護數據的完整性。

#實例
mysql> create table student(
    -> id int not null auto_increment,
    -> name char(32) not null,
    -> class_id int not null,
    -> primary key(id),
    -> foreign key(class_id) references class(id)    #設置外鍵
    -> );

外鍵約束

mysql> insert into student (name,class_id) values('wangwang',3);
ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint fails (`testdb`.`student`, CONSTRAINT `student_ibfk_1` FOREIGN KEY (`class_id`) REFERENCES `class` (`id`))

#插入數據時,當插入的class_id爲class表中不存在的id時,報錯
mysql> delete from class where id=1;
ERROR 1451 (23000): Cannot delete or update a parent row: a foreign key constraint fails (`testdb`.`student`, CONSTRAINT `student_ibfk_1` FOREIGN KEY (`class_id`) REFERENCES `class` (`id`))

#一個表的字段被其餘表關聯後,沒法刪除

MySQL null值處理

使用selecet 命令及where子句進行查詢時,當提供的查詢條件字段爲null時,該命令可能沒法正常工做。爲處理這種狀況,MySQL提供了三大運算符:

is null:當列的值是null時,此運算符返回true

is not null:當列的值不爲null時,此運算符返回true

<=>:比較操做符(不一樣於=運算符),當比較的兩個值爲null時返回true

關於 NULL 的條件比較運算是比較特殊的。你不能使用 = NULL 或 != NULL 在列中查找 NULL 值 。
在MySQL中,NULL值與任何其它值的比較(即便是NULL)永遠返回false,即 NULL = NULL 返回false 。
MySQL中處理NULL使用IS NULL和IS NOT NULL運算符。

鏈接查詢

MySQL的join能夠在兩個或多個表中查詢數據。在select、update和delete語句中使用join來聯合多表查詢。

join按照功能大體分爲如下三類:

inner join(內鏈接,或等值鏈接):獲取兩個表中字段匹配關係的記錄。

left join(左鏈接):獲取左表全部記錄,即便右表沒有對應的匹配記錄。

right join(右鏈接):與left join相反,用於獲取右表全部記錄,即便左表沒有對應匹配的記錄。

實例:

有如下兩張表

#class表
+----+----------+
| id | class    |
+----+----------+
|  1 | animal-1 |
|  2 | animal-2 |
|  3 | animal-3 |
+----+----------+

#student表
+----+--------+----------+
| id | name   | class_id |
+----+--------+----------+
|  2 | gougou |        1 |
|  3 | maomao |        2 |
|  4 | tutu   |        1 |
+----+--------+----------+

inner join(內鏈接)

mysql> select * from class inner join student on class.id=student.class_id;
+----+----------+----+--------+----------+
| id | class    | id | name   | class_id |
+----+----------+----+--------+----------+
|  1 | animal-1 |  2 | gougou |        1 |
|  2 | animal-2 |  3 | maomao |        2 |
|  1 | animal-1 |  4 | tutu   |        1 |
+----+----------+----+--------+----------+

#另外一種寫法
mysql> select class.*,student.* from class,student where class.id=student.class_id;
+----+----------+----+--------+----------+
| id | class    | id | name   | class_id |
+----+----------+----+--------+----------+
|  1 | animal-1 |  2 | gougou |        1 |
|  2 | animal-2 |  3 | maomao |        2 |
|  1 | animal-1 |  4 | tutu   |        1 |
+----+----------+----+--------+----------+


#配合where子句
mysql> select * from class inner join student on class.id=student.class_id where class_id=1;
+----+----------+----+--------+----------+
| id | class    | id | name   | class_id |
+----+----------+----+--------+----------+
|  1 | animal-1 |  2 | gougou |        1 |
|  1 | animal-1 |  4 | tutu   |        1 |
+----+----------+----+--------+----------+

mysql> select class.*,student.* from class,student where class.id=student.class_id and class_id=1;
+----+----------+----+--------+----------+
| id | class    | id | name   | class_id |
+----+----------+----+--------+----------+
|  1 | animal-1 |  2 | gougou |        1 |
|  1 | animal-1 |  4 | tutu   |        1 |
+----+----------+----+--------+----------+

left join(左鏈接)

mysql> select * from class left join student on class.id=student.class_id;
+----+----------+------+--------+----------+
| id | class    | id   | name   | class_id |
+----+----------+------+--------+----------+
|  1 | animal-1 |    2 | gougou |        1 |
|  2 | animal-2 |    3 | maomao |        2 |
|  1 | animal-1 |    4 | tutu   |        1 |
|  3 | animal-3 | NULL | NULL   |     NULL |
+----+----------+------+--------+----------+

right join(右鏈接)

mysql> select * from class right join student on class.id=student.class_id;
+------+----------+----+--------+----------+
| id   | class    | id | name   | class_id |
+------+----------+----+--------+----------+
|    1 | animal-1 |  2 | gougou |        1 |
|    1 | animal-1 |  4 | tutu   |        1 |
|    2 | animal-2 |  3 | maomao |        2 |
+------+----------+----+--------+----------+

full join,MySQL不支持full join,可使用另外一種方法實現

mysql> select * from class left join student on class.id=student.class_id union 
    -> select * from class right join student on class.id=student.class_id;
+------+----------+------+--------+----------+
| id   | class    | id   | name   | class_id |
+------+----------+------+--------+----------+
|    1 | animal-1 |    2 | gougou |        1 |
|    2 | animal-2 |    3 | maomao |        2 |
|    1 | animal-1 |    4 | tutu   |        1 |
|    3 | animal-3 | NULL | NULL   |     NULL |
+------+----------+------+--------+----------+

事務

MySQL 事務主要用於處理操做量大,複雜度高的數據。好比說,在人員管理系統中,你刪除一我的員,你即須要刪除人員的基本資料,也要刪除和該人員相關的信息,如信箱,文章等等,這樣,這些數據庫操做語句就構成一個事務!

  • 在MySQL中只有使用了Innodb數據庫引擎的數據庫或表才支持事務
  • 事務處理能夠用來維護數據庫的完整性,保證成批的SQL語句要麼所有執行,要麼所有不執行
  • 事務用來管理insert,update,delete語句

通常來講,事務是必須知足4個條件(ACID): Atomicity(原子性)、Consistency(穩定性)、Isolation(隔離性)、Durability(可靠性)

  • 事務的原子性:一組事務,要麼成功;要麼撤回。
  • 穩定性 : 有非法數據(外鍵約束之類),事務撤回。
  • 隔離性:事務獨立運行。一個事務處理後的結果,影響了其餘事務,那麼其餘事務會撤回。事務的100%隔離,須要犧牲速度。
  • 可靠性:軟、硬件崩潰後,InnoDB數據表驅動會利用日誌文件重構修改。可靠性和高速度不可兼得, innodb_flush_log_at_trx_commit選項決定何時把事務保存到日誌裏。

使用begin開始一個事務;使用rollback進行回滾,這樣數據將不會寫入數據庫;使用commit進行提交,寫入數據庫

索引

MySQL索引的創建對於MySQL的高效運行是很重要的,索引能夠大大提升MySQL的檢索速度。

索引分單列索引和組合索引。單列索引,即一個索引只包含單個列,一個表能夠有多個單列索引,但這不是組合索引。組合索引,即一個索引包含多個列。

建立索引時,你須要確保該索引是應用在 SQL 查詢語句的條件(通常做爲 WHERE 子句的條件)。 

實際上,索引也是一張表,該表保存了主鍵與索引字段,並指向實體表的記錄。

索引的缺點:雖然索引大大提升了查詢速度,同時卻會下降更新表的速度,如對錶進行INSERT、UPDATE和DELETE。由於更新表時,MySQL不只要保存數據,還要保存一下索引文件。創建索引會佔用磁盤空間的索引文件。

建立普通索引

普通索引是最基本的索引,沒有任何限制。有如下幾種方法建立:

CREATE INDEX indexName ON mytable(username(length));
#若是是CHAR,VARCHAR類型,length能夠小於字段實際長度;若是是BLOB和TEXT類型,必須指定 length。

ALTER mytable ADD INDEX [indexName] ON (username(length));

#建立表時直接建立
CREATE TABLE mytable( 
ID INT NOT NULL,  
username VARCHAR(16) NOT NULL, 
INDEX [indexName] (username(length)) 
);
#刪除索引
DROP INDEX [indexName] ON mytable;

建立惟一索引

與普通索引不一樣,索引列的值必須惟一,但容許有空值。若是是組合索引,則列值的組合必須惟一。有如下幾種方法建立:

CREATE UNIQUE INDEX indexName ON mytable(username(length));

ALTER mytable ADD UNIQUE [indexName] ON (username(length));

#建立表的時候直接指定
CREATE TABLE mytable( 
ID INT NOT NULL,  
username VARCHAR(16) NOT NULL, 
UNIQUE [indexName] (username(length)) 
);

使用alter命令添加和刪除索引

ALTER TABLE tbl_name ADD PRIMARY KEY (column_list): 該語句添加一個主鍵,這意味着索引值必須是惟一的,且不能爲NULL。
ALTER TABLE tbl_name ADD UNIQUE index_name (column_list): 這條語句建立索引的值必須是惟一的(除了NULL外,NULL可能會出現屢次)。
ALTER TABLE tbl_name ADD INDEX index_name (column_list): 添加普通索引,索引值可出現屢次。
ALTER TABLE tbl_name ADD FULLTEXT index_name (column_list):該語句指定了索引爲 FULLTEXT ,用於全文索引。
ALTER TABLE tbl_name DROP INDEX (index_name); 刪除索引

使用alter命令添加和刪除主鍵

主鍵只能做用於一個列上,添加主鍵索引時,你須要確保該主鍵默認不爲空(NOT NULL)。實例以下:
mysql> ALTER TABLE testalter_tbl MODIFY i INT NOT NULL;
mysql> ALTER TABLE testalter_tbl ADD PRIMARY KEY (i);
 
你也可使用 ALTER 命令刪除主鍵:
mysql> ALTER TABLE testalter_tbl DROP PRIMARY KEY;
刪除指定時只需指定PRIMARY KEY,但在刪除索引時,你必須知道索引名。

顯示索引信息

mysql> SHOW INDEX FROM table_name\G

六、Python使用pymysql操做MySQL

pymysql爲第三方模塊,可使用pip install pymysql安裝。使用該模塊能夠在Python中鏈接MySQL並執行原生SLQ命令。

pymysql使用實例

MySQL中存在testdb數據庫,數據庫中有表student,表的具體信息以下

+----+--------+----------+
| id | name   | class_id |
+----+--------+----------+
|  2 | gougou |        1 |
|  3 | maomao |        2 |
|  4 | tutu   |        1 |
|  6 | guigui |        2 |
+----+--------+----------+

查詢數據

import pymysql

# 建立鏈接
conn = pymysql.connect(host="IP", port=3306, user="用戶名", password="密碼", db="testdb")

# 建立遊標,相似登陸MySQL後命令提示符的光標
cursor = conn.cursor()

# 執行SQL,並返回受影響行數
effect_line = cursor.execute("select * from student")
# 讀取第一行數據
res1 = cursor.fetchone()
# 讀取前n行數據
res2 = cursor.fetchmany(2)
# 讀取所有數據
res3 = cursor.fetchall()

# 相似MySQL事務,提交後才執行
conn.commit()

# 關閉遊標和鏈接
cursor.close()
conn.close()

print("%s條記錄受影響" % effect_line)
print(res1)
print(res2)
print(res3)

#結果
4條記錄受影響
(2, 'gougou', 1)
((3, 'maomao', 2), (4, 'tutu', 1))    # 注意:前面已讀取一行,這裏就從第二行開始
((6, 'guigui', 2),)    # 注意:前面的數據已讀取,這裏只讀取了最後一行

注:在fetch數據時按照順序進行,可使用cursor.scroll(num,mode)來移動遊標位置,如:

  • cursor.scroll(1,mode='relative')  # 相對當前位置移動
  • cursor.scroll(2,mode='absolute') # 相對絕對位置移動

添加和修改數據

import pymysql

conn = pymysql.connect(host="IP", port=3306, user="用戶名", password="密碼", db="testdb")
cursor = conn.cursor()

effect_line1 = cursor.execute("update student set name='wangwang' where id=%s", (2,))

# 一次執行多條
effect_line2 = cursor.executemany("insert into student (name,class_id) values (%s, %s)", [("niuniu", 1), ("yangyang", 2)])

conn.commit()

cursor.close()
conn.close()

print("%s條記錄受影響" % effect_line1)
print("%s條記錄受影響" % effect_line2)

#結果
1條記錄受影響
2條記錄受影響

#執行後的student表
+----+----------+----------+
| id | name     | class_id |
+----+----------+----------+
|  2 | wangwang |        1 |
|  3 | maomao   |        2 |
|  4 | tutu     |        1 |
|  6 | guigui   |        2 |
|  7 | niuniu   |        1 |
|  8 | yangyang |        2 |
+----+----------+----------+

獲取自增的新ID

# Life is short,you need Python!

import pymysql

conn = pymysql.connect(host="IP", port=3306, user="用戶名", password="密碼", db="testdb")
cursor = conn.cursor()

cursor.execute("insert into student (name, class_id) values ('yaya', 2)")

conn.commit()

cursor.close()
conn.close()

# 獲取自增的新ID
new_id = cursor.lastrowid
print(new_id)

#結果
9

以字典形式返回數據

import pymysql

conn = pymysql.connect(host="IP", port=3306, user="用戶名", password="密碼", db="testdb")

# 遊標設置爲字典類型
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)

cursor.execute("select * from student")

res = cursor.fetchone()

conn.commit()

cursor.close()
conn.close()

print(res)

#結果
{'id': 2, 'name': 'wangwang', 'class_id': 1}

七、ORM — sqlalchemy使用

什麼是ORM

ORM英文全稱爲object relational mapping,對象映射關係程序,對於面向對象的編程語言來講一切皆對象,但數據庫倒是關係型的,爲了保證一致的使用習慣,經過ORM將編程語言的對象模型和數據庫的關係模型創建映射關係,這樣在使用編程語言對數據庫進行操做的時候能夠直接使用編程語言的對象模型,而不用直接使用SQL語言。

orm

ORM的優勢:

  1. 隱藏了數據訪問細節,「封閉」了通用數據庫交互,使得咱們與通用數據庫交互變得簡單易行,而且徹底不用考慮SQL語言。快速開發由此而來。
  2. ORM使咱們構造固化數據結構變得簡單易行。

ORM的缺點:

無可避免的,自動化意味着映射和關聯管理,代價是犧牲性能(在早期這是全部不喜歡ORM人的共同點)。如今的各類ORM框架都在嘗試使用各類方法來減輕這塊,效果仍是很顯著的。

SQLAlchemy安裝

在Python中,最有名的ORM框架是SQLAlchemy。它是一個第三方模塊,可使用pip install sqlalchemy安裝。

SQLAlchemy基本使用

建立一個表

from sqlalchemy import create_engine
from sqlalchemy import Column, Integer, CHAR
from sqlalchemy.ext.declarative import declarative_base

# 鏈接數據庫
engine = create_engine("mysql+pymysql://用戶名:密碼@IP/testdb", encoding="utf-8", echo=True)

Base = declarative_base()    # 生成ORM基類


class Student(Base):
    __tablename__ = "student"    # 表名
    id = Column(Integer, primary_key=True)
    name = Column(CHAR(32), nullable=False)  # 建立變長字符串使用String,nullable=False爲該字段不能爲空

Base.metadata.create_all(engine)    # 建立表結構

另外一種方法建立一個表

from sqlalchemy import Table, MetaData, Column, Integer, CHAR
from sqlalchemy.orm import mapper

metadata = MetaData()

student = Table("student", metadata,
                Column("id", Integer, primary_key=True),
                Column("name", CHAR(32), nullable=False))


class Student(object):
    def __init__(self, name):
        self.name = name

mapper(Student, student)

事實上,第一種建立方法就是第二種方法的再封裝。

SQLAlchemy支持中文

engine = create_engine("mysql+pymysql://用戶名:密碼@IP/testdb?charset=utf8", encoding="utf-8")

插入記錄到表

from sqlalchemy.orm import sessionmaker

SessionClass = sessionmaker(bind=engine)    # 建立與數據庫的會話,這裏返回的是一個class
Session = SessionClass()    # 生成session的實例

obj1 = Student(name="gougou")    # 須要插入的記錄
obj2 = Student(name="maomao")

Session.add(obj1)    # 將記錄插入數據庫,注意,這裏尚未真正寫到數據庫中
Session.add(obj2)

Session.commit()    # 統一提交給數據庫

此時在MySQL中查詢student表,結果以下:

+----+--------+
| id | name   |
+----+--------+
|  1 | gougou |
|  2 | maomao |
+----+--------+

SQLAlchemy查詢、修改、刪除記錄

data = Session.query(Student).filter_by(name="gougou").first()    # 查詢第一條記錄
print(data)
#結果
<__main__.Student object at 0x00000196737E7C50>
#返回的數據映射成一個對象,直接打印即爲該對象的內存地址

#能夠像調用對象的屬性同樣調用字段
data = Session.query(Student).filter_by(name="gougou").first()    # 查詢第一條記錄
print("查詢出的記錄ID:%s,記錄內容:%s" % (data.id, data.name))
#結果
查詢出的記錄ID:1,記錄內容:gougou

爲了是查詢結果變得更加可讀,可以使用下面的方法:

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy import Column, Integer, CHAR
from sqlalchemy.ext.declarative import declarative_base

engine = create_engine("mysql+pymysql://用戶名:密碼@IP/testdb", encoding="utf-8")
Base = declarative_base()


class Student(Base):
    __tablename__ = "student"
    id = Column(Integer, primary_key=True)
    name = Column(CHAR(32), nullable=False)

    def __repr__(self):
        return "ID:%s,name:%s" % (self.id, self.name)

SessionClass = sessionmaker(bind=engine)
Session = SessionClass()

data = Session.query(Student).filter_by(name="gougou").first()    # 查詢第一條記錄
print(data)

#結果
ID:1,name:gougou

查詢全部記錄

data = Session.query(Student.id, Student.name).all()
print(data)

#結果
[(1, 'guigui'), (2, 'maomao'), (3, 'niuniu'), (4, 'gougou'), (5, 'tutu')]

多條件查詢

data = Session.query(Student).filter(Student.id > 1).filter(Student.id < 5).all()
print(data)

#結果
[ID:2 NAME:maomao, ID:3 NAME:niuniu, ID:4 NAME:gougou]

經常使用查詢語法

等於

data = Session.query(Student).filter(Student.name == "niuniu").all()
print(data)

#結果
[ID:5 NAME:niuniu]

不等於

data = Session.query(Student).filter(Student.name != "niuniu").all()
print(data)

#結果
[ID:1 NAME:gougou, ID:2 NAME:maomao, ID:3 NAME:guigui, ID:4 NAME:tutu]

like

data = Session.query(Student).filter(Student.name.like("%i%")).all()
print(data)

#結果
[ID:3 NAME:guigui, ID:5 NAME:niuniu]

in、not in

#in
data = Session.query(Student).filter(Student.name.in_(["a", "niuniu"])).all()
print(data)
#結果
[ID:5 NAME:niuniu]


#not in
data = Session.query(Student).filter(~Student.name.in_(["gougou", "maomao", "niuniu"])).all()
print(data)
#結果
[ID:3 NAME:guigui, ID:4 NAME:tutu]

null,not null

#null
data = Session.query(Student).filter(Student.name.is_(None)).all()
print(data)
#結果
[]


#not null
data = Session.query(Student).filter(Student.name.isnot(None)).all()
print(data)
#結果
[ID:1 NAME:gougou, ID:2 NAME:maomao, ID:3 NAME:guigui, ID:4 NAME:tutu, ID:5 NAME:niuniu]

and

from sqlalchemy import and_

data = Session.query(Student).filter(and_(Student.id == 1, Student.name == "gougou")).all()
print(data)


#另外一種寫法
data = Session.query(Student).filter(Student.id == 1).filter(Student.name == "gougou").all()
print(data)

or

data = Session.query(Student).filter(or_(Student.name == "niuniu", Student.name == "gougou")).all()
print(data)

filter與filter_by的區別

data = Session.query(Student).filter_by(name="gougou").all()

data = Session.query(Student).filter(Student.name == "gougou").all()

修改

data = Session.query(Student).filter_by(name="gougou").first()    # 查詢出該條記錄
data.name = "guigui"    # 修改該記錄的字段的值
Session.commit()

刪除

data = Session.query(Student).filter_by(id=5).first()
Session.delete(data)
Session.commit()

回滾

Session.rollback()

統計

data = Session.query(Student).filter(Student.name.like("%i%")).count()
print(data)

分組

from sqlalchemy import func

data = Session.query(func.count(Student.name), Student.name).group_by(Student.name).all()
print(data)

#結果
[(1, 'gougou'), (1, 'guigui'), (1, 'maomao'), (1, 'niuniu')]

SQLAlchemy外鍵關聯

建立classes和student兩個表,其中student表的class_id字段關聯class表中的id字段。

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker, relationship
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, CHAR, ForeignKey

engine = create_engine("mysql+pymysql://用戶名:密碼@IP/testdb", encoding="utf-8")
Base = declarative_base()


class Classes(Base):
    __tablename__ = "classes"
    id = Column(Integer, primary_key=True)
    name = Column(CHAR(32), nullable=False)

    def __repr__(self):
        return "Class:%s" % self.name


class Student(Base):
    __tablename__ = "student"
    id = Column(Integer, primary_key=True)
    name = Column(CHAR(32), nullable=False)
    class_id = Column(Integer, ForeignKey("classes.id"), nullable=False)

    # 在class表中經過backref字段反向查詢出全部在student表中的關聯項
    student = relationship("Classes", backref="students")

    def __repr__(self):
        return "ID:%s NAME:%s" % (self.id, self.name)

Base.metadata.create_all(engine)

SessionClass = sessionmaker(bind=engine)
Session = SessionClass()

classes = ["animal1", "animal2"]
for i in classes:
    Session.add(Classes(name=i))

students = {"gougou": 1, "maomao": 1, "guigui": 1, "niuniu": 2, "tutu": 2}
for j in students:
    Session.add(Student(name=j, class_id=students[j]))

Session.commit()

經過class查詢相應的student記錄

data = Session.query(Classes).all()
for i in data:
    print(i, i.students)
data = Session.query(Classes).filter_by(id=1).all()
for i in data:
    print(i.students)

經過student查詢class記錄

data = Session.query(Student).all()
for i in data:
    print(i, i.student.name)

多外鍵關聯

建立classes和student表,其中student表中的class_id字段與class_name字段關聯classes表中的id字段

class Classes(Base):
    __tablename__ = "classes"
    id = Column(Integer, primary_key=True)
    name = Column(CHAR(32), nullable=False)


class Student(Base):
    __tablename__ = "student"
    id = Column(Integer, primary_key=True)
    name = Column(CHAR(32), nullable=False)
    class_id = Column(Integer, ForeignKey("classes.id"), nullable=False)
    class_name = Column(Integer, ForeignKey("classes.id"), nullable=False)

    students = relationship("Classes", foreign_keys=[class_id])
    team = relationship("Classes", foreign_keys=[class_name])

SQLAlchemy多對多關係

實例

設計一個能描述圖書與做者關係的表結構,需求以下:

  1. 一本書能夠有好幾個做者
  2. 一個做者能夠寫好幾本書

此時能夠經過中間表來完成書與做者之間的多對多關聯

建立book,author,book_to_author三張表

from sqlalchemy import create_engine
from sqlalchemy.orm import relationship, sessionmaker
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Table, Column, Integer, CHAR, ForeignKey

engine = create_engine("mysql+pymysql://用戶名:密碼@IP/testdb?charset=utf8", encoding="utf-8")
Base = declarative_base()

book_to_author = Table("book_to_author", Base.metadata,
                       Column("book_id", Integer, ForeignKey("book.id")),
                       Column("author_id", Integer, ForeignKey("author.id")))


class Book(Base):
    __tablename__ = "book"
    id = Column(Integer, primary_key=True)
    name = Column(CHAR(64), nullable=False)

    authors = relationship("Author", secondary=book_to_author, backref="books")

    def __repr__(self):
        return "Book Name:%s" % self.name


class Author(Base):
    __tablename__ = "author"
    id = Column(Integer, primary_key=True)
    name = Column(CHAR(32), nullable=False)

    def __repr__(self):
        return "Author Name:%s" % self.name

Base.metadata.create_all(engine)

SessionClass = sessionmaker(bind=engine)
Session = SessionClass()

book1 = Book(name="Python核心編程")
book2 = Book(name="Linux內核")

a1 = Author(name="aaa")
a2 = Author(name="bbb")
a3 = Author(name="ccc")
a4 = Author(name="ddd")
a5 = Author(name="eee")

book1.authors = [a1, a2, a3]
book2.authors = [a4, a5]

Session.add_all([book1, book2, a1, a2, a3, a4, a5])
Session.commit()

MySQL中建立如下三張表。

#author
+----+------+
| id | name |
+----+------+
|  1 | aaa  |
|  2 | bbb  |
|  3 | ccc  |
|  4 | ddd  |
|  5 | eee  |
+----+------+

#book
+----+--------------------+
| id | name               |
+----+--------------------+
|  1 | Python核心編程     |
|  2 | Linux內核          |
+----+--------------------+

#book_to_author
+---------+-----------+
| book_id | author_id |
+---------+-----------+
|       1 |         1 |
|       1 |         2 |
|       1 |         3 |
|       2 |         4 |
|       2 |         5 |
+---------+-----------+
#book_to_author爲ORM自動建立自動維護

查詢

print("經過書名查做者".center(50, "*"))
authors = Session.query(Book).filter(Book.name == "Python核心編程").all()
for i in authors:
    print(i, i.authors, "\n")

print("經過做者查書名".center(50, "*"))
books = Session.query(Author).filter(Author.name == "aaa").all()
for j in books:
    print(j, j.books)

#結果
*********************經過書名查做者**********************
Book Name:Python核心編程 [Author Name:aaa, Author Name:bbb, Author Name:ccc] 

*********************經過做者查書名**********************
Author Name:aaa [Book Name:Python核心編程]

刪除

author = Session.query(Author).filter(Author.name == "aaa").all()
Session.delete(author[0])
Session.commit()
#此時ORM自動從book_to_author中刪除對應的關聯關係
相關文章
相關標籤/搜索