sqlalchemy:关闭声明性多态连接?

发布于 2024-09-17 06:19:07 字数 1314 浏览 4 评论 0原文

sqlalchemy 有没有办法在单个查询中关闭声明式的多态连接加载?大多数时候这很好,但我有:

class A(Base) : 
   discriminator = Column('type', mysql.INTEGER(1), index=True, nullable=False)
   __mapper_args__ = { 'polymorphic_on' : discriminator }
   id = Column(Integer, primary_key=True)
   p = Column(Integer)

class B(A) : 
   __mapper_args__ = { 'polymorphic_identity' : 0 }
   id = Column(Integer, primary_key=True)
   x = Column(Integer)

class C(A) : 
   __mapper_args__ = { 'polymorphic_identity' : 1 }
   id = Column(Integer, primary_key=True)
   y = Column(String)

我想进行一个查询,以便获得 Bx > 的所有 A.ids。 10,如果 A 实际上是 B,或者 Cy == 'blah',如果 A 实际上是 C,则全部按 p 排序。

为了迭代地做到这一点,我从第一部分开始 - “如果 A 实际上是 B,则获取 Bx > 10 的所有 A.id”。所以我想我应该从外部连接开始:

session.query(A.id).outerjoin((B, B.id == A.id)).filter(B.x > 10)

...除了似乎没有办法避免让outerjoin((B, B.id == A.id))子句生成A中所有内容的完整连接子选择中 B 中的所有内容。如果 B 不继承自 A,那么这种情况就不会发生,所以我认为是多态声明性代码生成做到了这一点。有办法把它关掉吗?或者强制外连接做我想做的事?

我想要的是这样的:

select a.id from A a left outer join B b on b.id == a.id where b.x > 10

但我得到的是这样的:

select a.id from A a left outer join (select B.id, B.x, A.id from B inner join A on B.id == A.id)

...顺便说一句,如果不可能,那么后者的效率是否比前者低? SQL 引擎实际上会执行该内连接,还是会忽略它?

Is there a way in sqlalchemy to turn off declarative's polymorphic join loading, in a single query? Most of the time it's nice, but I have:

class A(Base) : 
   discriminator = Column('type', mysql.INTEGER(1), index=True, nullable=False)
   __mapper_args__ = { 'polymorphic_on' : discriminator }
   id = Column(Integer, primary_key=True)
   p = Column(Integer)

class B(A) : 
   __mapper_args__ = { 'polymorphic_identity' : 0 }
   id = Column(Integer, primary_key=True)
   x = Column(Integer)

class C(A) : 
   __mapper_args__ = { 'polymorphic_identity' : 1 }
   id = Column(Integer, primary_key=True)
   y = Column(String)

I want to make a query such that I get all A.ids for which B.x > 10, if that A is actually a B, or where C.y == 'blah', if that A is actually a C, all ordered by p.

To do it iteratively, I'm starting just with the first part - "get all A.id for which B.x > 10 if that A is actually a B". So I thought I would start with an outer join:

session.query(A.id).outerjoin((B, B.id == A.id)).filter(B.x > 10)

... except there seems to be no way to avoid having that outerjoin((B, B.id == A.id)) clause generate a full join of everything in A to everything in B within a subselect. If B doesn't inherit from A, then that doesn't happen, so I'm thinking it's the polymorphic declarative code generation that does that. Is there a way to turn that off? Or to force the outerjoin to do what I want?

What I want is something like this:

select a.id from A a left outer join B b on b.id == a.id where b.x > 10

but instead I get something like:

select a.id from A a left outer join (select B.id, B.x, A.id from B inner join A on B.id == A.id)

... as an aside, if it's not possible, then is the latter less efficient than the former? Will the sql engine actually perform that inner join, or will it elide it?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

闻呓 2024-09-24 06:19:07

您可以尝试单独为每个子类构建查询,然后将它们联合在一起。查询 B.id 时,SQLAlchemy 隐式加入超类并返回 A.id,因此采用 B.id 的选择并集C.id 仅返回单个列。

>>> b_query = session.query(B.id).filter(B.x > 10)
>>> c_query = session.query(C.id).filter(C.y == 'foo')
>>> print b_query.union(c_query)
SELECT anon_1."A_id" AS "anon_1_A_id" 
FROM (SELECT "A".id AS "A_id" 
FROM "A" JOIN "B" ON "A".id = "B".id 
WHERE "B".x > ? UNION SELECT "A".id AS "A_id" 
FROM "A" JOIN "C" ON "A".id = "C".id 
WHERE "C".y = ?) AS anon_1

您仍然会得到一个子选择,但只有一个连接“层” - 外部选择只是重命名列。

You could try building the queries for each subclass individually, then unioning them together. When querying B.id, SQLAlchemy implicitly joins the superclass and returns A.id instead, so taking the union of selects for B.id and C.id only returns a single column.

>>> b_query = session.query(B.id).filter(B.x > 10)
>>> c_query = session.query(C.id).filter(C.y == 'foo')
>>> print b_query.union(c_query)
SELECT anon_1."A_id" AS "anon_1_A_id" 
FROM (SELECT "A".id AS "A_id" 
FROM "A" JOIN "B" ON "A".id = "B".id 
WHERE "B".x > ? UNION SELECT "A".id AS "A_id" 
FROM "A" JOIN "C" ON "A".id = "C".id 
WHERE "C".y = ?) AS anon_1

You still get a subselect, but only a single "layer" of joins - the outer select is just renaming the column.

爱本泡沫多脆弱 2024-09-24 06:19:07

您应该使用 with_polymorphic()而不是outerjoin(),它似乎返回了预期的结果:

session.query(A).with_polymorphic(B).filter(B.x > 10).all()
# BEGIN
# SELECT "A".type AS "A_type", "A".id AS "A_id", "A".p AS "A_p", "B".id AS "B_id", "B".x AS "B_x" 
# FROM "A" LEFT OUTER JOIN "B" ON "A".id = "B".id 
# WHERE "B".x > ?
# (10,)
# Col ('A_type', 'A_id', 'A_p', 'B_id', 'B_x')

相比:

session.query(A.id).outerjoin((B, B.id == A.id)).filter(B.x > 10)
# BEGIN
# SELECT "A".id AS "A_id" 
# FROM "A" LEFT OUTER JOIN (SELECT "A".type AS "A_type", "A".id AS "A_id", "A".p AS "A_p", "B".id AS "B_id", "B".x AS "B_x" 
# FROM "A" JOIN "B" ON "A".id = "B".id) AS anon_1 ON anon_1."A_id" = "A".id 
# WHERE anon_1."B_x" > ?
# (10,)
# Col ('A_id',)

我用来测试这个的代码,以防有人想测试这个简洁的SQLAlchemy:

#!/usr/bin/env python
import logging
from sqlalchemy import *
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

Base = declarative_base()

class A(Base) :
   __mapper_args__ = { 'polymorphic_on' : discriminator }
   __tablename__ = 'A'

   id = Column(Integer, primary_key=True)
   discriminator = Column('type', Integer, index=True, nullable=False)
   p = Column(Integer)

class B(A) :
   __mapper_args__ = { 'polymorphic_identity' : 0 }
   __tablename__ = 'B'

   id = Column(Integer, ForeignKey('A.id'), primary_key=True)
   x = Column(Integer)

class C(A) :
   __mapper_args__ = { 'polymorphic_identity' : 1 }
   __tablename__ = 'C'

   id = Column(Integer, ForeignKey('A.id'), primary_key=True)
   y = Column(String)

meta = Base.metadata
meta.bind = create_engine('sqlite://')
meta.create_all()

Session = sessionmaker()
Session.configure(bind=meta.bind)
session = Session()

log = logging.getLogger('sqlalchemy')
log.addHandler(logging.StreamHandler())
log.setLevel(logging.DEBUG)

session.query(A.id).outerjoin((B, B.id == A.id)).filter(B.x > 10).all()
session.query(A).with_polymorphic(B).filter(B.x > 10).all()

我在Python 2.7上用SQLAlchemy 0.6.4运行了这个。

You should use with_polymorphic() instead of outerjoin(), which seems to return the expected results:

session.query(A).with_polymorphic(B).filter(B.x > 10).all()
# BEGIN
# SELECT "A".type AS "A_type", "A".id AS "A_id", "A".p AS "A_p", "B".id AS "B_id", "B".x AS "B_x" 
# FROM "A" LEFT OUTER JOIN "B" ON "A".id = "B".id 
# WHERE "B".x > ?
# (10,)
# Col ('A_type', 'A_id', 'A_p', 'B_id', 'B_x')

Compared to:

session.query(A.id).outerjoin((B, B.id == A.id)).filter(B.x > 10)
# BEGIN
# SELECT "A".id AS "A_id" 
# FROM "A" LEFT OUTER JOIN (SELECT "A".type AS "A_type", "A".id AS "A_id", "A".p AS "A_p", "B".id AS "B_id", "B".x AS "B_x" 
# FROM "A" JOIN "B" ON "A".id = "B".id) AS anon_1 ON anon_1."A_id" = "A".id 
# WHERE anon_1."B_x" > ?
# (10,)
# Col ('A_id',)

The code I used to test this, in case anybody wants to test this neat bit of SQLAlchemy:

#!/usr/bin/env python
import logging
from sqlalchemy import *
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

Base = declarative_base()

class A(Base) :
   __mapper_args__ = { 'polymorphic_on' : discriminator }
   __tablename__ = 'A'

   id = Column(Integer, primary_key=True)
   discriminator = Column('type', Integer, index=True, nullable=False)
   p = Column(Integer)

class B(A) :
   __mapper_args__ = { 'polymorphic_identity' : 0 }
   __tablename__ = 'B'

   id = Column(Integer, ForeignKey('A.id'), primary_key=True)
   x = Column(Integer)

class C(A) :
   __mapper_args__ = { 'polymorphic_identity' : 1 }
   __tablename__ = 'C'

   id = Column(Integer, ForeignKey('A.id'), primary_key=True)
   y = Column(String)

meta = Base.metadata
meta.bind = create_engine('sqlite://')
meta.create_all()

Session = sessionmaker()
Session.configure(bind=meta.bind)
session = Session()

log = logging.getLogger('sqlalchemy')
log.addHandler(logging.StreamHandler())
log.setLevel(logging.DEBUG)

session.query(A.id).outerjoin((B, B.id == A.id)).filter(B.x > 10).all()
session.query(A).with_polymorphic(B).filter(B.x > 10).all()

I ran this on Python 2.7 with SQLAlchemy 0.6.4.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文