简体   繁体   中英

SQLAlchemy: Use column from outer query in subquery

My issue

I have this (MSSQL) query which I'd like to convert to SQLAlchemy:

SELECT * FROM Artikel AS root WHERE (SELECT COUNT(*) FROM Artikel WHERE Artikel.MM_ParentID = root.ROW_ID) = 0 AND MM_ParentID = 0;

This is what I tried:

root = sqlalchemy.orm.aliased(GSDArtikel, name='root')
parent_count_query = gsd_session.query(sqla.func.count()).select_from(GSDArtikel).filter(GSDArtikel.MM_ParentID == root.row_id)
results = gsd_session.query(root).filter((parent_count_query == 0) & (root.MM_ParentID == 0))

However, parent_count_query == 0 generates a bool, which makes this fail:

TypeError: unsupported operand type(s) for &: 'bool' and 'BinaryExpression'

parent_count_query has the type sqlalchemy.orm.query.Query .

I also tried adding .subquery() which changes the type to sqlalchemy.sql.selectable.Alias , but still gives me False when comparing it.

When I print the generated inner query, it doesn't quite look like I'd expect it to:

SELECT count(*) AS count_1 
FROM [Artikel], [Artikel] AS root 
WHERE [Artikel].[MM_ParentID] = root.row_id

I also tried using sqla.and_(parent_count_query == 0, root.MM_ParentID == 0) instead of & - then I don't get a TypeError , but instead get the following SQL for results :

SELECT ...
FROM [Artikel] AS root 
WHERE 0 = 1

What am I doing wrong?

The big picture

I have a table with root and children rows, which basically looks like this:

| row_id | MM_ParentID |
------------------------
| 1      | 0           |
| 2      | 0           |
| 3      | 0           |
| 4      | 1           |
| 5      | 1           |
| 6      | 3           |

What I'm trying to find is all rows which are parents ( MM_ParentID == 0 ) and have no children (subquery which gets all children with MM_ParentID equal to the row_id of the current item returns 0 rows). So in this case, the item with row_id 2 would be returned.

Your problem is that you cannot build a subquery in sqlalchemy and then just compare the result as you would do in pure SQL .

Anyway, if I've understood correctly your problem, I would tackle it in a different way; first fetching the Articles that do have a parent,

In [39]:
from sqlalchemy.orm import aliased
root = aliased(Article, name='root')
subq = session.query(Article.id).select_from(Article).filter(Article.parent_id == root.id)
print(subq)

SELECT article.id AS article_id 
FROM article, article AS root 
WHERE article.parent_id = root.id

And then looking for the root articles like these:

In [40]:
from sqlalchemy import tuple_
results = session.query(Article).filter(~Article.id.in_(subq))
print(results)

SELECT article.id AS article_id, article.parent_id AS article_parent_id 
FROM article 
WHERE article.id NOT IN (SELECT article.id AS article_id 
FROM article AS root 
WHERE article.parent_id = root.id)

For sure there are other ways; this is the simpler I've figured out, probably not optimal if your table is very big. (And sorry that I've used different spelling for your table and column names!)

Hope it helps.

I ended up simplifying the query so it joins the children to the parents and then gets the ones where the child IS NULL :

stray_parents = (gsd_session.query(item)
                 .outerjoin(child, item.row_id == child.MM_ParentID)
                 .filter(item.MM_ParentID == 0, child.row_id == None))

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM