---
# System prepended metadata

title: SQLAlchemy 2.0(04)查詢資料(1)
tags: [python, sqlalchemy]

---

# 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)說明。