一、MySQL
1、概述
什么是數據庫 ?
答:數據的倉庫,和Excel表中的行和列是差不多的,只是有各種約束和不同數據類型的表格
什么是 MySQL、Oracle、SQLite、Access、MS SQL Server等 ?
答:他們均是一個軟件,都有兩個主要的功能:
- a. 將數據保存到文件或內存
- b. 接收特定的命令,然后對文件進行相應的操作
PS:如果有了以上軟件,無須自己再去創建文件和文件夾,而是直接傳遞 命令 給上述軟件,讓其來進行文件操作,他們統稱為數據庫管理系統(DBMS,Database Management System)
什么是SQL ?
答:上述提到MySQL等軟件可以接受命令,並做出相應的操作,由於命令中可以包含刪除文件、獲取文件內容等眾多操作,對於編寫的命令就是是SQL語句。SQL語句是結構化語言(Structured Query Language)的縮寫,SQL是一種專門用來與數據庫通信的語言。
2、下載安裝
MySQL是一個關系型數據庫管理系統,由瑞典MySQL AB 公司開發,目前屬於 Oracle 旗下公司。MySQL 最流行的關系型數據庫管理系統,在 WEB 應用方面MySQL是最好的 RDBMS (Relational Database Management System,關系數據庫管理系統) 應用軟件之一。
想要使用MySQL來存儲並操作數據,則需要做幾件事情:
a. 安裝MySQL服務端
b. 安裝MySQL客戶端
b. 【客戶端】連接【服務端】
c. 【客戶端】發送命令給【服務端MySQL】服務的接受命令並執行相應操作(增刪改查等)
下載 http://dev.mysql.com/downloads/mysql/ 安裝 windows: 點點點 Linux: yum install mysql-server Mac: 點點點
3、數據庫操作
(1)、顯示數據
SHOW DATABASES;
默認數據庫:
mysql - 用戶權限相關數據
test - 用於用戶測試數據
information_schema - MySQL本身架構相關數據
(2)、使用數據庫
USE db_name;
(3)、顯示所有表
SHOW TABLES;
(4)、用戶授權
用戶管理:
創建用戶 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地址' identified by '密碼' -- 授權並創建密碼 revoke 權限 on 數據庫.表 from '用戶'@'IP地址' -- 取消權限
-->有哪些權限:

all privileges 除grant外的所有權限 select 僅查權限 select,insert 查和插入權限 ... usage 無訪問權限 alter 使用alter table alter routine 使用alter procedure和drop procedure create 使用create table create routine 使用create procedure create temporary tables 使用create temporary tables create user 使用create user、drop user、rename user和revoke all privileges create view 使用create view delete 使用delete drop 使用drop table execute 使用call和存儲過程 file 使用select into outfile 和 load data infile grant option 使用grant 和 revoke index 使用index insert 使用insert lock tables 使用lock table process 使用show full processlist select 使用select show databases 使用show databases show view 使用show view update 使用update reload 使用flush shutdown 使用mysqladmin shutdown(關閉MySQL) super 使用change master、kill、logs、purge、master和set global。還允許mysqladmin調試登陸 replication client 服務器位置的訪問 replication slave 由復制從屬使用
-->數據庫范圍:
對於目標數據庫以及內部其他: 數據庫名.* 數據庫中的所有 數據庫名.表 指定數據庫中的某張表 數據庫名.存儲過程 指定數據庫中的存儲過程 *.* 所有數據庫
-->用戶范圍:
用戶名@IP地址 用戶只能在改IP下才能訪問 用戶名@192.168.1.% 用戶只能在改IP段下才能訪問(通配符%表示任意) 用戶名@% 用戶可以再任意IP下訪問(默認IP地址為%)
4、表操作
(1)、創建表
create table 表名( 列名 類型 是否可以為空, 列名 類型 是否可以為空 )
約束(1)是否可空null
是否可空,null表示空,非字符串 not null - 不可空 null - 可空
約束(2)默認值default
默認值,創建列時可以指定默認值,當插入數據時如果未主動設置,則自動添加默認值 create table tb1( nid int not null defalut 2, num int not null )
約束(3)自增auto_increment
自增,如果為某列設置自增列,插入數據時無需設置此列,默認將自增(表中只能有一個自增列) create table tb1( nid int not null auto_increment primary key, num int null ) 或 create table tb1( nid int not null auto_increment, num int null, index(nid) ) 注意:1、對於自增列,必須是索引(含主鍵)。 2、對於自增可以設置步長和起始值 show session variables like 'auto_inc%'; set session auto_increment_increment=2; set session auto_increment_offset=10; shwo global variables like 'auto_inc%'; set global auto_increment_increment=2; set global auto_increment_offset=10;
約束(4)主鍵primary key
主鍵,一種特殊的唯一索引,不允許有空值,如果主鍵使用單個列,則它的值必須唯一,如果是多列,則其組合必須唯一。 create table tb1( nid int not null auto_increment primary key, num int null ) 或 create table tb1( nid int not null, num int not null, primary key(nid,num) )
約束(5)唯一鍵unique key
create table tb1( nid int not null auto_increment primary key, num int null unique key, )
約束(6)外鍵foreign key
外鍵,一個特殊的索引,只能是指定內容 creat table color( nid int not null primary key, name char(16) not null ) create table fruit( nid int not null primary key, smt char(32) null , color_id int not null, constraint fk_cc foreign key (color_id) references color(nid) )
(2)、刪除表
drop table 表名
(3)、清空表
delete from 表名 truncate table 表名 # 和上面的區別在於,這條語句能夠使自增id恢復到0
(4)、修改表
添加列:alter table 表名 add 列名 類型 刪除列:alter table 表名 drop column 列名 修改列: alter table 表名 modify column 列名 類型; -- 類型 alter table 表名 change 原列名 新列名 類型; -- 列名,類型 添加主鍵: alter table 表名 add primary key(列名); 刪除主鍵: alter table 表名 drop primary key; alter table 表名 modify 列名 int, drop primary key; 添加外鍵:alter table 從表 add constraint 外鍵名稱(形如:FK_從表_主表) foreign key 從表(外鍵字段) references 主表(主鍵字段); 刪除外鍵:alter table 表名 drop foreign key 外鍵名稱 修改默認值:ALTER TABLE testalter_tbl ALTER i SET DEFAULT 1000; 刪除默認值:ALTER TABLE testalter_tbl ALTER i DROP DEFAULT;
(5)、基本數據類型
MySQL的數據類型大致分為:數值、時間和字符串
http://www.runoob.com/mysql/mysql-data-types.html
五、基本操作
1、增加數據
insert into 表 (列名,列名...) values (值,值,值...) insert into 表 (列名,列名...) values (值,值,值...),(值,值,值...) insert into 表 (列名,列名...) select (列名,列名...) from 表
2、刪除數據
delete from 表 delete from 表 where id=1 and name='alex'
3、修改數據
update 表 set name = 'alex' where id>1
4、查詢數據
select * from 表 select * from 表 where id > 1 select nid,name,gender as gg from 表 where id > 1
5、其他

1、條件 select * from 表 where id > 1 and name != 'alex' and num = 12; select * from 表 where id between 5 and 16; select * from 表 where id in (11,22,33) select * from 表 where id not in (11,22,33) select * from 表 where id in (select nid from 表) 2、通配符 select * from 表 where name like 'ale%' - ale開頭的所有(多個字符串) select * from 表 where name like 'ale_' - ale開頭的所有(一個字符) 3、限制 select * from 表 limit 5; - 前5行 select * from 表 limit 4,5; - 從第4行開始的5行 select * from 表 limit 5 offset 4 - 從第4行開始的5行 4、排序 select * from 表 order by 列 asc - 根據 “列” 從小到大排列 select * from 表 order by 列 desc - 根據 “列” 從大到小排列 select * from 表 order by 列1 desc,列2 asc - 根據 “列1” 從大到小排列,如果相同則按列2從小到大排序 5、分組 select num from 表 group by num select num,nid from 表 group by num,nid select num,nid from 表 where nid > 10 group by num,nid order nid desc select num,nid,count(*),sum(score),max(score),min(score) from 表 group by num,nid select num from 表 group by num having max(id) > 10 特別的:group by 必須在where之后,order by之前 6、連表 無對應關系則不顯示 select A.num, A.name, B.name from A,B Where A.nid = B.nid 無對應關系則不顯示 select A.num, A.name, B.name from A inner join B on A.nid = B.nid A表所有顯示,如果B中無對應關系,則值為null select A.num, A.name, B.name from A left join B on A.nid = B.nid B表所有顯示,如果B中無對應關系,則值為null select A.num, A.name, B.name from A right join B on A.nid = B.nid 7、組合 組合,自動處理重合 select nickname from A union select name from B 組合,不處理重合 select nickname from A union all select name from B
六、視圖
視圖是一個虛擬表(非真實存在),其本質是【根據SQL語句獲取動態的數據集,並為其命名】,用戶使用時只需使用【名稱】即可獲取結果集,並可以將其當作表來使用。

SELECT * FROM (SELECT nid,NAME FROM tb1 WHERE nid > 2) AS A WHERE A.NAME > 'redhat';
1、創建視圖

--格式:CREATE VIEW 視圖名稱 AS SQL語句
CREATE VIEW v1 AS
SELECT nid,name FROM A WHERE nid > 4;
2、刪除視圖

--格式:DROP VIEW 視圖名稱
DROP VIEW v1
3、修改視圖

-- 格式:ALTER VIEW 視圖名稱 AS SQL語句 ALTER VIEW v1 AS SELECT A.nid,B.NAME FROM A LEFT JOIN B ON A.id = B.nid LEFT JOIN C ON A.id = C.nid WHERE A.id > 2 AND C.nid < 5;
4、使用視圖
使用視圖時,將其當作表進行操作即可,由於視圖是虛擬表,所以無法使用其對真實表進行創建、更新和刪除操作,僅能做查詢用。

select * from v1
七、觸發器
對某個表進行【增/刪/改】操作的前后如果希望觸發某個特定的行為時,可以使用觸發器,觸發器用於定制用戶對表的行進行【增/刪/改】前后的行為。
1、創建基本語法

# 插入前 CREATE TRIGGER tri_before_insert_tb1 BEFORE INSERT ON tb1 FOR EACH ROW BEGIN ... END # 插入后 CREATE TRIGGER tri_after_insert_tb1 AFTER INSERT ON tb1 FOR EACH ROW BEGIN ... END # 刪除前 CREATE TRIGGER tri_before_delete_tb1 BEFORE DELETE ON tb1 FOR EACH ROW BEGIN ... END # 刪除后 CREATE TRIGGER tri_after_delete_tb1 AFTER DELETE ON tb1 FOR EACH ROW BEGIN ... END # 更新前 CREATE TRIGGER tri_before_update_tb1 BEFORE UPDATE ON tb1 FOR EACH ROW BEGIN ... END # 更新后 CREATE TRIGGER tri_after_update_tb1 AFTER UPDATE ON tb1 FOR EACH ROW BEGIN ... END

delimiter // CREATE TRIGGER tri_before_insert_tb1 BEFORE INSERT ON tb1 FOR EACH ROW BEGIN IF NEW. NAME == 'redhat' THEN INSERT INTO tb2 (NAME) VALUES ('aa') END END// delimiter ;

delimiter // CREATE TRIGGER tri_after_insert_tb1 AFTER INSERT ON tb1 FOR EACH ROW BEGIN IF NEW. num = 666 THEN INSERT INTO tb2 (NAME) VALUES ('666'), ('666') ; ELSEIF NEW. num = 555 THEN INSERT INTO tb2 (NAME) VALUES ('555'), ('555') ; END IF; END// delimiter ;
特別的:NEW表示即將插入的數據行,OLD表示即將刪除的數據行。
2、刪除觸發器
DROP TRIGGER tri_after_insert_tb1;
3、使用觸發器
觸發器無法由用戶直接調用,而知由於對表的【增/刪/改】操作被動引發的。
insert into tb1(num) values(666)
八、存儲過程
存儲過程是一個SQL語句集合,當主動去調用存儲過程時,其中內部的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、執行存儲過程
-- 無參數 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)

#!/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)
九、函數
MySQL中提供了許多內置函數,例如:

CHAR_LENGTH(str) 返回值為字符串str 的長度,長度的單位為字符。一個多字節字符算作一個單字符。 對於一個包含五個二字節字符集, LENGTH()返回值為 10, 而CHAR_LENGTH()的返回值為5。 CONCAT(str1,str2,...) 字符串拼接 如有任何一個參數為NULL ,則返回值為 NULL。 CONCAT_WS(separator,str1,str2,...) 字符串拼接(自定義連接符) CONCAT_WS()不會忽略任何空字符串。 (然而會忽略所有的 NULL)。 CONV(N,from_base,to_base) 進制轉換 例如: SELECT CONV('a',16,2); 表示將 a 由16進制轉換為2進制字符串表示 FORMAT(X,D) 將數字X 的格式寫為'#,###,###.##',以四舍五入的方式保留小數點后 D 位, 並將結果以字符串的形式返回。若 D 為 0, 則返回結果不帶有小數點,或不含小數部分。 例如: SELECT FORMAT(12332.1,4); 結果為: '12,332.1000' INSERT(str,pos,len,newstr) 在str的指定位置插入字符串 pos:要替換位置其實位置 len:替換的長度 newstr:新字符串 特別的: 如果pos超過原字符串長度,則返回原字符串 如果len超過原字符串長度,則由新字符串完全替換 INSTR(str,substr) 返回字符串 str 中子字符串的第一個出現位置。 LEFT(str,len) 返回字符串str 從開始的len位置的子序列字符。 LOWER(str) 變小寫 UPPER(str) 變大寫 LTRIM(str) 返回字符串 str ,其引導空格字符被刪除。 RTRIM(str) 返回字符串 str ,結尾空格字符被刪去。 SUBSTRING(str,pos,len) 獲取字符串子序列 LOCATE(substr,str,pos) 獲取子序列索引位置 REPEAT(str,count) 返回一個由重復的字符串str 組成的字符串,字符串str的數目等於count 。 若 count <= 0,則返回一個空字符串。 若str 或 count 為 NULL,則返回 NULL 。 REPLACE(str,from_str,to_str) 返回字符串str 以及所有被字符串to_str替代的字符串from_str 。 REVERSE(str) 返回字符串 str ,順序和字符順序相反。 RIGHT(str,len) 從字符串str 開始,返回從后邊開始len個字符組成的子序列 SPACE(N) 返回一個由N空格組成的字符串。 SUBSTRING(str,pos) , SUBSTRING(str FROM pos) SUBSTRING(str,pos,len) , SUBSTRING(str FROM pos FOR len) 不帶有len 參數的格式從字符串str返回一個子字符串,起始於位置 pos。帶有len參數的格式從字符串str返回一個長度同len字符相同的子字符串,起始於位置 pos。 使用 FROM的格式為標准 SQL 語法。也可能對pos使用一個負值。假若這樣,則子字符串的位置起始於字符串結尾的pos 字符,而不是字符串的開頭位置。在以下格式的函數中可以對pos 使用一個負值。 mysql> SELECT SUBSTRING('Quadratically',5); -> 'ratically' mysql> SELECT SUBSTRING('foobarbar' FROM 4); -> 'barbar' mysql> SELECT SUBSTRING('Quadratically',5,6); -> 'ratica' mysql> SELECT SUBSTRING('Sakila', -3); -> 'ila' mysql> SELECT SUBSTRING('Sakila', -5, 3); -> 'aki' mysql> SELECT SUBSTRING('Sakila' FROM -4 FOR 2); -> 'ki' TRIM([{BOTH | LEADING | TRAILING} [remstr] FROM] str) TRIM(remstr FROM] str) 返回字符串 str , 其中所有remstr 前綴和/或后綴都已被刪除。若分類符BOTH、LEADIN或TRAILING中沒有一個是給定的,則假設為BOTH 。 remstr 為可選項,在未指定情況下,可刪除空格。 mysql> SELECT TRIM(' bar '); -> 'bar' mysql> SELECT TRIM(LEADING 'x' FROM 'xxxbarxxx'); -> 'barxxx' mysql> SELECT TRIM(BOTH 'x' FROM 'xxxbarxxx'); -> 'bar' mysql> SELECT TRIM(TRAILING 'xyz' FROM 'barxxyz'); -> 'barx'
更多:
http://doc.mysql.cn/mysql5/refman-5.1-zh.html-chapter/functions.html#encryption-functions
http://dev.mysql.com/doc/refman/5.7/en/string-functions.html
1、自定義函數

delimiter \\ create function f1( i1 int, i2 int) returns int BEGIN declare num int; set num = i1 + i2; return(num); END \\ delimiter ;
2、刪除函數
drop function func_name;
3、執行函數

# 獲取返回值 declare @i VARCHAR(32); select UPPER('alex') into @i; SELECT @i; # 在查詢中使用 select f1(11,nid) ,name from tb2;
十、索引
事務用於將某些操作的多個SQL作為原子性操作,一旦有某一個出現錯誤,即可回滾到原來的狀態,從而保證數據庫數據完整性。

delimiter \\ 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 ;

DECLARE @i TINYINT;
call p1(@i);
select @i;
十一、索引
索引,是數據庫中專門用於幫助用戶快速查詢數據的一種數據結構。類似於字典中的目錄,查找字典內容時可以根據目錄查找到數據的存放位置,然后直接獲取即可。
MySQL中常見索引有:
- 普通索引
- 唯一索引
- 主鍵索引
- 組合索引
1、普通索引
普通索引僅有一個功能:加速查詢

create table in1( nid int not null auto_increment primary key, name varchar(32) not null, email varchar(64) not null, extra text, index ix_name (name) )

create index index_name on table_name(column_name)

drop index_name on table_name;

show index from table_name;
注意:對於創建索引時如果是BLOB 和 TEXT 類型,必須指定length。

create index ix_extra on in1(extra(32));
2、唯一索引
唯一索引有兩個功能:加速查詢 和 唯一約束(可含null)

create table in1( nid int not null auto_increment primary key, name varchar(32) not null, email varchar(64) not null, extra text, unique ix_name (name) )

create unique index 索引名 on 表名(列名)

drop unique index 索引名 on 表名
3、主鍵索引
主鍵有兩個功能:加速查詢 和 唯一約束(不可含null)

create table in1( nid int not null auto_increment primary key, name varchar(32) not null, email varchar(64) not null, extra text, index ix_name (name) ) OR create table in1( nid int not null auto_increment, name varchar(32) not null, email varchar(64) not null, extra text, primary key(ni1), index ix_name (name) )

alter table 表名 add primary key(列名);

alter table 表名 drop primary key;
alter table 表名 modify 列名 int, drop primary key;
4、組合索引
組合索引是將n個列組合成一個索引
其應用場景為:頻繁的同時使用n列來進行查詢,如:where n1 = 'redhat' and n2 = 666。

create table in3( nid int not null auto_increment primary key, name varchar(32) not null, email varchar(64) not null, extra text )

create index ix_name_email on in3(name,email);
如上創建組合索引之后,查詢:
- name and email -- 使用索引
- name -- 使用索引
- email -- 不使用索引
注意:對於同時搜索n個條件時,組合索引的性能好於多個單一索引合並。
其他
1、條件語句

delimiter \\ CREATE PROCEDURE proc_if () BEGIN declare i int default 0; if i = 1 THEN SELECT 1; ELSEIF i = 2 THEN SELECT 2; ELSE SELECT 7; END IF; END\\ delimiter ;
2、循環語句

delimiter \\ CREATE PROCEDURE proc_while () BEGIN DECLARE num INT ; SET num = 0 ; WHILE num < 10 DO SELECT num ; SET num = num + 1 ; END WHILE ; END\\ delimiter ;

delimiter \\ CREATE PROCEDURE proc_repeat () BEGIN DECLARE i INT ; SET i = 0 ; repeat select i; set i = i + 1; until i >= 5 end repeat; END\\ delimiter ;

delimiter \\ CREATE PROCEDURE proc_loop () BEGIN declare i int default 0; loop_label: loop select i; set i=i+1; if i>=5 then leave loop_label; end if; end loop; END\\ delimiter ;
3、動態執行SQL語句

delimiter \\ DROP PROCEDURE IF EXISTS proc_sql \\ CREATE PROCEDURE proc_sql () BEGIN declare p1 int; set p1 = 11; set @p1 = p1; PREPARE prod FROM 'select * from tb2 where nid > ?'; EXECUTE prod USING @p1; DEALLOCATE prepare prod; END\\ delimiter ;
參考:
http://www.cnblogs.com/wupeiqi/articles/5713315.html
http://www.cnblogs.com/wupeiqi/articles/5713323.html
http://www.cnblogs.com/wupeiqi/articles/5713334.html
http://www.cnblogs.com/wupeiqi/articles/5713363.html
http://www.cnblogs.com/wupeiqi/articles/5729934.html
http://www.cnblogs.com/wupeiqi/articles/5748496.html
二、pymsql模塊
pymsql是Python中操作MySQL的模塊,其使用方法和MySQLdb幾乎相同。這里介紹pymysql
一、下載安裝:
pip install pymysql
二、使用
# 先往數據庫插入點數據 mysql> CREATE DATABASE IF NOT EXISTS testdb DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci; mysql> use testdb; mysql> CREATE TABLE hosts (id int not null auto_increment primary key,ip varchar(20) not null,port int not null); mysql> INSERT INTO hosts (ip,port) values ('1.1.1.1',22),('1.1.1.2',22), ('1.1.1.3',22),('1.1.1.4',22), ('1.1.1.5',22);
1、執行SQL
#!/usr/bin/env python # -*- coding:utf-8 -*- import pymysql # 創建連接 conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='123456', db='testdb', charset='utf8') # 創建游標 cursor = conn.cursor() # 執行SQL,並返回收影響行數 r = effect_row = cursor.execute("update hosts set ip = '1.1.1.2'") print(r) # 執行SQL,並返回受影響行數 effect_row = cursor.execute("update hosts set ip = '1.1.1.6' where id > %s", (6,)) # 執行SQL,並返回受影響行數 effect_row = cursor.executemany("insert into hosts(ip,port) values(%s,%s)", [("1.1.1.7",21),("1.1.1.8",23)]) # 提交,不然無法保存新建或者修改的數據 conn.commit() # 關閉游標 cursor.close() # 關閉連接 conn.close()
2、獲取新創建數據自增ID
#!/usr/bin/env python # -*- coding:utf-8 -*- import pymysql conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='123456', db='testdb') cursor = conn.cursor() cursor.executemany("insert into hosts(ip,port) values(%s,%s)", [("1.1.1.9",21),("1.1.1.10",222)]) conn.commit() cursor.close() conn.close() # 獲取最新自增ID new_id = cursor.lastrowid print(new_id)
# 獲取當前行行號
current_row = cursor.rownumber
print(current_row)
3、獲取查詢數據
#!/usr/bin/env python # -*- coding:utf-8 -*- import pymysql conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='123456', db='testdb') cursor = conn.cursor() cursor.execute("select * from hosts") # 獲取第一行數據 row = cursor.fetchone() print(row) print(cursor.rownumber) # 獲取第二行數據 row_1 = cursor.fetchone() print(row_1) print(cursor.rownumber) # 獲取前n行數據 row_2 = cursor.fetchmany(3) print(row_2) # 獲取所有數據 row_3 = cursor.fetchall() print(row_3) conn.commit() cursor.close() conn.close()
注:在fetch數據時按照順序進行,可以使用cursor.scroll(num,mode)來移動游標位置,如:
- cursor.scroll(1,mode='relative') # 相對當前位置移動
- cursor.scroll(2,mode='absolute') # 相對絕對位置移動
4、fetch數據類型
關於默認獲取的數據是元祖類型,如果想要或者字典類型的數據,即:
#!/usr/bin/env python # -*- coding:utf-8 -*- import pymysql conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='123456', db='testdb') # 游標設置為字典類型 cursor = conn.cursor(cursor=pymysql.cursors.DictCursor) r = cursor.execute("select * from hosts") print(r) result = cursor.fetchone() print(result) conn.commit() cursor.close() conn.close()
三、SQLAchemy
SQLAlchemy是Python編程語言下的一款ORM框架,該框架建立在數據庫API之上,使用關系對象映射進行數據庫操作,簡言之便是:將對象轉換成SQL,然后使用數據API執行SQL並獲取執行結果。
SQLAlchemy安裝
pip install sqlalchemy
SQLAlchemy本身無法操作數據庫,其必須以來pymsql等第三方插件,Dialect用於和數據API進行交流,根據配置文件的不同調用不同的數據庫API,從而實現對數據庫的操作,如:
MySQL-Python mysql+mysqldb://<user>:<password>@<host>[:<port>]/<dbname> pymysql mysql+pymysql://<username>:<password>@<host>/<dbname>[?<options>] MySQL-Connector mysql+mysqlconnector://<user>:<password>@<host>[:<port>]/<dbname> cx_Oracle oracle+cx_oracle://user:pass@host:port/dbname[?key=value&key=value...] 更多詳見:http://docs.sqlalchemy.org/en/latest/dialects/index.html
一、底層處理
使用 Engine/ConnectionPooling/Dialect 進行數據庫操作,Engine使用ConnectionPooling連接數據庫,然后再通過Dialect執行SQL語句。
#!/usr/bin/env python # -*- coding:utf-8 -*- from sqlalchemy import create_engine engine = create_engine("mysql+pymysql://root:123456@127.0.0.1:3306/testdb", max_overflow=5) # max_overflow可以超出連接池5個連接 #執行SQL cur = engine.execute( "INSERT INTO hosts (ip,port) VALUES ('1.1.1.11', 22)" ) #新插入行自增ID cur.lastrowid #執行SQL cur = engine.execute( "INSERT INTO hosts (ip,port) VALUES(%s, %s)",[('1.1.1.12', 22),('1.1.1.13', 55),] ) #執行SQL cur = engine.execute( "INSERT INTO hosts (ip,port) VALUES (%(ip)s, %(port)s)", ip='1.1.1.15', port=80 ) #執行SQL cur = engine.execute('select * from hosts') #獲取第一行數據 cur.fetchone() #獲取第n行數據 cur.fetchmany(3) #獲取所有數據 cur.fetchall()
二、ORM功能使用
使用 ORM/Schema Type/SQL Expression Language/Engine/ConnectionPooling/Dialect 所有組件對數據進行操作。根據類創建對象,對象轉換成SQL,執行SQL。
1、創建表
#!/usr/bin/env python # -*- coding:utf-8 -*- from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import Column, Integer, String, ForeignKey, UniqueConstraint, Index from sqlalchemy.orm import sessionmaker, relationship from sqlalchemy import create_engine engine = create_engine("mysql+pymysql://root:123456@127.0.0.1:3306/testdb", max_overflow=5) Base = declarative_base() # 創建單表 class Users(Base): __tablename__ = 'users' id = Column(Integer, primary_key=True,autoincrement=True) name = Column(String(32)) extra = Column(String(16)) __table_args__ = ( UniqueConstraint('id', 'name', name='uix_id_name'), # 設置組合唯一鍵約束 Index('ix_id_name', 'name', 'extra'), # 創建索引 ) # 一對多關系 class Favor(Base): __tablename__ = 'favor' nid = Column(Integer, primary_key=True,autoincrement=True) caption = Column(String(50), default='playing', unique=True) class Person(Base): __tablename__ = 'person' nid = Column(Integer, primary_key=True,autoincrement=True) name = Column(String(32), index=True, nullable=True) favor_id = Column(Integer, ForeignKey("favor.nid")) # 設置外鍵,外鍵依賴於主表favor的主鍵nid # 與生成表結構無關,僅用於外鍵查詢方便 favor = relationship("Favor", backref='pers') # 多對多 class Group(Base): __tablename__ = 'group' id = Column(Integer, primary_key=True,autoincrement=True) name = Column(String(64), unique=True, nullable=False) # group = relationship('Group',secondary=ServerToGroup,backref='host_list') class Server(Base): __tablename__ = 'server' id = Column(Integer, primary_key=True, autoincrement=True) hostname = Column(String(64), unique=True, nullable=False) port = Column(Integer, default=22) class ServerToGroup(Base): """多對多關系,則是多生成一張表用來存儲主鍵""" __tablename__ = 'servertogroup' nid = Column(Integer, primary_key=True, autoincrement=True) server_id = Column(Integer, ForeignKey('server.id')) # 設置主鍵 group_id = Column(Integer, ForeignKey('group.id')) # 設置主鍵 def init_db(): """創建表的函數,調用之后會創建表""" Base.metadata.create_all(engine) def drop_db(): """刪除表的函數,調用之后會刪除表""" Base.metadata.drop_all(engine) init_db() # 創建完最好注釋,如果表已經創建,執行此步不會再創建表 # drop_db() # 想要對表進行操作則需要綁定會話 Session = sessionmaker(bind=engine) session = Session()
2、操作表
(1)增加數據
# 插入一條 obj = Users(name='redhat', extra='rh') print(obj) session.add(obj) # 插入多條 session.add_all([ Users(name='centos', extra='ct'), Users(name='debian', extra='db'), Users(name='ubuntu', extra='ub'), Users(name='fedora', extra='fd'), ]) # 插入完需要提交否則不生效 session.commit()
(2)刪除數據
session.query(Users).filter(Users.id > 5).delete() session.commit()
(3)修改數據
session.query(Users).filter(Users.id > 2).update({"name" : "opensuse"}) session.query(Users).filter(Users.id > 2).update({Users.name: Users.name + "099"}, synchronize_session=False) #session.query(Users).filter(Users.id > 2).update({"num": Users.num + 1}, synchronize_session="evaluate") session.commit()
(4)查詢數據(如果沒有使用ORM則返回的是一個表對象)
ret = session.query(Users).all() ret = session.query(Users.name, Users.extra).all() ret = session.query(Users).filter_by(name='opensuse099').all() ret = session.query(Users).filter_by(name='opensuse099').first()
(5)其他一些查詢操作
# 條件 ret = session.query(Users).filter_by(name='centos').all() ret = session.query(Users).filter(Users.id > 1, Users.name == 'centos').all() ret = session.query(Users).filter(Users.id.between(1, 3), Users.name == 'centos').all() ret = session.query(Users).filter(Users.id.in_([1,3,4])).all() ret = session.query(Users).filter(~Users.id.in_([1,3,4])).all() ret = session.query(Users).filter(Users.id.in_(session.query(Users.id).filter_by(name='centos'))).all() from sqlalchemy import and_, or_ ret = session.query(Users).filter(and_(Users.id > 3, Users.name == 'centos')).all() ret = session.query(Users).filter(or_(Users.id < 2, Users.name == 'centos')).all() ret = session.query(Users).filter( or_( Users.id < 2, and_(Users.name == 'centos', Users.id > 3), Users.extra != "" )).all() # 通配符 ret = session.query(Users).filter(Users.name.like('e%')).all() ret = session.query(Users).filter(~Users.name.like('e%')).all() # 限制 ret = session.query(Users)[1:2] # 排序 ret = session.query(Users).order_by(Users.name.desc()).all() ret = session.query(Users).order_by(Users.name.desc(), Users.id.asc()).all() # 分組 from sqlalchemy.sql import func ret = session.query(Users).group_by(Users.extra).all() ret = session.query( func.max(Users.id), func.sum(Users.id), func.min(Users.id)).group_by(Users.name).all() ret = session.query( func.max(Users.id), func.sum(Users.id), func.min(Users.id)).group_by(Users.name).having(func.min(Users.id) >2).all() # 連表 ret = session.query(Users, Favor).filter(Users.id == Favor.nid).all() ret = session.query(Person).join(Favor).all() ret = session.query(Person).join(Favor, isouter=True).all() # 組合 q1 = session.query(Users.name).filter(Users.id > 2) q2 = session.query(Favor.caption).filter(Favor.nid < 2) ret = q1.union(q2).all() q1 = session.query(Users.name).filter(Users.id > 2) q2 = session.query(Favor.caption).filter(Favor.nid < 2) ret = q1.union_all(q2).all()
三、一對多、多對多的連表查詢
一對多
(1)、新創建個數據庫sqldb
mysql> create database sqldb default character set utf8 collate utf8_general_ci;
(2)、通過ORM生成表
#!/usr/bin/env python # -*- coding:utf-8 -*- from sqlalchemy import create_engine from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import Column, Integer, String, ForeignKey, UniqueConstraint, Index from sqlalchemy.orm import sessionmaker, relationship engine = create_engine("mysql+pymysql://root:123456@127.0.0.1:3306/sqldb", max_overflow=5) Base = declarative_base() # 一對多關系 class User(Base): __tablename__ = 'user' nid = Column(Integer, primary_key=True,autoincrement=True) username = Column(String(50)) group_id = Column(Integer, ForeignKey("group.nid")) # 虛擬關系與Group建立關系,相當於會User生成一個group字段,Group表生成一個uuu字段,實際是不會生成這些字段只是虛擬的關系,其實只是生成了sql語句 group = relationship("Group", backref='uuu') def __repr__(self): """只是打印的時候輸出的內容和數據庫取數據沒有關系""" temp = "%s:%s:%s" %(self.nid,self.username,self.group_id) return temp class Group(Base): __tablename__ = 'group' nid = Column(Integer, primary_key=True,autoincrement=True) caption = Column(String(50)) def init_db(): Base.metadata.create_all(engine) def drop_db(): Base.metadata.drop_all(engine) init_db() #drop_db() Session = sessionmaker(bind=engine) session = Session()
(3)、增加數據,執行完需要注釋掉
session.add_all([ Group(caption='chuzhong'), Group(caption='gaozhong'), Group(caption='daxue'), ]) session.add_all([ User(username='redhat', group_id=1), User(username='centos', group_id=2), User(username='fedora', group_id=3), User(username='debian', group_id=2), User(username='ubuntu', group_id=1), User(username='geentoo', group_id=3), User(username='archlinux', group_id=1), User(username='windows', group_id=2), User(username='suse', group_id=3), ]) session.commit()
(4)、內連接
# select * from user left join group on user.group_id = group.nid sql = session.query(User,Group).join(Group) # 打印SQL語句 print(sql) ret = session.query(User,Group).join(Group).all() # 默認是內連接 print(ret)
(5)、外連接
sql = session.query(User,Group).join(Group,isouter=True) # 打印sql語句 print(sql) ret = session.query(User,Group).join(Group,isouter=True).all() # isouter=True 指定外連接,默認是做外連接 print(ret)
(6)、練習:
-->獲取User表完整信息,兩種方式
# 通過傳統方式 sql = session.query(User.username,Group.caption).join(Group,isouter=True) print(sql) ret = session.query(User.username,Group.caption).join(Group,isouter=True).all() print(ret) # 通過虛擬關系 # 一般relationship 外鍵設置在哪張表,relationship就設置在哪表,通過這張表查詢就叫正向查詢, ret = session.query(User).all() for obj in ret: # obj代指user表的每一行數據 # obj.group代指group對象 print(obj.nid,obj.username,obj.group_id, obj.group,obj.group.nid,obj.group.caption)
-->通過Group表屬於daxue的有多少username,兩種方式
# 傳統方式 ret = session.query(User.username, Group.caption).join(Group, isouter=True).filter(Group.caption == 'daxue').all() print(ret) # 通過虛擬關系 # 反向查詢 obj = session.query(Group).filter(Group.caption == 'daxue').first() print(obj.nid) print(obj.caption) print(obj.uuu)
多對多
(1)、通過ORM生成表
#!/usr/bin/env python # -*- coding:utf-8 -*- from sqlalchemy import create_engine from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import Column, Integer, String, ForeignKey, UniqueConstraint, Index from sqlalchemy.orm import sessionmaker, relationship engine = create_engine("mysql+pymysql://root:123456@127.0.0.1:3306/sqldb", max_overflow=5) Base = declarative_base() # 多對多關系 class Host(Base): __tablename__ = 'host' nid = Column(Integer, primary_key=True,autoincrement=True) hostname = Column(String(50)) ip = Column(String(32)) port = Column(String(32)) class HostUser(Base): __tablename__ = 'host_user' nid = Column(Integer, primary_key=True,autoincrement=True) username = Column(String(32)) class HostToHostUser(Base): __tablename__ = 'host_to_host_user' nid = Column(Integer, primary_key=True,autoincrement=True) # 保留主鍵,以后通過找主鍵刪除數據方便 host_id = Column(Integer,ForeignKey('host.nid')) host_user_id = Column(Integer,ForeignKey('host_user.nid')) host = relationship('Host',backref='h') host_user = relationship('HostUser',backref='u') def init_db(): Base.metadata.create_all(engine) def drop_db(): Base.metadata.drop_all(engine) init_db() # drop_db() Session = sessionmaker(bind=engine) session = Session()
(2)增加數據,執行完需要注釋掉
session.add_all([ Host(hostname='c1',ip='1.1.1.1',port='22'), Host(hostname='c2',ip='1.1.1.2',port='22'), Host(hostname='c3',ip='1.1.1.3',port='22'), Host(hostname='c4',ip='1.1.1.4',port='22'), Host(hostname='c5',ip='1.1.1.5',port='22'), ]) session.add_all([ HostUser(username='root'), HostUser(username='redhat'), HostUser(username='centos'), HostUser(username='ubuntu'), ]) session.add_all([ HostToHostUser(host_id=1,host_user_id=1), HostToHostUser(host_id=1,host_user_id=2), HostToHostUser(host_id=1,host_user_id=3), HostToHostUser(host_id=2,host_user_id=2), HostToHostUser(host_id=2,host_user_id=4), HostToHostUser(host_id=2,host_user_id=3), ]) session.commit()
練習:
獲取Host中所有用戶
第一種方式:傳統方式
# 第一步獲取Host中用戶等於c1的對象 host_obj = session.query(Host).filter(Host.hostname == 'c1').first() # host_obj.nid 獲取用戶等於c1的nid # 第二步通過HostToHostUser表找到HostUser外鍵對應的host_user_id host_2_host_user = session.query(HostToHostUser.host_user_id).filter(HostToHostUser.host_id == host_obj.nid).all() print(host_2_host_user) [(1,),(2,),(3,)] r = zip(*host_2_host_user) print(list(r)[0]) [1,2,3] # 第三步通過第二步找到HostUser中nid對應的HostUser.username users = session.query(HostUser.username).filter(HostUser.nid.in_(list(r)[0])).all() print(users) 可以把上面的步驟全部合並到一起 # session.query(Host.nid).filter(Host.hostname == 'c1') # session.query(HostToHostUser.host_user_id).filter(HostToHostUser.host_id == session.query(Host.nid).filter(Host.hostname == 'c1')) r = session.query(HostUser.username).filter(HostUser.nid.in_(session.query(HostToHostUser.host_user_id).filter(HostToHostUser.host_id == session.query(Host.nid).filter(Host.hostname == 'c1')))).all() print(r)
第二種方式:虛擬關系(relationship是在外鍵表HostToHostUser表中)
# 通過relation虛擬關系中的h字段反向查找,查找到第三表中的對象,然后通過正向查找 host_obj = session.query(Host).filter(Host.hostname == 'c1').first() for item in host_obj.h: print(item.host_user.username)
第三種方式:虛擬關系(relationship是在Host表中)需要修改下表結構
class HostToHostUser(Base): __tablename__ = 'host_to_host_user' nid = Column(Integer, primary_key=True,autoincrement=True) # 保留主鍵,以后通過找主鍵刪除數據方便 host_id = Column(Integer,ForeignKey('host.nid')) host_user_id = Column(Integer,ForeignKey('host_user.nid')) # 多對多關系 class Host(Base): __tablename__ = 'host' nid = Column(Integer, primary_key=True,autoincrement=True) hostname = Column(String(50)) ip = Column(String(32)) port = Column(String(32))
# 關系表在User表中,realationship(A,AB.__table,B中的虛擬字段) host_user = relationship('HostUser', secondary=HostToHostUser.__table__, backref='h') class HostUser(Base): __tablename__ = 'host_user' nid = Column(Integer, primary_key=True,autoincrement=True) username = Column(String(32))
此時直接就可以從Host表查找到HostUser表中的數據而不用涉及HostToHostUser外鍵表了
host_obj = session.query(Host).filter(Host.hostname == 'c1').first() for item in host_obj.host_user: print(item.username)