2017-09-17

mLab 雲端資料庫與 Python 之 CRUD – II. 修改/刪除篇

對資料庫最常見也最主要的處理,就是進行資料的「新增」、「修改」、「刪除」、「查詢」等動作,簡稱為資料庫的CRUD,讓我們繼續「修改」和「刪除」吧…



接續上一篇《mLab 雲端資料庫與 Python 之 CRUD - I. 新增/查詢篇》,接下來要介紹的是mLab雲端資料庫CRUD - C(create 新增)R(read 讀取/查詢)U(update 更新/修改)D(delete 刪除)中的「修改」和「刪除」。

U (update 更新/修改)
Update 修改,也被稱為更新,是將資料庫裡已存在「資料表(collection)」中的「資料(document)」的內容(更精確的說法應該是「資料欄位(field))作更新/修改,要注意在mLab _id 這個資料欄位是主鍵(primarykey),是不可修改的。
PyMongo中提供了3種修改方法 update_one()update_many()replace_one()update_one() 是修改一筆資料,update_many() 是修改多筆資料,replace_one() 是整筆替換。前面說過,update是修改已存在的資料,因此修改時要輸入條(condition),才知道那些資料(符合條件者)要修改。

update方法有3個比較重要的參數:
UpdateResult = update(filter, update, upsert)
l   filter (必要):就是條件(condition),符合此條件的資料才會被修改。
l   update (必要):是要修改的動作,要把那個資料欄位的資料修改成什麼。
l   upsert (非必要):如果都沒有資料符合條件,是不是要新增一筆資料,預設為否(False)。這也是mLab資料庫特別之處,若是標準SQL語言,沒有這樣的處理方式,update若沒有符合的資料,就不會有動作,一定要存在的資料才能修改。
l   UpdateResult 修改結果,是修改執行後的回傳值,記錄了有幾筆符合條件的資料,有幾筆資料被修改。

就來實際試試看吧,這次我們使用IPython,一樣由Windows開始功能表中開啟IPython

先確定是否已有pymongo套件?因為pymongo不是Anaconda Distribution中預設安裝的套件,所以得開啟Anaconda Prompt安裝pymongo。使用命令 conda install pymongo,輸入y,同意安裝。

完成pymongo安裝,我們回到IPython中,因為pymongo已安裝在套件中,以後使用不需再import pymongo了。先建立資料庫連線,取得資料表。

# 建立 MongoDB 資料庫連線
from pymongo import MongoClient
myClient = MongoClient("Your_MongoDB_URI")
myDB = myClient.mlabtest01
myCollection = myDB.coll01


先看看coll01資料表中的資料,用find() 全部讀出來。

# 顯示所有資料
for myDocs in myCollection.find():
    print(myDocs)

對照一下mLab中的資料表內容,當然都一致囉。


◇ 單筆修改 update_one()
先把資料apple的價格改為250,條件是Name = apple,修改動作是Price=250,這裡我們需用到 $set 運算子去修改資料,再全部讀出確認結果,程式如下:

# 把apple的價格改為250,並全部讀出
myResult = myCollection.update_one({"name":"apple"}, {"$set":{"price":250}})
for myDocs in myCollection.find():
   print(myDocs)

真的修改成250了。upsert這個參數是非必要的,預設是否,所以若條件不符,就不會有任何動作。指定給myResult的原因為,update_one() 執行後會回傳 updateResult 修改結果,可以用如下程式來看修改結果。

myResult.matched_count  # 有幾筆符合條件的資料
myResult.modified_count  # 有幾筆資料被修改


結果都是1筆。這時,您是否心想,廢話有幾筆符合條件的資料,當然就會有幾筆資料被修改,這兩個數字一定一樣的呀。有沒有可能會不一樣?如果我們使用update_one() 方法,只會修改一筆資料,但有兩筆以上的資料符合條件,那會怎麼樣呢?我們來試試這時候價格是250的資料有兩筆了,一筆是apple,一筆是Apricot,可以用find() 來確認。

# 找出價格是250的資料
for myDocs in myCollection.find({"price":250}):
   print(myDocs)

接下來我們用update_one()把其中一筆的價格變成251,同時再列出全部確認資料修改,再查看matched_countmodified_count

# 用update_one() 將價格由250改成251,再列出全部
myResult = myCollection.update_one({"price":250}, {"$set":{"price":251}})
for myDocs in myCollection.find():
   print(myDocs)
myResult.matched_count  # 有幾筆符合條件的資料
myResult.modified_count  # 有幾筆資料被修改


matched_countmodified_count您會看到都是1,但只有第一筆符合條件的資料 apple,價格被修改成251了,第二筆 Apricot也符合條件,並沒有被修改。因此您可以知道fine_one()方法會修改找到符合條件的第一筆資料,修改它,然後就結束了,所以matched_count會等於modified_count
那到底會不會有matched_count不等於modified_count的時候呢?有的,如果您使用多筆修改,而有些待修正的資料剛好就是您要修正的資料時,修改就不用被執行,這時候matched_count就會比modified_count大了。

◇ 多筆修改 update_many()
接下來試試多筆修改 update_many(),我們先列出價格大於200的資料。

# 查詢價格大於200的資料
for myDocs in myCollection.find({"price": {"$gt": 200}}):
   print(myDocs)


3筆,我們用update_many()把這3筆資料的庫存stock都改成10,並把這3筆再列出來確認修改,再查看matched_countmodified_count

# 用update_many()把這3筆資料的庫存stock都改成10
myResult = myCollection.update_many({"price":{"$gt": 200}}, {"$set":{"stock":10}})
for myDocs in myCollection.find():
   print(myDocs)
myResult.matched_count  # 有幾筆符合條件的資料
myResult.modified_count  # 有幾筆資料被修改


3筆的庫存都被修成10了,matched_countmodified_count都是3

◇ 整筆替換 replace_one()
replace_one() 是將符合條件的資料,除了 _id 欄位外,整筆完全以新資料替換。替換資料與新增功能相同可以增加、減少資料欄位,但不需有 _id 欄位,因為 _id 會不變。因此只有 replace_one() 功能,而沒有replace_many(),資料表裡沒道理有兩筆完全相同的資料。

這是目前資料表中的資料,我們將第1apple整筆替換試試,replace_one()一樣會回傳UpdateResult,第一個參數是條件,第二個參數是替換資料,執行完成後再列出資料,並查看matched_countmodified_count。程式碼如下。

# 以 replace_one() 整筆替換資料 apple
myResult = myCollection.replace_one({"name": "apple"},{"name": "Apple","unit": "box","price": 120,"stock": 20})
for myDocs in myCollection.find():
   print(myDocs)
myResult.matched_count  # 有幾筆符合條件的資料
myResult.modified_count  # 有幾筆資料被修改


可以看到apple被改成了Apple,單位是box,價格120,庫存20matched_countmodified_count都是1。我們再改一次,價格改為150,不放庫存資料,注意條件要改成Apple囉,程式碼:

# Apple資料價格改為150,不放庫存資料
myResult = myCollection.replace_one({"name": "Apple"},{"name": "Apple","unit": "box","price": 150})
for myDocs in myCollection.find():
   print(myDocs)


可以看到價格改成150了,庫存欄位資料就不見了,您可以了解replace_one() 是整筆用新資料替換掉。
PyMongo的修改方法就介紹到這,想了解update方法更多細節,可以參考Update Data with PyMongo


D (delete 刪除)
最後出場的是刪除,將資料表(collection)中的整筆資料(documents)刪除。PyMongo中提供了 delete_one() delete_many() 兩種方法,和修改一樣是將符合條件(condition)的資料整筆刪除。delete刪除方法比較簡單:

DeleteResult = delete(filter)
l   filter (必要):就是條件(condition),符合此條件的資料才會被刪除。
l   DeleteResult 刪除結果,是刪除執行後的回傳值,記錄了有幾筆資料被刪除。

◇ 單筆刪除 delete_one()
先刪除一筆資料,我們把 name=apple 這筆資料刪除,然後查看一下deleteResult,程式碼如下:

# 以 delete_one() 刪除 apple
myResult = myCollection.delete_one({"name": "apple"})
myResult.deleted_count   # 有幾筆資料被刪除


deleted_count=0,因為name錯了,應該是Apple,所以沒事發生。再一次,修正成Apple如下:

# 以 delete_one() 刪除 Apple
myResult = myCollection.delete_one({"name": "Apple"})
myResult.deleted_count   # 有幾筆資料被刪除
for myDocs in myCollection.find():
   print(myDocs)


Apple資料真的不見了,deleted_count=1

◇ 多筆刪除 delete_many()
再試試刪除多筆,價格大於200的資料有兩筆,我們將它們刪除。

# 以 delete_many() 刪除價格大於200的資料
myResult = myCollection.delete_many({"price":{"$gt": 200}})
for myDocs in myCollection.find():
   print(myDocs)
myResult.deleted_count   # 有幾筆資料被刪除


價格大於200的資料都不見了,deleted_count=2
如果我們用單筆刪除 delete_one() 但條件有多筆符合呢?這時資料中價格大於50有兩筆,我們用delete_one() 試試看。

# 查詢價格大於 50 有幾筆?
for myDocs in myCollection.find({"price": {"$gt": 50}}):
   print(myDocs)
# 刪除價格大於 50 的資料
myResult = myCollection.delete_one({"price": {"$gt": 50}})
myResult.deleted_count   # 有幾筆資料被刪除
for myDocs in myCollection.find():
   print(myDocs)


deleted_count=1,只有第1筆杏仁被刪除了,所以我們知道 delete_one() 同樣是刪除第1筆符合條件的資料,如果有多筆符合的話。
PyMongo的刪除方法及CRUD的介紹就到此結束了,您若想了解delete方法更多細節,可以參考Remove Data with PyMongo


參考資料: