mysql和SQLAlchemy
一、MySQL分組查詢
1.1 MySQL對數據表進行分組查詢(GROUP BY)
1、GROUP BY基本語法格式:
GROUP BY關鍵字可以將查詢結果按照某個字段或多個字段進行分組。字段中值相等的為一組。基本的語法格式如下:
GROUP BY 屬性名 [HAVING 條件表達式] [WITH ROLLUP]
- 屬性名:是指按照該字段的值進行分組。
- HAVING 條件表達式:用來限制分組后的顯示,符合條件表達式的結果將被顯示。
- WITH ROLLUP:將會在所有記錄的最后加上一條記錄。加上的這一條記錄是上面所有記錄的總和。
2、GROUP BY聯合函數使用:
1)GROUP BY關鍵字可以和GROUP_CONCAT()函數一起使用。
2)GROUP_CONCAT()函數會把每個分組中指定的字段值都顯示出來。
3)同時,GROUP BY關鍵字通常與集合函數一起使用。集合函數包括COUNT()函數、SUM()函數、AVG()函數、MAX()函數和MIN()函數等。
4)注意:如果GROUP BY不與上述函數一起使用,那么查詢結果就是字段取值的分組情況。字段中取值相同的記錄為一組,但是只顯示該組的第一條記錄。
1. 首先執行不帶GROUP BY關鍵字的SELECT語句。如下圖所示:
select * from employee;
2. 執行帶有GROUP BY關鍵字的SELECT語句。代碼如下:
SELECT * FROM employee GROUP BY sex;
+----+------+--------+-----+-----+-------------+
| id | num | name | sex | age | homeaddress |
+----+------+--------+-----+-----+-------------+
| 2 | 1001 | 馬莉莉 | 女 | 24 | 河南開封 |
| 1 | 1001 | 王冬軍 | 男 | 26 | 河南鄭州 |
+----+------+--------+-----+-----+-------------+
2 rows in set (0.00 sec)
上圖中代碼執行的結果只顯示了兩條記錄。這兩條記錄的sex字段的值分別為“女”和“男”。
查詢結果進行比較,GROUP BY關鍵字只顯示每個分組的一條記錄。這說明,GROUP BY關鍵字單獨使用時,只能查詢出每個分組的一條記錄,這樣做的意義不大。
因此,一般在使用集合函數時才使用GROUP BY關鍵字。
1.2 GROUP BY關鍵字與GROUP_CONCAT()函數一起使用
GROUP BY關鍵字與GROUP_CONCAT()函數一起使用時,每個分組中指定的字段值會全部顯示出來。
實例:將employee表按照sex字段進行分組查詢。使用GROUP_CONCAT()函數將每個分組的name字段的值顯示出來。
SELECT sex,GROUP_CONCAT(name) FROM employee GROUP BY sex;
+-----+--------------------+
| sex | GROUP_CONCAT(name) |
+-----+--------------------+
| 女 | 馬莉莉,張雪梅 |
| 男 | 王冬軍,劉兵,Tom |
+-----+--------------------+
2 rows in set (0.00 sec)
上圖中代碼執行的結果顯示,查詢結果分為兩組。sex字段取值為“女”的記錄是一組,取值為“男”的記錄是一組。
每一組中所有人的名字都被查詢出來了。
該實例說明,使用GROUP_CONCAT()函數可以很好的把分組情況表示出來。
mysql> SELECT sex,GROUP_CONCAT(name) FROM employee GROUP BY sex WITH ROLLUP;
+-----+-------------------------------+
| sex | GROUP_CONCAT(name) |
+-----+-------------------------------+
| 女 | 馬莉莉,張雪梅 |
| 男 | 王冬軍,劉兵,Tom |
| NULL | 馬莉莉,張雪梅,王冬軍,劉兵,Tom |
+-----+-------------------------------+
3 rows in set (0.00 sec)
1.3 GROUP BY關鍵字與集合函數一起使用
GROUP BY關鍵字與集合函數一起使用時,可以通過集合函數計算分組中的總記錄、最大值、最小值等。
實例:將employee表的sex字段進行分組查詢。sex字段取值相同的為一組。然后對每一組使用集合函數COUNT()函數進行計算,求出每一組的記錄數。
mysql> SELECT sex,COUNT(sex) FROM employee GROUP BY sex;
+-----+------------+
| sex | COUNT(sex) |
+-----+------------+
| 女 | 2 |
| 男 | 3 |
+-----+------------+
2 rows in set (0.00 sec)
上圖中代碼執行的結果顯示,查詢結果按sex字段的取值進行分組。取值為“女”的記錄為一組,取值為“男”的記錄為一組。
COUNT(sex)計算出了sex字段不同分組的記錄數。第一組共有2條記錄,第二組共有3條記錄。
WITH ROLLUP:將會在所有記錄的最后加上一條記錄。加上的這一條記錄是上面所有記錄的總和。
mysql> SELECT sex,COUNT(sex) FROM employee GROUP BY sex WITH ROLLUP;
+-----+------------+
| sex | COUNT(sex) |
+-----+------------+
| 女 | 2 |
| 男 | 3 |
| NULL | 5 |
+-----+------------+
3 rows in set (0.00 sec)
1.4 GROUP BY關鍵字與HAVING一起使用
使用GROUP BY關鍵字時,如果加上“HAVING 條件表達式”,則可以限制輸出的結果。只有符合條件表達式的結果才會顯示。
實例:將employee表的sex字段進行分組查詢。然后顯示記錄數大於等於3的分組。
SELECT語句的代碼如下:
mysql> SELECT sex,COUNT(sex) FROM employee GROUP BY sex HAVING COUNT(sex)>=3;
+-----+------------+
| sex | COUNT(sex) |
+-----+------------+
| 男 | 3 |
+-----+------------+
1 row in set (0.00 sec)
1.5按照多個字段進行分組
在MySQL中,還可以按照多個字段進行分組。例如,employee表按照num字段和sex字段進行分組。分組過程中,
先按照num字段進行分組,遇到num字段的值相等的情況時,再把num值相等的記錄按照sex字段進行分組。
實例:將employee表按照num字段和sex字段進行分組。
SELECT語句的代碼如下:
mysql> SELECT * FROM employee GROUP BY num,sex;
+----+------+--------+-----+-----+-------------+
| id | num | name | sex | age | homeaddress |
+----+------+--------+-----+-----+-------------+
| 2 | 1001 | 馬莉莉 | 女 | 24 | 河南開封 |
| 1 | 1001 | 王冬軍 | 男 | 26 | 河南鄭州 |
| 3 | 1002 | 劉兵 | 男 | 25 | 廣東省廣州 |
| 5 | 1004 | 張雪梅 | 女 | 20 | 福建廈門 |
| 4 | 1004 | Tom | 男 | 18 | America |
+----+------+--------+-----+-----+-------------+
5 rows in set (0.00 sec)
二、Mysql聯表查詢
2.1 內聯結和外聯結的含義及區別
1.內聯結:將兩個表中存在聯結關系的字段符合聯結關系的那些記錄形成記錄集的聯結。
2.外聯結:分為外左聯結和外右聯結。
右聯結A、B表的結果和左聯結B、A的結果是一樣的,也就是說:
Select A.name B.name From A Left Join B On A.id=B.id
Select A.name B.name From B Right Join A on B.id=A.id
執行后的結果是一樣的。
說明:
1)內外聯結的區別是內聯結將去除所有不符合條件的記錄,而外聯結則保留其中部分。
2) 外左聯結與外右聯結的區別在於如果用A左聯 結B則A中所有記錄都會保留在結果中,此時B中只有符合聯結條件的記錄,而右聯結相反。
2.2、例子
假設有如下兩張表:
表A |
|
ID |
Name |
1 |
Tiim |
2 |
Jimmy |
3 |
John |
4 |
Tom |
表B |
|
ID |
Hobby |
1 |
Football |
2 |
Basketball |
2 |
Tennis |
4 |
Soccer |
1)內聯結:
Select A.Name B.Hobby from A, B where A.id = B.id
這是隱式的內聯結,查詢的結果是:
Name |
Hobby |
Tim |
Football |
Jimmy |
Basketball |
Jimmy |
Tennis |
Tom |
Soccer |
它的作用和:
Select A.Name from A INNER JOIN B ON A.id = B.id
是一樣的。
2)外左聯結
Select A.Name from A Left JOIN B ON A.id = B.id
這樣查詢得到的結果將會是保留所有A表中聯結字段的記錄,若無與其相對應的B表中的字段記錄則留空,結果如下:
Name |
Hobby |
Tim |
Football |
Jimmy |
Basketball,Tennis |
John |
|
Tom |
Soccer |
所以從上面結果看出,因為A表中的John記錄的ID沒有在B表中有對應ID,因此為空,但Name欄仍有John記錄。
2)外右聯結
Select A.Name from A Right JOIN B ON A.id = B.id
結果將會是:
Name |
Hobby |
Tim |
Football |
Jimmy |
Basketball |
Jimmy |
Tennis |
Tom |
Soccer |
此時B表中的全部記錄都打印了,但是A表沒有顯示完整記錄,只是顯示了跟B表相關聯的記錄。
2.3、聯表查詢中用到的一些參數
1.USING (column_list):
其作用是為了方便書寫聯結的多對應關系,大部分情況下USING語句可以用ON語句來代替,如下面例子:
a LEFT JOIN b USING (c1,c2,c3),其作用相當於
a LEFT JOIN b ON a.c1=b.c1 AND a.c2=b.c2 AND a.c3=b.c3
2.STRAIGHT_JOIN:
由於默認情況下MySQL在進行表的聯結的時候會先讀入左表,當使用了這個參數后MySQL將會先讀入右表,這是個MySQL的內置優化參數,
大家應該在特定情況下使用,譬如已經確認右表中的記錄數量少,在篩選后能大大提高查詢速度。
三、數據庫操作
下載
http://dev.mysql.com/downloads/mysql/
安裝
windows:
點點點
Linux:
yum -y install mysql mysql-server mysql-devel
3.1顯示數據庫,顯示表
SHOW DATABASES; SHOW TABLES;
默認數據庫:
mysql - 用戶權限相關數據
test - 用於用戶測試數據
information_schema - MySQL本身架構相關數據
3.2 用戶授權
1)用戶管理:
創建用戶 create user '用戶名'@'IP地址' identified by '密碼'; 刪除用戶 drop user '用戶名'@'IP地址'; 修改用戶 rename user '用戶名'@'IP地址'; to '新用戶名'@'IP地址';; 修改密碼 set password for '用戶名'@'IP地址' = Password('新密碼') PS:用戶權限相關數據保存在mysql數據庫的user表中,所以也可以直接對其進行操作(不建議)
2)授權管理:
show grants for '用戶'@'IP地址' -- 查看權限 grant 權限 on 數據庫.表 to '用戶'@'IP地址' -- 授權 revoke 權限 on 數據庫.表 from '用戶'@'IP地址' -- 取消權限
3)對於權限:

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 由復制從屬使用
4)對於數據庫:

對於目標數據庫以及內部其他: 數據庫名.* 數據庫中的所有 數據庫名.表 指定數據庫中的某張表 數據庫名.存儲過程 指定數據庫中的存儲過程 *.* 所有數據庫
5)對於用戶和IP:

用戶名@IP地址 用戶只能在改IP下才能訪問 用戶名@192.168.1.% 用戶只能在改IP段下才能訪問(通配符%表示任意) 用戶名@% 用戶可以再任意IP下訪問(默認IP地址為%)
6)示例:
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'
四、表操作
4.1創建表
create table 表名( 列名 類型 是否可以為空, 列名 類型 是否可以為空 )

是否可空,null表示空,非字符串 not null - 不可空 null - 可空

默認值,創建列時可以指定默認值,當插入數據時如果未主動設置,則自動添加默認值 create table tb1( nid int not null defalut 2, num int not null )

自增,如果為某列設置自增列,插入數據時無需設置此列,默認將自增(表中只能有一個自增列) 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;

主鍵,一種特殊的唯一索引,不允許有空值,如果主鍵使用單個列,則它的值必須唯一,如果是多列,則其組合必須唯一。 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) )

外鍵,一個特殊的索引,只能是指定內容 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) )
4.2 刪除表
drop table 表名
4.3 清空表
delete from 表名 truncate table 表名
4.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;
4.5基本數據類型
MySQL的數據類型大致分為:數值、時間和字符串
http://www.runoob.com/mysql/mysql-data-types.html
五、基本操作
5.1增

insert into 表 (列名,列名...) values (值,值,值...) insert into 表 (列名,列名...) values (值,值,值...),(值,值,值...) insert into 表 (列名,列名...) select (列名,列名...) from 表
5.2 刪

delete from 表 delete from 表 where id=1 and name='alex'
5.3 改

update 表 set name = 'alex' where id>1
5.4 查

select * from 表 select * from 表 where id > 1 select nid,name,gender as gg from 表 where id > 1
5.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
六、PyMySQL或MySQLdb
pymsql是Python中操作MySQL的模塊,其使用方法和MySQLdb幾乎相同。
6.1 下載安裝:
pip3 install pymysql
6.2 使用
1、執行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()
2、獲取新創建數據自增ID
#!/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.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() # 獲取最新自增ID new_id = cursor.lastrowid
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') # 相對絕對位置移動
4、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()
七、sqlalchemy
SQLAlchemy是Python編程語言下的一款ORM框架,該框架建立在數據庫API之上,使用關系對象映射進行數據庫操作,簡言之便是:將對象轉換成SQL,然后使用數據API執行SQL並獲取執行結果。
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
7.1 底層處理
使用 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:123@127.0.0.1:3306/t1", max_overflow=5) # 執行SQL # cur = engine.execute( # "INSERT INTO hosts (host, color_id) VALUES ('1.1.1.22', 3)" # ) # 新插入行自增ID # cur.lastrowid # 執行SQL # cur = engine.execute( # "INSERT INTO hosts (host, color_id) VALUES(%s, %s)",[('1.1.1.22', 3),('1.1.1.221', 3),] # ) # 執行SQL # cur = engine.execute( # "INSERT INTO hosts (host, color_id) VALUES (%(host)s, %(color_id)s)", # host='1.1.1.99', color_id=3 # ) # 執行SQL # cur = engine.execute('select * from hosts') # 獲取第一行數據 # cur.fetchone() # 獲取第n行數據 # cur.fetchmany(3) # 獲取所有數據 # cur.fetchall()
7.2 ORM功能使用
使用 ORM/Schema Type/SQL Expression Language/Engine/ConnectionPooling/Dialect 所有組件對數據進行操作。根據類創建對象,對象轉換成SQL,執行SQL。
sqlalchemy不會幫你創建數據庫,要自己創建數據庫。
sqlalchemy要么創建表,要么刪除表,默認不支持修改表結構,得下載sqlalchemy第三方的工具,才能修改。
在django的orm里可以修改表結構。
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:123@127.0.0.1:3306/t1", max_overflow=5) # 建立數據庫連接 Base = declarative_base() # 創建base module # 創建單表 class Users(Base): # 創建類,要繼承Base __tablename__ = 'users' # 定義的表名 id = Column(Integer, primary_key=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) caption = Column(String(50), default='red', unique=True) class Person(Base): __tablename__ = 'person' nid = Column(Integer, primary_key=True) name = Column(String(32), index=True, nullable=True) favor_id = Column(Integer, ForeignKey("favor.nid")) # 和favor表的id做了外鍵關聯 # 多對多 class Group(Base): __tablename__ = 'group' id = Column(Integer, primary_key=True) name = Column(String(64), unique=True, nullable=False) 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')) # 外鍵到server表的id group_id = Column(Integer, ForeignKey('group.id')) # 外鍵到group表的id def init_db(): Base.metadata.create_all(engine) # 只要執行這一句,就會自動找base所有的子類,根據這些子類把表批量創建出來。 def drop_db(): Base.metadata.drop_all(engine) # 表示批量刪除所有base的子類創建的表
ForeignKeyConstraint(['other_id'], ['othertable.other_id']),
2、操作表
創建session搞上會話。
通過session來操作數據庫。
如果想往表里添加數據的話,用
session.add()
或者
session.addall()
就從某個表里加了數據
舉例說明:
Session = sessionmaker(bind=engine) session = Session() obj = Users(name="qiaomei", extra='qm') #創建一個記錄,就是創建一個對象 session.add(obj) #把對象加到session里 session.add_all([ Users(name="qiaomei1", extra='qm'), Users(name="qiaomei2", extra='qm'), ]) session.commit()
如果是多個條件,且的話,就在filter里加上逗號,添加多個條件。
session.query(Users).filter(Users.id > 2,Users.name='qiaomei').delete()
session.commit()

#!/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:123@127.0.0.1:3306/t1", max_overflow=5) Base = declarative_base() # 創建單表 class Users(Base): __tablename__ = 'users' id = Column(Integer, primary_key=True) name = Column(String(32)) extra = Column(String(16)) __table_args__ = ( UniqueConstraint('id', 'name', name='uix_id_name'), Index('ix_id_name', 'name', 'extra'), ) def __repr__(self): return "%s-%s" %(self.id, self.name) # 一對多 class Favor(Base): __tablename__ = 'favor' nid = Column(Integer, primary_key=True) caption = Column(String(50), default='red', unique=True) def __repr__(self): return "%s-%s" %(self.nid, self.caption) class Person(Base): __tablename__ = 'person' nid = Column(Integer, primary_key=True) name = Column(String(32), index=True, nullable=True) favor_id = Column(Integer, ForeignKey("favor.nid")) # 與生成表結構無關,僅用於查詢方便 favor = relationship("Favor", backref='pers') # 多對多 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')) group = relationship("Group", backref='s2g') server = relationship("Server", backref='s2g') class Group(Base): __tablename__ = 'group' id = Column(Integer, primary_key=True) name = Column(String(64), unique=True, nullable=False) port = Column(Integer, default=22) # 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) def init_db(): Base.metadata.create_all(engine) def drop_db(): Base.metadata.drop_all(engine) Session = sessionmaker(bind=engine) session = Session()
增

obj = Users(name="qiaomei0", extra='qm') session.add(obj) session.add_all([ Users(name="qiaomei1", extra='qm'), Users(name="qiaomei2", extra='qm'), ]) session.commit()
刪

session.query(Users).filter(Users.id > 2,Users.name='qiaomei').delete() session.commit()
改

session.query(Users).filter(Users.id > 2).update({"name" : "099"}) 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()
查

ret = session.query(Users).all() print(type(ret[0])) # <class '__main__.Users'> ret = session.query(Users.name, Users.extra).all() ret = session.query(Users).filter_by(name='qiaomei').all() ret = session.query(Users).filter_by(name='qiaomei').first() ''' 打印結果: ret1: [1-qiaomei0, 2-qiaomei1, 3-qiaomei2] 注意:打印的是User類對象列表。(一條記錄對應着一個User對象) ret2: [('qiaomei0', 'qm'), ('qiaomei1', 'qm'), ('qiaomei2', 'qm')] ret3: [] ret4: None '''
# 如果想查看生成的sql語句是什么,就不加all()
q = session.query(Users) print(q) # SELECT users.id AS users_id, users.name AS users_name, users.extra AS users_extra FROM users
其他

# 條件 ret = session.query(Users).filter_by(name='alex').all() ret = session.query(Users).filter(Users.id > 1, Users.name == 'eric').all() # filtwr里有逗號,條件是並且的關系 ret = session.query(Users).filter(Users.id.between(1, 3), Users.name == 'eric').all() ret = session.query(Users).filter(Users.id.in_([1,3,4])).all() # id是1,3,4其中一個 ret = session.query(Users).filter(~Users.id.in_([1,3,4])).all() # id不在,~是否的意思 ret = session.query(Users).filter(Users.id.in_(session.query(Users.id).filter_by(name='eric'))).all() # id在一個數組中,但是要先執行里面的sql from sqlalchemy import and_, or_ ret = session.query(Users).filter(and_(Users.id > 3, Users.name == 'eric')).all() # and 表示都是並且關系 ret = session.query(Users).filter(or_(Users.id < 2, Users.name == 'eric')).all() ret = session.query(Users).filter( # 內部是and連接,得出一個布爾值,然后外面再用or or_( Users.id < 2, and_(Users.name == 'eric', 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() # 首先按照User表倒序排列,如果有相同,再排序 # 分組 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() # 按照name分組,並且取其他列(id列)的最大,總數,最小值。 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() # having是group_by的條件過濾 # 連表 ret = session.query(Users, Favor).filter(Users.id == Favor.nid).all() # 直接讓兩個表進行連接 # 直接讓兩個表聯合。這里join默認是innerjoin,這里沒有寫他們的對應關系,它們在內部自己找。 # 它是怎么找的呢,在創建表的時候,有類型是foreignkey,是根據它來找的。 ret = session.query(Person).join(Favor).all() ret = session.query(Person).join(Favor, isouter=True).all() # isouter=True表示leftjoin。沒有rightjoin,如果想rightjoin,替換表寫的位置。 # 組合 q1 = session.query(Users.name).filter(Users.id > 2) q2 = session.query(Favor.caption).filter(Favor.nid < 2) ret = q1.union(q2).all() # 把q1.q2兩個聯合的全部取到,union會幫你去重 q1 = session.query(Users.name).filter(Users.id > 2) q2 = session.query(Favor.caption).filter(Favor.nid < 2) ret = q1.union_all(q2).all() # union_all不會幫你去重
7.3 sqlalchemy單表查詢
1)__repr__ 指定打印對象顯示內容
def __repr__ 通過這樣定義,print Users對象的時候,就打印出需要顯示的內容。
如果是django,如果是python2.7,就是__unicode__方式,如果是python3,就是__str__方式。
ret = session.query(Users).filter_by(name='qiaomei').all()
print ret 這時候顯示出的是__repr__打印的結果組成的數組。
__repr__對數據庫查詢,一點作用都沒有,只是print對象有用。
2)獲取屬性
ret = session.query(Users).filter_by(name='qiaomei').all()
print ret[0].name就是獲取名字。
這種方式是直接獲取屬性值
q1 = session.query(Users.name).filter(Users.id > 2)
print q1
打印:[('qiaomei1',),('qiaomei2',)]
2)建立外鍵關聯
ForeignKey,就加上外鍵約束
avor_id = Column(Integer, ForeignKey("favor.nid"))

# 創建單表 class Users(Base): __tablename__ = 'users' id = Column(Integer, primary_key=True) name = Column(String(32)) extra = Column(String(16)) __table_args__ = ( UniqueConstraint('id', 'name', name='uix_id_name'), Index('ix_id_name', 'name', 'extra'), ) def __repr__(self): return "%s-%s" %(self.id, self.name)
7.4 聯表查詢-一對多-low方式
4.1)默認是innerjoin。
# 直接讓兩個表聯合。這里join默認是innerjoin,這里沒有寫他們的對應關系,它們在內部自己找。
# 它是怎么找的呢,在創建表的時候,有類型是foreignkey,是根據它來找的。
ret = session.query(Person).join(Favor).all()
相當於sql語句:
兩個表通過on,來關聯
sql = session.query(Person).join(Favor)
print(sql)
'''
inner join打印sql,只打印person表所有字段信息,不打印favor表
SELECT person.nid AS person_nid, person.name AS person_name, person.favor_id AS person_favor_id
FROM person JOIN favor ON favor.nid = person.favor_id
'''
4.2)isouter=True就是left join
ret1 = session.query(Person).join(Favor,isouter=True).all()
sql1 = session.query(Person).join(Favor,isouter=True)
print(sql1)
'''
打印sql,只打印person表所有字段的信息,但是沒有打印favor表
SELECT person.nid AS person_nid, person.name AS person_name, person.favor_id AS person_favor_id
FROM person LEFT OUTER JOIN favor ON favor.nid = person.favor_id
'''
4.3)兩張表的信息都打印出來
ret2 = session.query(Person,Favor).join(Favor,isouter=True).all() print(ret2) sql2 = session.query(Person,Favor).join(Favor,isouter=True) print(sql2) ''' left join,打印結果:打印person和favor兩張表的所有字段。 [(<__main__.Person object at 0x0000000003B34FD0>, 1-white), (<__main__.Person object at 0x0000000003B69BE0>, 2-blue), (<__main__.Person object at 0x0000000003B69C50>, 2-blue)] left join,打印sql:打印person和favor兩張表的所有字段。 SELECT person.nid AS person_nid, person.name AS person_name, person.favor_id AS person_favor_id, favor.nid AS favor_nid, favor.caption AS favor_caption FROM person LEFT OUTER JOIN favor ON favor.nid = person.favor_id '''
4.4)聯表,只打印某些字段
聯表,上面的都是打印對象,而現在我們要獲取指定字段的值。
ret3 = session.query(Person.name,Favor.caption).join(Favor,isouter=True).all() print(ret3) sql3 = session.query(Person.name,Favor.caption).join(Favor,isouter=True) print(sql3) ''' left join,打印結果:某些指定字段值 [('qiaomei0', 'white'), ('qiaomei1', 'blue'), ('qiaomei2', 'blue')] left join,打印sql:某些指定字段值 SELECT person.name AS person_name, favor.caption AS favor_caption FROM person LEFT OUTER JOIN favor ON favor.nid = person.favor_id '''
7.5 聯表查詢-一對多-推薦方式
1)正向查詢
正向查詢指的是:多對一,多的那端開始查,也就是foreignkey寫在哪里,從哪里查。
使用上面的方法非常麻煩,我們用更高效的方法。
只要在表里加上這一句話:
favor = relationship("Favor", backref='pers')
class Person(Base): __tablename__ = 'person' nid = Column(Integer, primary_key=True) name = Column(String(32), index=True, nullable=True) favor_id = Column(Integer, ForeignKey("favor.nid")) # 與生成表結構無關,僅用於查詢方便 favor = relationship("Favor", backref='pers') # obj代指的是Person表的每一行數據 # obj.favor代指favor對象,obj.favor.nid就拿到了Person關聯的favor對象的id。 # 所以你不用做聯表,它內部幫你做聯表。 ret = session.query(Person).all() for obj in ret: # 每個obj就是一行數據。 print(obj.nid,obj.name,obj.favor_id,obj.favor,obj.favor.nid,obj.favor.caption)
'''
打印結果:
Person的id, name, favor_id,favor對象,favor的id,caption
1 qiaomei0 1 1-white 1 white
2 qiaomei1 2 2-blue 2 blue
3 qiaomei2 2 2-blue 2 blue
'''
2)反向查詢
反向查詢指的是:多對一,從一的那端開始查,也就是從沒寫foreignkey的表里反查。
# 多對一,從一的那端反查。
# Person和Favor是多對一,假如查詢喜歡藍色的所有人。Favor的caption為blue的所有對應的Person
# 傳統方式,反向查詢: ret3 = session.query(Person.name,Favor.caption).join(Favor,isouter=True).filter(Favor.caption == 'blue').all()
Person表里,寫了backref='pers',就相當於在favor表里加了個字段pers
favor = relationship("Favor", backref='pers')
class Person(Base): __tablename__ = 'person' nid = Column(Integer, primary_key=True) name = Column(String(32), index=True, nullable=True) favor_id = Column(Integer, ForeignKey("favor.nid")) # 與生成表結構無關,僅用於查詢方便 favor = relationship("Favor", backref='pers') class Favor(Base): __tablename__ = 'favor' nid = Column(Integer, primary_key=True) caption = Column(String(50), default='red', unique=True) # Person表里寫的:backref='pers',相當於在這里加上字段pers。只是用於查詢,不會修改表結構。 # pers = 。。。。。。。。 def __repr__(self): return "%s-%s" %(self.nid, self.caption)
你可以直接通過Favor對象的pers字段找到跟這個顏色關聯的所有person。
在數據庫里沒有真實的字段對應的,只是幫你生成sql語句而已。
# 新方式,反向查詢 obj = session.query(Favor).filter(Favor.caption=='blue').first() # 先找到caption為blue的Favor對象 print(obj.nid) print(obj.caption) print(obj.pers) ''' 打印結果: 2 blue [<__main__.Person object at 0x0000000003B5BBE0>, <__main__.Person object at 0x0000000003B5BC50>] '''
3)總結
Foreignkey和relationship要成對寫在一個表里。
class Person(Base): __tablename__ = 'person' nid = Column(Integer, primary_key=True) name = Column(String(32), index=True, nullable=True) favor_id = Column(Integer, ForeignKey("favor.nid")) # 與生成表結構無關,僅用於查詢方便 favor = relationship("Favor", backref='pers')
Person對Favor 是多對一的關系,foreignkey加在了多的那端(Person表)。
Person對象.favor.favor的字段:叫做正向查找
Favor對象.pers.person的字段:反向查找
7.6 SQLAlchemy表創建過程
我們通過寫個類來創建表。
兩個步驟:
1)把這個類轉為table對象。
2)根據這個table對象生成sql語句(create table。。。)。
一個類就是一張表。
用對象來創建表也可以,但是都不這么用。
class Person(Base): __tablename__ = 'person' nid = Column(Integer, primary_key=True) name = Column(String(32), index=True, nullable=True) favor_id = Column(Integer, ForeignKey("favor.nid")) # 與生成表結構無關,僅用於查詢方便 favor = relationship("Favor", backref='pers')
八 sqlalchemy聯表查詢:多對多
8.1 聯表查詢多對多結構
#!/usr/bin/env python # -*- coding:utf-8 -*- __author__ = 'WangQiaomei' 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/s13", max_overflow=5) Base = declarative_base() # 主機 class Host(Base): __tablename__ = 'host' nid = Column(Integer,primary_key=True,autoincrement=True) hostname = Column(String(32)) port = Column(String(32)) ip = 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')) def init_db(): Base.metadata.create_all(engine) def drop_db(): Base.metadata.drop_all(engine) # init_db() Session = sessionmaker(bind=engine) session = Session() session.add_all([ Host(hostname="c1",port="22",ip="1.1.1.1"), Host(hostname="c2",port="22",ip="1.1.1.2"), Host(hostname="c3",port="22",ip="1.1.1.3"), Host(hostname="c4",port="22",ip="1.1.1.4"), Host(hostname="c5",port="22",ip="1.1.1.5"), ]) session.add_all([ HostUser(username="root"), HostUser(username="db"), HostUser(username="nb"), HostUser(username="sb"), ]) session.commit() 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()
8.2 聯表查詢
主機: 1 c1 2 c2 3 c3 服務器用戶: 1 root 2 nb 3 db 4 sb 關系表 nid 主機ID 服務器ID 1 1 1 2 1 3 2 2 2 4 。。。。。。。。。。。。 約束:外鍵
1) 傳統方式
如果我們想查:主機c1上有幾個用戶。
通過傳統的方式應該:
1)通過主機表:找到c1對應的主機id。
2)通過第三張關聯表查詢:主機id對應的幾個用戶id。
3)通過用戶表:找到這些用戶id,對應的用戶名。
# 需求來了。。。
# 獲取主機c1的所有的用戶
# 需求來了。。。 # 獲取主機c1的所有的用戶 # 1 獲取c1主機對象 host_obj = session.query(Host).filter(Host.hostname=='c1').first() # host_obj.nid # 我們只取映射,不取對象。如果取的是對象,還得循環對象列表,然后取出所有的nid。 # 2 跟c1關聯的所有用戶id host_2_host_user=session.query(HostToHostUser.nid).filter(HostToHostUser.host_id == host_obj.nid).all() print(host_2_host_user) # 打印:[(1,), (2,), (3,)] # 我們拿到的是用戶id,是個集合。接下來要找集合下任意一個的所有用戶。應該用in操作 # 3 獲取用戶名 users = session.query(HostUser.username).filter(HostUser.nid.in_([1,2])).all() # 這樣把id為1,2的所有用戶都拿到了。 print(users) # [('root',), ('db',)] # 我們怎樣把[(1,), (2,), (3,)] 轉換為 [1,2,3],用zip r=zip(*host_2_host_user) print(r) # print(list(r)[0]) # [(1, 2, 3)] ==> (1, 2, 3) # 元組或列表放在這里都可以 users = session.query(HostUser.username).filter(HostUser.nid.in_(list(r)[0])).all() # list(r)[0] 這個zip對象是迭代器,取一次就沒了。 print(users) # [('root',), ('db',), ('nb',)] 獲取了主機c1的所有的用戶
2) 新方式1
host和HostUser是兩張表,HostToHostUser是第三張表,是多對多關系表。
這個第三張表都有foreignkey關聯着這兩張表。
Host 和HostToHostUser是一對多的關系
HostUser和HostToHostUser是一對多的關系
兩個一對多就變成多對多了。
所以我們可以把relationship在HostToHostUser這張表里寫一遍。
添加下面兩行:
# 多對多表 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")
# 新方式
host_obj=session.query(Host).filter(Host.hostname=='c1').first() print(host_obj.nid) print(host_obj.hostname) # 主機c1對應的第三張表的對象(通過反查) # [<__main__.HostToHostUser object at 0x0000000003CBB470>, <__main__.HostToHostUser object at 0x0000000003CBB438>, # <__main__.HostToHostUser object at 0x0000000003CBB5F8>] print(host_obj.h) # 因為foreignkey寫在第三張表上,所以主機或用戶表來查第三張表就是反查,如果是第三張表查主機或用戶就是正向查找。 # 循環獲取第三張表的對象 for item in host_obj.h: print(item.host_user.username) ''' 打印: root db nb '''
3) 新方式2[推薦]
多對多最終推薦方式:
A 關系(B,AB.__table__) AB ==> fk, B
AB.__table__就表示第三張表的對象。
更改方式:
1)把HostToHostUser類放在Host前面
2)去掉HostToHostUser的所有relationship
3)Host表加上relationship,關聯HostUser表和第三張表的對象
# 多對多表 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(32)) port = Column(String(32)) ip = Column(String(32)) host_user = relationship("HostUser", secondary=HostToHostUser.__table__, backref="h")
多對多就非常簡單了:
host_obj=session.query(Host).filter(Host.hostname=='c1').first() print(host_obj.host_user) ''' 打印結果:找到了c1所有關聯的主機用戶名 [<__main__.HostUser object at 0x0000000003B18C50>, <__main__.HostUser object at 0x0000000003B18940>, <__main__.HostUser object at 0x0000000003B18BA8>] '''