在 SQLAlchemy 连接子句中使用函数输出
Using function output in SQLAlchemy join clause
我正在尝试将相当短的 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)
这会为时间序列创建子select,但会产生数据库错误:
There is an entry for table "places", but it cannot be referenced from this part of the query.
所以我的问题是:您将如何在 sqlalchemy 中执行此操作。此外,我对这很困难的想法持开放态度,因为我使用 SQL 的方法是愚蠢的。
问题是给定的查询结构 SQLAlchemy 产生了一个查询
SELECT ...
FROM places,
(...) AS date_list LEFT OUTER JOIN count ON ... AND count."placeID" = places.id
...
有 2 个 FROM
-列表项:places
和连接。项目不能相互引用 1,因此 ON
子句中的 places.id
导致错误。
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
.
我正在尝试将相当短的 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)
这会为时间序列创建子select,但会产生数据库错误:
There is an entry for table "places", but it cannot be referenced from this part of the query.
所以我的问题是:您将如何在 sqlalchemy 中执行此操作。此外,我对这很困难的想法持开放态度,因为我使用 SQL 的方法是愚蠢的。
问题是给定的查询结构 SQLAlchemy 产生了一个查询
SELECT ...
FROM places,
(...) AS date_list LEFT OUTER JOIN count ON ... AND count."placeID" = places.id
...
有 2 个 FROM
-列表项:places
和连接。项目不能相互引用 1,因此 ON
子句中的 places.id
导致错误。
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
.