# SQLAlchemy 2.0(03)寫入資料 ###### tags: `python` `sqlalchemy` [官方文件](https://docs.sqlalchemy.org/en/20/index.html) 從[SQLAlchemy 2.0(02)建置資料表](https://hackmd.io/@shaoeChen/rJJvzRH1h)中已經知道如何建置資料表。目前現代系統中都免不了要跟資料庫有查詢、寫入、編輯、刪除的動作,就一起跟著官方文件的範例來看在Core與ORM的情況下如何處理。 ## insert ```python= from sqlalchemy import insert stmt = insert(user_table).values(name="spongebob", fullname="Spongebob Squarepants") ``` * 第2行的部份就只是生成一個表達式,還沒有真正的寫入 我們可以列印一下`stms`看看裡面有什麼: ``` INSERT INTO user_account (name, fullname) VALUES (:name, :fullname) ``` 可以發現到結果有兩個是參數的形態,這部份要利用`compile`才能取得: ```python= >>> compiled = stmt.compile() >>> compiled.params {'name': 'spongebob', 'fullname': 'Spongebob Squarepants'} ``` 有資料庫語法之後就可以在上下文中執行這個語法: ```python= with engine.connect() as conn: result = conn.execute(stmt) conn.commit() ``` 寫入的結果我們可以利用屬性`inserted_primary_key`來取得寫入的pk value: ```python= >>> result.inserted_primary_key (1,) ``` 不過我們並不是一定要先產生一個資料庫語法`stms`再來執行,其實我們是可以直接在上下文中執行的: ```python= with engine.connect() as conn: result = conn.execute( insert(user_table), [ {"name": "sandy", "fullname": "Sandy Cheeks"}, {"name": "patrick", "fullname": "Patrick Star"}, ], ) conn.commit() ``` 回想[SQLAlchemy 2.0(01)概觀](https://hackmd.io/@shaoeChen/B1CJAsHJ3)裡面的範例所採用的方法是利用`text`來硬刻語法,而這邊是利用`insert`。 官方給出一個比較進階的範例: ```python= from sqlalchemy import select, bindparam scalar_subq = ( select(user_table.c.id) .where(user_table.c.name == bindparam("username")) .scalar_subquery() ) print(scalar_subq) ``` ``` (SELECT user_account.id FROM user_account WHERE user_account.name = :username) ``` 列印出這個subquery可以看的到`user_account.name`是一個參數`:username`。然後我們再利用這個subquery來做資料的寫入: ```python= with engine.connect() as conn: result = conn.execute( insert(address_table).values(user_id=scalar_subq), [ { "username": "spongebob", "email_address": "spongebob@sqlalchemy.org", }, {"username": "sandy", "email_address": "sandy@sqlalchemy.org"}, {"username": "sandy", "email_address": "sandy@squirrelpower.org"}, ], ) conn.commit() ``` 上面的程式碼會得到下面的結果,比較能明白是在做什麼: ``` BEGIN (implicit) INSERT INTO address (user_id, email_address) VALUES ((SELECT user_account.id FROM user_account WHERE user_account.name = ?), ?) [...] [('spongebob', 'spongebob@sqlalchemy.org'), ('sandy', 'sandy@sqlalchemy.org'), ('sandy', 'sandy@squirrelpower.org')] COMMIT ``` 最上面的範例有提到,寫入之後會回傳寫入的索引值,針對要回傳什麼的這個部份是可以定義的,只需要加上`returing`: ```python= insert_stmt = insert(address_table).returning( address_table.c.id, address_table.c.email_address ) print(insert_stmt) ``` 下面結果可以確認: ``` INSERT INTO address (id, user_id, email_address) VALUES (:id, :user_id, :email_address) RETURNING address.id, address.email_address ``` 也可以搭配`from_select`做資料的寫入: ```python= select_stmt = select(user_table.c.id, user_table.c.name + "@aol.com") insert_stmt = insert(address_table).from_select( ["user_id", "email_address"], select_stmt ) print(insert_stmt.returning(address_table.c.id, address_table.c.email_address)) ``` 生成的資料庫語法如下: ``` INSERT INTO address (user_id, email_address) SELECT user_account.id, user_account.name || :name_1 AS anon_1 FROM user_account RETURNING address.id, address.email_address ``` 看起來千變萬化的操作,可能不是很好理解,所以搭配所生成的資料庫語法應該還是可以有一定的理解才是。