mysql和SQLAlchemy


mysqlSQLAlchemy

一、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地址為%)
對於用戶和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
View Code

5.2

        delete from 表
        delete from 表 where id=1 and name='alex'
View Code

5.3

update 表 set name = 'alex' where id>1
View Code 

5.4

        select * from 表
        select * from 表 where id > 1
        select nid,name,gender as gg from 表 where id > 1
View Code  

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
View Code

 

六、PyMySQLMySQLdb

pymsqlPython中操作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') # 相對絕對位置移動

4fetch數據類型

  關於默認獲取的數據是元祖類型,如果想要或者字典類型的數據,即:

#!/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第三方的工具,才能修改。

djangoorm里可以修改表結構。

 

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()
View Code

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

session.commit()
View Code

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()
View Code 

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

'''
View Code

# 如果想查看生成的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不會幫你去重
View Code

 

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.2isouter=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)總結

Foreignkeyrelationship要成對寫在一個表里。

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') 

PersonFavor 是多對一的關系,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

hostHostUser是兩張表,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>]
'''

  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM