我写了一个SQL查询,我试图移植到SQLAlchemy,但收到以下错误:
sqlalchemy.exc.InvalidRequestError: Can't join table/selectable 'workflows' to itself
SQL(工作):
SELECT
w.user_id, COUNT(l.id)
FROM
logs as l
INNER JOIN
workflows as w
ON l.workflow_id = w.id
WHERE
l.type = 's'
AND l.timestamp > extract(epoch from now()) - 86400
GROUP BY
w.user_id;
SQLAlchemy(不工作):
session.query(
Workflow.user_id, func.count(Log.id)
).join(
Workflow, Workflow.id == Log.workflow_id
).where(
Log.type == 's', Log.timestamp > time.time() - 86400
).group_by(
Workflow.user_id
).all()
这是预期的输出:
+----------+---------+
| user_id | count |
+----------+---------+
| 1 | 5 |
| 2 | 10 |
+----------+---------+
我做错了什么?
答案 0 :(得分:6)
部分.query(Workflow.user_id, func.count(Log.id))
将Workflow
和Log
添加到您的查询中。第一个模型标记为主表,其他模型标记为辅助表。如果之后没有调用.join()
,则主表和辅助表都将添加到FROM
子句中。如果有.join()
的调用,它会将收到的表移动到JOIN
子句。这里重要的是.join()
只能应用于辅助表。
问题是您对.join(Workflow, Workflow.id == Log.workflow_id)
的调用会尝试将主表标记为已加入。要解决问题,您需要加入辅助表:.join(Log, Workflow.id == Log.workflow_id)
您可以添加echo=True
以查看SQLAlchemy生成的SQL。调试查询非常方便。或者您可以compile单个查询来查看生成的SQL。
答案 1 :(得分:0)
如前所述,查询的主要实体是Workflow
,因此联接正在尝试将Workflow
与其自身联接,这是不可能的,至少在没有使用别名的情况下是不可能的。
除了简单地重新排序联接之外,还可以使用Query.select_from()
来显式控制联接的左侧:
session.query(
Workflow.user_id, func.count(Log.id)
).select_from(
Log
).join(
Workflow, Workflow.id == Log.workflow_id
).filter(
Log.type == 's', Log.timestamp > time.time() - 86400
).group_by(
Workflow.user_id
).all()