SQLAlchemy的UNION作为CTE缺少外括号

时间:2017-09-29 11:27:18

标签: python postgresql sqlalchemy

我尝试使用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;

(我知道这个查询在公共表表达式方面没有意义,但只是让它成为我想要的输出。)

使用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)

但生成的输出是:

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;

请注意查询结果中CTE定义周围缺少的括号。出于某种原因,SQLAlchemy在为包含CompoundSelect对象的CTE构建查询部分时拒绝创建它们。它的元素。

我找到了类似问题的解决方案,建议使用session.query(...).subquery()然后使用sa.union_all(...)合并各自的结果,但由于我没有可用的会话,我无法遵循这种方法。

我尝试过所有可能性(使用alias(),使用order_by()执行parens等等),但没有任何结果可以产生预期效果。我甚至用SQLCompiler.visit_compound_select方法替换了强制使用括号(它被递归调用,并且在某些情况下不会放置parens)。

没有任何帮助,我已经咬了我的桌子。也许我错过了一些非常基本的东西?如果你们中的某个人能够把我推向正确的方向,我将非常感激。

编辑:在select()之前对内部CTE进行了UNION尝试,仍然没有成功。即使我将UNION更深地嵌入到选择中,结果也保持不变。

编辑(2):这真的很奇怪。将我的(相当复杂的)原始代码分解为MWE,括号已切换。现在,它们不适用于常规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)

...产生查询:

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

请注意,现在_cte_a_cte_b缺少括号。

我真的觉得我错过了非常基本的东西......

1 个答案:

答案 0 :(得分:1)

有一些不必要的详细构造,union_all()调用是错误的,这似乎抛弃了编译器。而不是CTE个实例传递它Select个实例。您通常也会通过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