使用 SQLAlchemy 按关系计数选择时出现自动别名问题 + Postgres

发布于 2025-01-10 03:43:58 字数 1902 浏览 0 评论 0原文

给出以下代码:

from sqlalchemy import Column, ForeignKey, Integer, alias, create_engine, func, select
from sqlalchemy.orm import declarative_base, relationship, sessionmaker

Base = declarative_base()
engine = create_engine(
    "postgresql+psycopg2://***:***@127.0.0.1:5432/***", future=True
)

Session = sessionmaker(engine)


class Foo(Base):
    __tablename__ = "foo"
    id = Column(Integer, primary_key=True)
    bars = relationship("Bar", uselist=True, secondary="foo_bar")
    baz_id = Column(ForeignKey("baz.id"))
    baz = relationship("Baz", back_populates="foos", lazy="joined")


class Bar(Base):
    __tablename__ = "bar"
    id = Column(Integer, primary_key=True)


class Baz(Base):
    __tablename__ = "baz"
    id = Column(Integer, primary_key=True)
    foos = relationship(Foo, uselist=True)


class FooBar(Base):
    __tablename__ = "foo_bar"
    foo_id = Column(ForeignKey(Foo.id), primary_key=True)
    bar_id = Column(ForeignKey(Bar.id), primary_key=True)


Base.metadata.create_all(engine)

stmt = (
    select(Foo)
    .join(FooBar, FooBar.foo_id == Foo.id)
    .group_by(Foo.id)
    .having(func.count(FooBar.foo_id) == 2)
)

Session().execute(stmt)

我想选择所有带有两个 BarFoo

但我遇到了以下错误:

column "baz_1.id" must appear in the GROUP BY clause or be used in an aggregate function

生成的 SQL 是:

SELECT foo.id, foo.baz_id, baz_1.id AS id_1 
FROM foo JOIN foo_bar ON foo_bar.foo_id = foo.id 
LEFT OUTER JOIN baz AS baz_1 ON baz_1.id = foo.baz_id GROUP BY foo.id 
HAVING count(foo_bar.foo_id) = :count_1

现在我明白了 Postgres 想要我做什么,但我不确定如何实现这一点,因为我无法添加 baz_1.id 到 GROUP PY 子句,因为它是 SQLAlchemy 动态生成的东西,我对它没有任何控制权。

Given the following code:

from sqlalchemy import Column, ForeignKey, Integer, alias, create_engine, func, select
from sqlalchemy.orm import declarative_base, relationship, sessionmaker

Base = declarative_base()
engine = create_engine(
    "postgresql+psycopg2://***:***@127.0.0.1:5432/***", future=True
)

Session = sessionmaker(engine)


class Foo(Base):
    __tablename__ = "foo"
    id = Column(Integer, primary_key=True)
    bars = relationship("Bar", uselist=True, secondary="foo_bar")
    baz_id = Column(ForeignKey("baz.id"))
    baz = relationship("Baz", back_populates="foos", lazy="joined")


class Bar(Base):
    __tablename__ = "bar"
    id = Column(Integer, primary_key=True)


class Baz(Base):
    __tablename__ = "baz"
    id = Column(Integer, primary_key=True)
    foos = relationship(Foo, uselist=True)


class FooBar(Base):
    __tablename__ = "foo_bar"
    foo_id = Column(ForeignKey(Foo.id), primary_key=True)
    bar_id = Column(ForeignKey(Bar.id), primary_key=True)


Base.metadata.create_all(engine)

stmt = (
    select(Foo)
    .join(FooBar, FooBar.foo_id == Foo.id)
    .group_by(Foo.id)
    .having(func.count(FooBar.foo_id) == 2)
)

Session().execute(stmt)

I want to select all Foos with exactly two Bars.

But I'm running into the following error:

column "baz_1.id" must appear in the GROUP BY clause or be used in an aggregate function

The generated SQL is:

SELECT foo.id, foo.baz_id, baz_1.id AS id_1 
FROM foo JOIN foo_bar ON foo_bar.foo_id = foo.id 
LEFT OUTER JOIN baz AS baz_1 ON baz_1.id = foo.baz_id GROUP BY foo.id 
HAVING count(foo_bar.foo_id) = :count_1

Now I get what Postgres wants me to do, but I'm not sure how to achieve this, since I can't add baz_1.id to the GROUP PY clause because it's something that SQLAlchemy generates on the fly and I don't have any control over it.

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

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

发布评论

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

评论(1

撑一把青伞 2025-01-17 03:43:58

由于 Foo 中关系的 lazy='joined' 选项,Baz 被包含在查询中。我们可以在查询中覆盖该选项,以便不执行联接并且查询按需要工作。

stmt = (
    select(Foo)
    .options(orm.lazyload(Foo.baz))  # <- don't automatically join Baz.
    .join(FooBar, FooBar.foo_id == Foo.id)
    .group_by(Foo.id)
    .having(func.count(FooBar.foo_id) == 2)
)

生成的SQL:

SELECT foo.id, foo.baz_id 
FROM foo 
JOIN foo_bar ON foo_bar.foo_id = foo.id 
GROUP BY foo.id 
HAVING count(foo_bar.foo_id) = %(count_1)s

Baz is being included in the query because of the lazy='joined' option on the relationship in Foo. We can override that option in the query, so that the join is not executed and the query works as desired.

stmt = (
    select(Foo)
    .options(orm.lazyload(Foo.baz))  # <- don't automatically join Baz.
    .join(FooBar, FooBar.foo_id == Foo.id)
    .group_by(Foo.id)
    .having(func.count(FooBar.foo_id) == 2)
)

Generated SQL:

SELECT foo.id, foo.baz_id 
FROM foo 
JOIN foo_bar ON foo_bar.foo_id = foo.id 
GROUP BY foo.id 
HAVING count(foo_bar.foo_id) = %(count_1)s
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文