一、GROUP BY基本語法格式:html
GROUP BY關鍵字能夠將查詢結果按照某個字段或多個字段進行分組。字段中值相等的爲一組。基本的語法格式以下:python
GROUP BY 屬性名 [HAVING 條件表達式] [WITH ROLLUP]mysql
二、GROUP BY聯合函數使用:sql
1)GROUP BY關鍵字能夠和GROUP_CONCAT()函數一塊兒使用。數據庫
2)GROUP_CONCAT()函數會把每一個分組中指定的字段值都顯示出來。django
3)同時,GROUP BY關鍵字一般與集合函數一塊兒使用。集合函數包括COUNT()函數、SUM()函數、AVG()函數、MAX()函數和MIN()函數等。編程
4)注意:若是GROUP BY不與上述函數一塊兒使用,那麼查詢結果就是字段取值的分組狀況。字段中取值相同的記錄爲一組,可是隻顯示該組的第一條記錄。windows
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關鍵字。
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)
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)
使用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)
在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)
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中只有符合聯結條件的記錄,而右聯結相反。
假設有以下兩張表:
表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表相關聯的記錄。
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
SHOW DATABASES; SHOW TABLES;
默認數據庫:
mysql - 用戶權限相關數據
test - 用於用戶測試數據
information_schema - MySQL自己架構相關數據
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'
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) )
drop table 表名
delete from 表名 truncate table 表名
添加列: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;
MySQL的數據類型大體分爲:數值、時間和字符串
http://www.runoob.com/mysql/mysql-data-types.html
insert into 表 (列名,列名...) values (值,值,值...) insert into 表 (列名,列名...) values (值,值,值...),(值,值,值...) insert into 表 (列名,列名...) select (列名,列名...) from 表
delete from 表 delete from 表 where id=1 and name='alex'
update 表 set name = 'alex' where id>1
select * from 表 select * from 表 where id > 1 select nid,name,gender as gg from 表 where id > 1
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
pymsql是Python中操做MySQL的模塊,其使用方法和MySQLdb幾乎相同。
pip3 install pymysql
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)來移動遊標位置,如:
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是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
使用 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()
使用 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']),
二、操做表
建立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不會幫你去重
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)
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 '''
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的字段:反向查找
咱們經過寫個類來建立表。
兩個步驟:
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')
#!/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()
主機: 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 '''
多對多最終推薦方式:
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>] '''