# 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
```
看起來千變萬化的操作,可能不是很好理解,所以搭配所生成的資料庫語法應該還是可以有一定的理解才是。