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