简体   繁体   English

SQLAlchemy的UNION作为CTE缺少外部括号

[英]SQLAlchemy's UNION as CTE missing outer parentheses

I try to use SQLAlchemy to generate a query like 我尝试使用SQLAlchemy生成类似的查询

WITH
_cte_a AS (SELECT x FROM some_table),
_cte_b AS (SELECT x FROM next_table),
_cte_c AS (
    SELECT x FROM _cte_a
    UNION ALL
    SELECT x FROM _cte_b)
SELECT *
FROM _cte_c;

(I know that this very query does not really make sense in terms of the common table expressions, but just let it be my desired output.) (我知道这个查询对于通用表表达式而言并没有什么意义,而只是让它成为我想要的输出。)

Using SQLAlchemy's selectables , my code looks like this: 使用SQLAlchemy的selectables ,我的代码如下所示:

import sqlalchemy as sa

cte_a = sa.select(
    [sa.column('x')]
).select_from(some_table).cte('_cte_a')

cte_b = sa.select(
    [sa.column('x')]
).select_from(next_table).cte('_cte_b')

cte_c = sa.union_all(cte_a, cte_b).cte('_cte_c')

# Query with session context; unavailable before.
query = session.query('*').select_from(cte_c)

But the generated output is: 但是生成的输出是:

WITH
_cte_a AS (SELECT x FROM some_table),
_cte_b AS (SELECT x FROM some_table),
_cte_c AS
    SELECT x FROM _cte_a
    UNION ALL
    SELECT x FROM _cte_b
SELECT *
FROM _cte_c;

Note the missing parentheses around the CTE definition in the query result. 请注意查询结果中CTE定义周围缺少的括号。 For some reason, SQLAlchemy refuses to create them when building the query part for a CTE that contains a CompoundSelect object as its element. 出于某种原因,在为包含CompoundSelect对象作为其元素的CTE构建查询部分时,SQLAlchemy拒绝创建它们。

I found a solution to a similar problem that proposes using session.query(...).subquery() and then combining their respective results using sa.union_all(...) , but as I do not have the session available, I cannot follow this approach. 我找到了一个解决类似问题的方法,该方法建议使用session.query(...).subquery() ,然后使用sa.union_all(...)合并各自的结果,但是由于没有可用的会话,我无法遵循这种方法。

I've tried like all possibilities (using alias() , enforcing parens using order_by() , and many more) but nothing yields the desired result. 我已经尝试了所有可能性(使用alias() ,使用order_by()强制执行parens等等),但是没有任何方法可以产生期望的结果。 I even replaced SQLCompiler.visit_compound_select method to enforce using parentheses (it is called recursively and does not place parens under certain circumstances). 我什至替换了SQLCompiler.visit_compound_select方法以使用括号强制执行(它被递归调用,在某些情况下不会放置括号)。

Nothing helped, and I'm already biting my table. 没有任何帮助,我已经在咬我的桌子了。 Maybe I am missing something very basic? 也许我缺少一些非常基本的东西? I would very much appreciate it if someone of you could push me into the correct direction. 如果你们中的某人可以将我推向正确的方向,我将不胜感激。

Edit: Also tried select() on the inner CTEs before UNION ing them, still no success. 编辑:还试图select()在内的CTE前UNION荷兰国际集团他们,仍然没有成功。 Even when I nest the UNION deeper into selects, the result remains the same. 即使当我将UNION更深地嵌套到选择中时,结果仍然相同。

Edit (2): This is really weird. 编辑(2):这真的很奇怪。 Breaking down my (rather complex) original code to a MWE, the parentheses have switched. 将我的(相当复杂的)原始代码分解为MWE,括号已切换。 Now, they are missing for regular CTEs, but are available for the UNION'ed one: 现在,常规CTE缺少它们,但UNION版本可用:

meta = sa.MetaData()
t_some = sa.Table('some_table', meta, sa.Column('x', sa.Integer))
t_next = sa.Table('next_table', meta, sa.Column('x', sa.Integer))

cte_a = sa.select([
    t_some.c.x.label('x')
]).select_from(
    t_some
).cte('_cte_a')

cte_b = sa.select([
    t_next.c.x.label('x')
]).select_from(
    t_next
).cte('_cte_b')

cte_c = sa.select([
    sa.column('x')
]).select_from(
    sa.union_all(
        cte_a, cte_b
    )
).cte('_cte_c')

query = session.query(
    sa.select([sa.column('x')]).select_from(cte_c)
).order_by(
    sa.column('x').asc()
)
print(query)

... yields the query: ...产生查询:

WITH _cte_a AS 
SELECT some_table.x AS x 
FROM some_table, 
_cte_b AS 
SELECT next_table.x AS x 
FROM next_table, 
_cte_c AS 
(SELECT x 
FROM (_cte_a UNION ALL _cte_b))
 SELECT x AS x 
FROM (SELECT x 
FROM _cte_c) ORDER BY x ASC

Note that now the parentheses are missing for _cte_a and _cte_b . 请注意,现在_cte_a_cte_b缺少括号。

I really feel like I am missing something very basic here... 我真的觉得我在这里错过了一些非常基本的东西...

There are some unnecessarily verbose constructs and the union_all() call is wrong, which seems to throw the compiler off. 有一些不必要的冗长构造,并且union_all()调用是错误的,这似乎使编译器无法正常运行。 Instead of CTE instances pass it Select instances. 而不是CTE实例传递它Select实例。 You'd also usually pass Session.query() entities to select, not queries: 通常,您还需要传递Session.query()实体进行选择,而不是查询:

In [57]: cte_a = select([t_some.c.x]).cte('cte_a')

In [58]: cte_b = select([t_next.c.x]).cte('cte_b')

In [59]: cte_c = select([cte_a.c.x]).union_all(select([cte_b.c.x])).cte('cte_c')

In [60]: session.query(cte_c.c.x).order_by(cte_c.c.x)
Out[60]: <sqlalchemy.orm.query.Query at 0x7f7a5998e400>

In [61]: print(_)
WITH cte_a AS 
(SELECT some_table.x AS x 
FROM some_table), 
cte_b AS 
(SELECT next_table.x AS x 
FROM next_table), 
cte_c AS 
(SELECT cte_a.x AS x 
FROM cte_a UNION ALL SELECT cte_b.x AS x 
FROM cte_b)
 SELECT cte_c.x AS cte_c_x 
FROM cte_c ORDER BY cte_c.x

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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