sqlalchemy 入門(下)

發表於2017-05-27

建議從這裡下載這篇文章對應的.ipynb檔案和相關資源。這樣你就能在Jupyter中邊閱讀,邊測試文中的程式碼。

說明

sqlalchemy 入門(上),這篇會更詳細的講解和演示如果用SQLAlchemy完成Select, Update, Delete的操作。

準備

實驗準備內容和sqlalchemy 入門(上)的內容一致,這裡不在重複多說。

選擇

選擇全部列

選擇全部的列

id name fullname
0 1 jack Jack Jones
1 2 wendy Wendy Williams

可以用下面方法得到涉及的列名

[‘id’, ‘name’, ‘fullname’]

另一種稍繁瑣但是更通用的方法

id name fullname
0 1 jack Jack Jones
1 2 wendy Wendy Williams

選擇指定列

可以在select的list中指定需要的列

id fullname
0 1 Jack Jones
1 2 Wendy Williams

也可以用下面這種語法指定要選擇的列。好處是可以支援變數作為列名。

id fullname
0 1 Jack Jones
1 2 Wendy Williams

注意

上面的幾個例子,並沒有指定FROM條件,SQLAlchemy自動新增了FROM語句。。

修改列名

用label實現

user_id user_name
0 1 jack
1 2 wendy

選擇計算後的結果

選擇的時候也可以進行一些運算。例如字串拼接。

id fullname
0 1 Fullname:Jack Jones
1 2 Fullname:Wendy Williams

下面是數值相加的例子。

id new_id
0 1 11
1 2 12

注意

上面的兩個例子裡,都使用了’+’號,SQLAlchemy根據資料型別自動決定編譯後的結果是字串連線還是數值相加。

使用sql function

可以使用func.func_name的形式應用函式,使用的時候只需要匯入func模組,接上資料庫中的函式名即可

user_id user_name
0 1 JACK
1 2 WENDY

注意應用函式的時候,label要放在在函式之外使用,否則是無效的,這是一個容易犯的錯誤。可以看到下面的例子裡,name列採用了自動命名。

user_id upper(test.users.name)
0 1 JACK
1 2 WENDY

使用window function

使用func.func_name().over()的方式即可

id name num
0 1 jack 1
1 2 wendy 2

使用case

case_test
0 A
1 B

新增常數列

user_id constant null
0 1 AAAAAA None
1 2 AAAAAA None

指定limit and offset

例如用offset跳過1行,並且用limit只顯示2行

id email_address
0 2 jack@msn.com
1 3 www@www.org

Order By排序

下面的例子中根據user.id和user.name排序

name
0 jack
1 wendy

如果要控制升序降序的話。可以呼叫desc()方法或者使用desc函式。

name
0 wendy
1 jack

name
0 wendy
1 jack

Group By

name count
0 wendy 2
1 jack 2

如果要根據多列去做group by的話、

name count
0 jack 2
1 wendy 2

使用Having條件

name count
0 wendy 2
1 jack 2

集合操作

這裡至演示union_all。 union, except_, except_all, intersect, intersect_all同理,不再演示。

id user_id email_address
0 1 1 jack@yahoo.com

where條件

id name fullname user_id email_address
0 1 jack Jack Jones 1 jack@yahoo.com
1 1 jack Jack Jones 1 jack@msn.com
2 2 wendy Wendy Williams 2 www@www.org
3 2 wendy Wendy Williams 2 wendy@aol.com

常見的where條件

等於

大於

不等於

有些條件需要呼叫方法實現,例如表現SQL中的IN的時候,這樣是不行的

False

而是應該用object本身提供的函式in_

類似的有between

字串匹配like

特殊的算符

如果有一些非常規的operator,總是可以用.op方法取實現

邏輯連詞

最常用的有and_, or_, not_

連續多個where連用也可以起到and的效果

也可以用python的&,|,~等邏輯連線符號代替and_(), or_(), not_

join表

自動Join

如果join時沒有指定on條件。SQLAlchemy會去檢查是否存在foreign key的定義。

helper.py的reset_tables函式中,已經定義了users.id是addresses.user_id的外來鍵。所以下面的語句能自動新增test.users.id = test.addresses.user_id的條件。

注意

auto_load得到的表無法找到正確的foreign_key

看上去定義了foreign key的資訊,但是執行的話會出錯

select_from

如果要將join語句用於select的話,可以用select_from語句,功能相當於SQL中的FROM。

Left Join

如果要使用left outer join,把join換成outerjoin

Outer Join

如果要使用outer join,把join換成outerjoin,並且加上引數full = True

Alias

要把一個子查詢結果像一張表那樣被使用時,需要使用alias()給子查詢命名。

使用SQLAlchemy的時候,由於可以通過python變數名來找到正確的查詢,因此並不一定要去指定命名,SQLAlchemy會新增自動的命名。下面的例子裡,SQLAlchemy採用了自動命名anon_1

關聯子查詢

基本概念

先用一個例子演示一下關聯子查詢,這個查詢的功能是找出每個使用者的id和擁有的郵箱的個數。

這裡需要注意的是,子查詢有以下幾個特點,

  • 子查詢中使用了test.users.id,但是子查詢的FROM語句中並沒有出現test.users。
  • 子查詢返回的結果為一行,一列。

實際上,這種情況下子查詢的test.users是由外層的test.users關聯的,如果外層是id為1的user,那麼子查詢的test.users.id就是1;如果外層是id為2的user,那麼子查詢的test.users.id就是2。

為了方便理解,可以想象有一個根據傳入的user id,查詢擁有的郵箱數的函式email_count,那麼下面的例子和上面的例子是等價的。

SQLAlchemy中的實現

上面例子中的查詢方式稱為關聯子查詢,下面演示怎麼在SQLAlchemy中構造關聯子查詢。

這裡要注意關聯子查詢的結尾必須是label,label一般用於給列重新命名。 (實際上由於關聯子查詢返回一行一列,的確像是一個列。) 如果關聯子查詢最後沒有加label而是alias,那麼就不會編譯成關聯子查詢。

可以看到上面編譯後的SQL語句中,FROM語句新增了test.users。子查詢本身就構成了一個完整的查詢,統計了所有郵箱的計數,結果為固定的4。

關聯行為的調整

可以通過correlate, correlate_except指定或排除sub query中需要關聯的外層的table範圍。例如通過correlate(None)或者correlate_except(users)禁止sub query關聯外層的users

可以看到上面的例子裡,sub query中自動在From語句中補充了users,成為完成的select語句,結果也變成了固定的4

join lateral

目前只有postgresql支援。 join lateral實際上可以看做另一種形式的關聯子查詢。

join lateral語句實際上也起到了遍歷users,取出id和對應email_address個數的功能。

這段程式碼等價於

Update

最基本的update,不指定任何條件,相當於對所有的row做了遍歷

選擇單行並進行遍歷

更新資料

對全資料更新

下面是最基本的update方式,在values方法中指定資料的更新方法。

如果不加where條件對的話,這個更新邏輯會應用與所有的資料。

對單行更新

也可以用dict的方式去指定更新方式。 如果用這種方式的話, object和列名都可以作為key。

對多行進行更新

用給定的資料進行更新

可以藉助上一個教程提到過的bindparam。 編寫針對單個row的update邏輯, 但是將需要更新的多條資料用list傳入即可實現批量的更新。

不過update語句的引數數值只能在execute階段傳入,不能使用params()傳入

用另一個select結果更新資料

關鍵是通過where條件去指定一下資料來源(select結果)和待更新的表之間是怎麼的匹配的。

也可以用類似關聯子查詢的方式那樣去進行資料升級。

下面我們用每個人字典排序的第一個郵箱去替換原來的name。

和關聯子查詢一樣,用這種方式修改資料時,返回的資料也必須是單行單列。

控制列操作的順序

由於SQLAlchemy編譯update語句的時候,預設是按照列在users表中的出現順序去編譯。

id name fullname
0 1 ***** jack
1 2 ***** wendy

編譯的語句中,順序是name=’*****’, fullname=test.users.name。如果我們需要精確的去控制編譯後語句列更新的順序,可以用下面的方式

id name fullname
0 1 ***** jack
1 2 ***** wendy

現在可以看到編譯的語句是SET fullname=test.users.name, name=’*****’,反映了tuple list的順序。通過這種方式我們就可以改變編譯的SET語句操作列的順序。

注意

對於postgresql,上面兩種SET的順序結果都是一致的。但是對於MYSQL資料庫,如果編譯結果是

在執行fullname=test.users.name部分的時候,會採用”*”作為name的數值,導致最後fullname也變成’*’,只有用

才能得到正確的結果。

這時就有必要去指定SET語句中列操作的順序。

刪除資料

掌握了修改資料的操作後, 刪除的操作就非常簡單。不過要注意約束條件。例如addresses的user_id是users表的外來鍵,那麼在刪除addresses表的資料前,是不能刪除users表的資料的。

刪除全量資料

由於外來鍵造成的約束,直接刪除users表是會報錯的。

需要先刪除addresses, 再刪除users

相關文章