【问题标题】:SQLAlchemy Nested CTE QuerySQLAlchemy 嵌套 CTE 查询
【发布时间】:2020-02-19 03:20:27
【问题描述】:

sqlalchemy 核心查询构建器似乎取消嵌套 CTE 查询并将其重新定位到已编译 sql 的“顶部”。

我正在转换一个现有的 Postgres 查询,该查询选择深度连接的数据作为单个 JSON 对象。语法相当做作,但它显着减少了大型查询的网络开销。目标是使用 sqlalchemy 核心查询构建器动态构建查询。

这是一个嵌套 CTE 的最小工作示例

with res_cte as (
    select
        account_0.name acct_name,
        (
            with offer_cte as (
                select
                    offer_0.id
                from
                    offer offer_0
                where
                    offer_0.account_id = account_0.id
            )
            select
                array_agg(offer_cte.id)
            from
                offer_cte
        ) as offer_arr
    from
        account account_0
)
select 
    acct_name::text, offer_arr::text
from res_cte

结果

acct_name,  offer_arr
---------------------
oliver,     null
rachel,     {3}
buddy,      {4,5}

(我的错误使用)核心查询构建器尝试取消嵌套 offer_cte 并导致每个 offer.id 与结果中的每个 account_name 相关联。

没有必要在答案中重新实现这个确切的查询,任何导致类似嵌套 CTE 的示例都是完美的。

【问题讨论】:

  • 你用的是什么版本的 postgresql?
  • 不是您要的,而是用子查询替换无偿的 CTE 以避免该问题。也更快。

标签: python sql postgresql sqlalchemy


【解决方案1】:

我刚刚实现了嵌套 cte 功能。它应该在 1.4.24 版本中登陆。

拉取请求:https://github.com/sqlalchemy/sqlalchemy/pull/6709

import sqlalchemy as sa
from sqlalchemy.ext.declarative import declarative_base

# Model declaration
Base = declarative_base()

class Offer(Base):
    __tablename__ = "offer"
    
    id = sa.Column(sa.Integer, primary_key=True)
    account_id = sa.Column(sa.Integer, nullable=False)
    
class Account(Base):
    __tablename__ = "account"

    id = sa.Column(sa.Integer, primary_key=True)
    name = sa.Column(sa.TEXT, nullable=False)

# Query construction
account_0 = sa.orm.aliased(Account)

# Watch the nesting keyword set to True
offer_cte = (
    sa.select(Offer.id)
    .where(Offer.account_id == account_0.id)
    .select_from(Offer)
    .correlate(account_0).cte("offer_cte", nesting=True)
)
offer_arr = sa.select(sa.func.array_agg(offer_cte.c.id).label("offer_arr"))

res_cte = sa.select(
    account_0.name.label("acct_name"),
    offer_arr.scalar_subquery().label("offer_arr"),
).cte("res_cte")

final_query = sa.select(
    sa.cast(res_cte.c.acct_name, sa.TEXT),
    sa.cast(res_cte.c.offer_arr, sa.TEXT),
)

它构造这个查询,返回你期望的结果:

WITH res_cte AS 
(
    SELECT
        account_1.name AS acct_name
        , (
            WITH offer_cte AS 
(
                SELECT
                    offer.id AS id
                FROM
                    offer
                WHERE
                    offer.account_id = account_1.id
            )
            SELECT
                array_agg(offer_cte.id) AS offer_arr
            FROM
                offer_cte
        ) AS offer_arr
    FROM
        account AS account_1
)
 SELECT
    CAST(res_cte.acct_name AS TEXT) AS acct_name
    , CAST(res_cte.offer_arr AS TEXT) AS offer_arr
FROM
    res_cte

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-08-16
    • 2018-08-22
    • 1970-01-01
    • 1970-01-01
    • 2012-07-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多