2015-11-23 15 views
1

假设我有一个activity表和一个subscription表。每个活动都有一个对其他对象的通用引用数组,每个订阅对同一组中的某个其他对象有一个通用引用。加入SQLAlchemy中的设置返回函数(SRF)和访问列表

CREATE TABLE activity (
    id serial primary key, 
    ob_refs UUID[] not null 
); 

CREATE TABLE subscription (
    id UUID primary key, 
    ob_ref UUID, 
    subscribed boolean not null 
); 

我想用设置返回函数unnest的加入,使我能找到的“最深切的”匹配的预订,像这样:

SELECT id 
FROM (
    SELECT DISTINCT ON (activity.id) 
     activity.id, 
     x.ob_ref, x.ob_depth, 
     subscription.subscribed IS NULL OR subscription.subscribed = TRUE 
      AS subscribed, 
    FROM activity 

    LEFT JOIN subscription 
     ON activity.ob_refs @> array[subscription.ob_ref] 

    LEFT JOIN unnest(activity.ob_refs) 
     WITH ORDINALITY AS x(ob_ref, ob_depth) 
     ON subscription.ob_ref = x.ob_ref 

    ORDER BY x.ob_depth DESC 
) sub 
WHERE subscribed = TRUE; 

但我想不出该怎么办第二次加入并访问列。我试图创建一个FromClauselike this

act_ref_t = (sa.select(
     [sa.column('unnest', UUID).label('ob_ref'), 
     sa.column('ordinality', sa.Integer).label('ob_depth')], 
     from_obj=sa.func.unnest(Activity.ob_refs)) 
    .suffix_with('WITH ORDINALITY') 
    .alias('act_ref_t')) 

... 

query = (query 
    .outerjoin(
     act_ref_t, 
     Subscription.ob_ref == act_ref_t.c.ob_ref)) 
    .order_by(activity.id, act_ref_t.ob_depth) 

但是导致这种SQL与另一个子查询:

LEFT OUTER JOIN (
     SELECT unnest AS ob_ref, ordinality AS ref_i 
     FROM unnest(activity.ob_refs) WITH ORDINALITY 
    ) AS act_ref_t 
    ON subscription.ob_refs @> ARRAY[act_ref_t.ob_ref] 

...这失败,因为失踪和unsupportedLATERAL关键字:

有一个表“活动”的条目,但它不能从这部分查询中引用。

那么,如何在不使用子查询的情况下为此SRF创建JOIN子句?还是有什么我失踪?

编辑1使用sa.textTextClause.columns代替sa.select让我拉近了许多:

act_ref_t = (sa.sql.text(
     "unnest(activity.ob_refs) WITH ORDINALITY") 
    .columns(sa.column('unnest', UUID), 
      sa.column('ordinality', sa.Integer)) 
    .alias('act_ref')) 

但由于它包裹在括号中的条款产生的SQL失败:

LEFT OUTER JOIN (unnest(activity.ob_refs) WITH ORDINALITY) 
    AS act_ref ON subscription.ob_ref = act_ref.unnest 

错误是syntax error at or near ")"。我可以得到TextAsFrom不能用圆括号包裹吗?

回答

2

事实证明这不是SA直接支持的,但正确的行为可以通过ColumnClauseFunctionElement来实现。首先导入this recipe,如zzzeekthis SA issue中所述。然后创建一个特殊unnest功能,其中包括WITH ORDINALITY修改:

class unnest_func(ColumnFunction): 
    name = 'unnest' 
    column_names = ['unnest', 'ordinality'] 

@compiles(unnest_func) 
def _compile_unnest_func(element, compiler, **kw): 
    return compiler.visit_function(element, **kw) + " WITH ORDINALITY" 

然后,您可以用它连接,排序等类似这样的:

act_ref = unnest_func(Activity.ob_refs) 
query = (query 
    .add_columns(act_ref.c.unnest, act_ref.c.ordinality) 
    .outerjoin(act_ref, sa.true()) 
    .outerjoin(Subscription, Subscription.ob_ref == act_ref.c.unnest) 
    .order_by(act_ref.c.ordinality.desc()))