在SQLAlchemy连接子句中使用函数输出

时间:2018-11-04 01:34:08

标签: python sql postgresql sqlalchemy

我正在尝试将相当短的SQL转换为sqlAlchemy ORM查询。 SQL使用Postgres的generate_series来创建一组日期,而我的目标是创建一组按列之一分类的时间序列数组。

表(简体)非常简单:

counts:
-----------------
count   (Integer)
day     (Date)
placeID (foreign key related to places)

"counts_pkey" PRIMARY KEY (day, placeID)

places:
-----------------
id
name   (varchar)

我需要的输出是每个地方的时间序列,其中包括当一天未报告计数时的空值。例如,这对应于四天的系列:

    array_agg    |    name
-----------------+-------------------
 {NULL,0,7,NULL} | A Place
 {NULL,1,NULL,2} | Some other place
 {5,NULL,3,NULL} | Yet another

我可以很容易地做到这一点,方法是在日期范围和地点上放置一个CROSS JOIN并将其与计数相结合:

SELECT array_agg(counts.count), places.name 
FROM generate_series('2018-11-01', '2018-11-04', interval '1 days') as day 
CROSS JOIN  places 
LEFT OUTER JOIN counts on counts.day = day.day AND counts.PlaceID = places.id 
GROUP BY places.name;

我似乎无法弄清楚的是如何使SQLAlchemy做到这一点。经过大量的挖掘,我发现一个old google groups thread几乎可以解决这个问题:

date_list = select([column('generate_series')])\
.select_from(func.generate_series(backthen, today, '1 day'))\ 
.alias('date_list')

time_series = db.session.query(Place.name, func.array_agg(Count.count))\
.select_from(date_list)\
.outerjoin(Count, (Count.day == date_list.c.generate_series) & (Count.placeID == Place.id ))\
.group_by(Place.name)

这会为时间序列创建子选择,但会产生数据库错误:

  

有一个用于表“ places”的条目,但是不能在查询的这一部分中引用它。

所以我的问题是:您将如何在sqlalchemy中做到这一点。另外,我很容易接受这样的想法,因为我使用SQL的方法是头脑僵硬的。

1 个答案:

答案 0 :(得分:1)

问题在于,给定查询构造SQLAlchemy会按照以下方式生成查询

SELECT ...
FROM places,
     (...) AS date_list LEFT OUTER JOIN count ON ... AND count."placeID" = places.id
...

有2个FROM列表项:places和联接。项不能互相交叉引用 1 ,因此,places.id子句中由于ON导致的错误。

SQLAlchemy不支持显式CROSS JOIN,但是另一方面,CROSS JOIN等效于INNER JOIN ON (TRUE)。您也可以省略将函数表达式包装在子查询中,并按giving it an alias的原样使用它:

date_list = func.generate_series(backthen, today, '1 day').alias('gen_day')

time_series = session.query(Place.name, func.array_agg(Count.count))\
    .join(date_list, true())\
    .outerjoin(Count, (Count.day == column('gen_day')) &
                      (Count.placeID == Place.id ))\
    .group_by(Place.name)

1 :除了函数调用FROM项或使用LATERAL的项。