# SQLAlchemy 2.0(04)查詢資料(1)
###### tags: `python` `sqlalchemy`
[官方文件](https://docs.sqlalchemy.org/en/20/tutorial/data_select.htmll)
select肯定是在資料庫操作上最多的一個行為,所以這個主題就會切成多篇來說明,避免一篇文章中的資訊過於大量。
## select
最標準的一種帶有條件的查詢,其表達式為`select(table).where(條件)`:
```python=
from sqlalchemy import select
stmt = select(user_table).where(user_table.c.name == "spongebob")
print(stmt)
```
```
SELECT user_account.id, user_account.name, user_account.fullname
FROM user_account
WHERE user_account.name = :name_1
```
一樣的,上面所建構的是一個語法,我們必需要執行上下文中才能實際的跟資料庫對話:
```python=
with engine.connect() as conn:
for row in conn.execute(stmt):
print(row)
```
相同的查詢如果是ORM的話,表達式是一樣的,但有些語的差異,`select(類別).where(類別.屬性)`:
```python=
stmt = select(User).where(User.name == "spongebob")
with Session(engine) as session:
for row in session.execute(stmt):
print(row)
```
兩者的執行的上下文也有所不同,Core是在`engine.connect`,而ORM則是在`Session`。更重要的是回傳的物件也會不一樣。Core的話是:
```
(1, 'spongebob', 'Spongebob Squarepants')
```
,而ORM的話則是:
```
(User(id=1, name='spongebob', fullname='Spongebob Squarepants'),)
```
上面的範例是全選,也就是`select(資料表)`,如果要指定欄位的話可以直接指定:
* `select(user_table.c.name, user_table.c.fullname)`
* `select(user_table.c["name", "fullname"])`
上面兩種表達式都可以取得一樣的結果:
```
SELECT user_account.name, user_account.fullname
FROM user_account
```
ORM在全選的時候也是類似的表達式,`select(User)`,不過當我們實際執行查詢取回資料的時候就有很多種作法了:
* `user = session.scalars(select(User)).first()`
* `row = session.execute(select(User)).first()`
官方建議的是第一種作法,利用`scalars`取得的回傳是`ScalarResult`。這應該等後面專門講到ORM的時候會有更詳細的說明。
在ORM中指定查詢欄位的表達式也非常直觀,`select(User.name, User.fullname)`:
```
SELECT user_account.name, user_account.fullname
FROM user_account
```
## label
資料庫查詢中我們可能會給欄位一個別名,這在SQLAlchemy可以利用`label`來實現:
```python
from sqlalchemy import func, cast
stmt = select(
("Username: " + user_table.c.name).label("username"),
).order_by(user_table.c.name)
with engine.connect() as conn:
for row in conn.execute(stmt):
print(f"{row.username}")
```
```
SELECT ? || user_account.name AS username
FROM user_account ORDER BY user_account.name
```
## text, literal_column
有時候我們在查詢的時候可能會加入一個內容是固定資訊的欄位,這資訊並不在原始的資料表中,這可以利用`text`來實現:
```python=
from sqlalchemy import text
stmt = select(text("'some phrase'"), user_table.c.name).order_by(user_table.c.name)
with engine.connect() as conn:
print(conn.execute(stmt).all())
```
```
SELECT 'some phrase', user_account.name
FROM user_account ORDER BY user_account.name
```
```
[('some phrase', 'patrick'), ('some phrase', 'sandy'), ('some phrase', 'spongebob')]
```
雖然`text`是可以滿足這個需求,不過官方看起來是比較建議採用`literal_column`,不同的地方在於後者明確的定義是一個欄位:
```python=
from sqlalchemy import literal_column
stmt = select(literal_column("'some phrase'").label("p"), user_table.c.name).order_by(
user_table.c.name
)
with engine.connect() as conn:
for row in conn.execute(stmt):
print(f"{row.p}, {row.name}")
```
* 第2行利用`literal_column`建置一個內容為`'some phrase'`的欄位,並且命名`"p"`
## where
條件式是資料庫互動中很重要的一環,畢竟有時候資料表的內容千千萬萬筆,也不是所有的資料都是需要的,一個優點是,SQLAlchemy讓我們可以用Python的標準運算符來做表式:
```python=
print(select(user_table).where(user_table.c.name == "squidward"))
```
```
SELECT user_account.id, user_account.name, user_account.fullname
FROM user_account
WHERE user_account.name = :name_1
```
如果要實現`AND`的話,就只要再接一次`where`就可以:
```python=
print(
select(address_table.c.email_address)
.where(user_table.c.name == "squidward")
.where(address_table.c.user_id == user_table.c.id)
)
```
```
SELECT address.email_address
FROM address, user_account
WHERE user_account.name = :name_1 AND address.user_id = user_account.id
```
也可以在`where`裡面給定多個條件說明:
```python=
print(
select(address_table.c.email_address).where(
user_table.c.name == "squidward",
address_table.c.user_id == user_table.c.id,
)
)
```
```
SELECT address.email_address
FROM address, user_account
WHERE user_account.name = :name_1 AND address.user_id = user_account.id
```
真的要用到`AND, OR`也是可以的,只要在`where`裡面放入`and_, or_`:
```python=
from sqlalchemy import and_, or_
print(
select(Address.email_address).where(
and_(
or_(User.name == "squidward", User.name == "sandy"),
Address.user_id == User.id,
)
)
)
```
```
SELECT address.email_address
FROM address, user_account
WHERE (user_account.name = :name_1 OR user_account.name = :name_2)
AND address.user_id = user_account.id
```
更多的運算符的部份可以參考[官方文件](https://docs.sqlalchemy.org/en/20/core/operators.html)說明。