2017-04-27 3 views
2

Y at-il un moyen de faire SQLAlchemy générer une requête avec une colonne personnalisée qui est une sous-requête qui est en corrélation avec la ligne actuelle:Générer sql avec sous-requête comme une colonne dans l'instruction select en utilisant SQLAlchemy

SELECT 
tab1.id, 
tab1.col1, 
..., 
(
    SELECT count(1) FROM tab2 
    WHERE tab2.tab1_id = tab1.id 
    GROUP BY tab2.col1 
) as cnt 
FROM tab1 
WHERE ... 
LIMIT 100 

en utilisant l'API ORM ?

session.query(Tab1, ?(subquery for additional column)?).filter(...).limit(100) 

J'utilise PostgreSQL 9.3 et la version ancienne de SQLAlchemy 0.9.8

+1

Vous avez besoin d'une sous-requête scalaire. ['Query.label()'] (http://docs.sqlalchemy.org/en/latest/orm/query.html#sqlalchemy.orm.query.Query.label) ou 'Query.as_scalar()' obtiendra vous que. Lisez aussi sur [sélections scalaires] (http://docs.sqlalchemy.org/en/latest/core/tutorial.html#scalar-selects) et corréler les sous-requêtes de la documentation Core. –

Répondre

2

Si vous en avez souvent besoin et/ou si le décompte fait partie intégrante de votre modèle Tab1, vous devez utiliser une propriété hybride telle que décrite dans l'autre réponse. Si d'autre part, vous avez besoin de ce juste pour une seule requête, alors vous pouvez créer le sous-requête scalaire en utilisant Query.label() ou Query.as_scalar():

count_stmt = session.query(func.count(1)).\ 
    filter(Tab2.tab1_id == Tab1.id).\ 
    group_by(Tab2.col1).\ 
    label('cnt') 

session.query(Tab1, count_stmt).filter(...).limit(100) 

Le sous-requête automatically correlate ce qu'il peut de la requête englobante.

1

Vous pouvez le faire, mais il fonctionne d'une manière tout à fait différente de la façon dont vous l'avez écrit. Vous pouvez créer une propriété de Tab1 qui dépend de la relation à tab2 (en supposant que tab2.tab1_id est une clé étrangère, qu'elle devrait être

Vos modèles ressemblent à ceci:.

class Parent(Base): 
    __tablename__ = 'parent' 
    id = Column(Integer, primary_key=True) 
    children = relationship("Child") 

class Child(Base): 
    __tablename__ = 'child' 
    id = Column(Integer, primary_key=True) 
    parent_id = Column(Integer, ForeignKey('parent.id')) 

selon the docs on relationships

Ensuite, vous pouvez ajouter quelque chose comme

@hybrid_property 
def number_of_children(self): 
    if self.children: 
     return len(self.children) 
    return 0 

@number_of_children.expression 
def number_of_children(cls): 
    return (select([func.count(Child.id)]) 
      .where(Child.cover_id == cls.id)) 

au modèle parent, selon this answer et more docs. Une fois que vous avez fait cela, vous pouvez filtrer sur cette propriété de la même manière que n'importe quelle autre colonne.