1. 程式人生 > >sqlalchemy的關聯子查詢

sqlalchemy的關聯子查詢

SQLAlchemy也算是用過好幾年了,不過一直都用著其中相對簡單的一小部分,最近寫個程式碰到個問題,需要作一個關聯子查詢,類似這樣的SQL語句:

SELECT master.*, (
    SELECT count(*) 
    FROM detail 
    WHERE detail.parentid=master.id AND detail.someflag IS NOT NULL
) FROM master;

試了很久不知道怎麼用ORM來寫。

如果COUNT不為0,用下面這個查詢的結果是一樣的:

SELECT master.id, count(detail.id) 
FROM master 
INNER JOIN detail ON detail.parentid=master.id 
WHERE detail.someflag IS NOT NULL 
GROUP BY master.id;

這個語句倒是可以用ORM實現:

qry = orm.query(master, func.count(detail.id).join(detail, 
    detail.parentid==master.id).filter(detail.someflag!=None).group_by(master)

但是如果COUNT為0就不行了,即便用OUTER JOIN也不行,這種情況下會丟失COUNT為0的master記錄。

想來想去大概只能用子查詢實現,但是試了這樣的語句,結果跟上面一個是一樣的,也會丟失COUNT為0的master記錄。

subqry = orm.query(detail.parentid, func.count(detail.id).label(
    "flagcnt").filter(detail.someflag!=None).group_by(detail.parentid).subquery()
qry = orm.query(master, subqry.c.flagcnt).join(subqry, subqry.c.parentid==master.id)

這句的SQL相當於這樣:

SELECT master.id, subqry.flagcnt 
FROM master 
JOIN (
    SELECT detail.parentid, count(*) AS flagcnt 
    FROM detail 
    WHERE detail.someflag IS NOT NULL 
    GROUP BY detail.parentid
) AS subqry ON subqry.parentid=master.id

沒辦法,只好繼續搜尋,最後還是在SO上看到一個類似問題的答案而受到啟發,解決了這個困擾了一天多的問題。

subqry = orm.query(func.count(detail.id).label("flagcnt")).filter(
    detail.parentid==master.id).filter(detail.someflag!=None).correlate(master).as_scalar()
qry = orm.query(master, subqry)

其中的重點就在於correlate和as_scalar。