# SQLAlchemy 2.0(07)查詢資料(4-union and other operator) ###### tags: `python` `sqlalchemy` [官方文件](https://docs.sqlalchemy.org/en/20/tutorial/data_select.html) 有時候我們的查詢並不單單是一個來源,有多個,然後我們希望可以垂直堆疊把資料整合起來,這可以利用`union`與`union all`。兩者最主要的差異在前者排除重覆資料,後者則是全部呈現。 ## union ```python= from sqlalchemy import union_all stmt1 = select(user_table).where(user_table.c.name == "sandy") stmt2 = select(user_table).where(user_table.c.name == "spongebob") u = union_all(stmt1, stmt2) with engine.connect() as conn: result = conn.execute(u) print(result.all()) ``` ``` SELECT user_account.id, user_account.name, user_account.fullname FROM user_account WHERE user_account.name = ? UNION ALL SELECT user_account.id, user_account.name, user_account.fullname FROM user_account WHERE user_account.name = ? ``` 這很單純的利用函數`union_all`把兩個`select`結合起來。我們也可以把這個結合起來的查詢`u`做為一個子查詢的操作: ```python= u_subq = u.subquery() stmt = ( select(u_subq.c.name, address_table.c.email_address) .join_from(address_table, u_subq) .order_by(u_subq.c.name, address_table.c.email_address) ) with engine.connect() as conn: result = conn.execute(stmt) print(result.all()) ``` ``` SELECT anon_1.name, address.email_address FROM address JOIN (SELECT user_account.id AS id, user_account.name AS name, user_account.fullname AS fullname FROM user_account WHERE user_account.name = ? UNION ALL SELECT user_account.id AS id, user_account.name AS name, user_account.fullname AS fullname FROM user_account WHERE user_account.name = ?) AS anon_1 ON anon_1.id = address.user_id ORDER BY anon_1.name, address.email_address ``` ## exists `exists`也是一資料庫查詢常用的作法,有人說有索引就用`exists`,沒索引就用`in`,再不然就直接`inner join`,反正青菜蘿蔔都有人說好,就自己體驗一下吧: ```python= subq = ( select(func.count(address_table.c.id)) .where(user_table.c.id == address_table.c.user_id) .group_by(address_table.c.user_id) .having(func.count(address_table.c.id) > 1) ).exists() with engine.connect() as conn: result = conn.execute(select(user_table.c.name).where(subq)) print(result.all()) ``` ``` SELECT user_account.name FROM user_account WHERE EXISTS (SELECT count(address.id) AS count_1 FROM address WHERE user_account.id = address.user_id GROUP BY address.user_id HAVING count(address.id) > ?) ``` 範例主要是先做一個聚合操作,利用`count`統計數量,然後用`having`做個條件排除,最後搭配`.exists()`弄成一個子查詢,在查詢的時候把這個子查詢做為條件`where`的參數。如果你想要的是NOT EXISTS的話,就把這個子查詢加個小蚯蚓變成`.where(~subq)`就可以了。 ## func 最後提到,多數我們平常操作資料庫常用的函數都是在`func`裡面,所以你可以用`func.upper`、`func.lower`...真找不到的情況下就來看看官方文件,相信會有所收獲的。