Python 查询一对多和多对一的链
我目前有3个表,大致描述为以下SQLAlchemy映射:Python 查询一对多和多对一的链,python,postgresql,sqlalchemy,flask-sqlalchemy,greatest-n-per-group,Python,Postgresql,Sqlalchemy,Flask Sqlalchemy,Greatest N Per Group,我目前有3个表,大致描述为以下SQLAlchemy映射: class Task(BASE): __tablename__ = 'tasks' id = Column(Integer, primary_key=True) service_id = Column(Integer, ForeignKey('services.id')) service = relationship('Service', back_populates="tasks") upda
class Task(BASE):
__tablename__ = 'tasks'
id = Column(Integer, primary_key=True)
service_id = Column(Integer, ForeignKey('services.id'))
service = relationship('Service', back_populates="tasks")
updates = relationship("TaskUpdate")
class TaskUpdate(BASE):
__tablename__ = 'task_updates'
id = Column(Integer, primary_key=True)
external_status = Column(String(32))
external_updated_at = Column(DateTime(timezone=True))
task_id = Column(Integer, ForeignKey('tasks.id'))
task = relationship('Task', back_populates="updates")
class Service(BASE):
__tablename__ = 'services'
id = Column(Integer, primary_key=True)
client_id = Column(Integer, ForeignKey('clients.id'))
client = relationship('Client', back_populates='services')
因此,从任务到任务更新,从任务到服务,我有一对多的关系
我正在尝试创建一个查询,以获取其最新TaskUpdate by timestamp的外部状态为New(新)或Open(打开)的所有任务
以下是我得到的:
sub = SESSION.query(
TaskUpdate.task_id,
TaskUpdate.external_status.label('last_status'),
func.max(TaskUpdate.external_updated_at).label('last_update')
).group_by(TaskUpdate.task_id
).subquery()
tasks = SESSION.query(Task
).join(Service
).filter(Service.client_id == client_id
).join((sub, sub.c.task_id == Task.id)
).filter(sub.c.last_status.in_(['New', 'Open']))
当我运行此操作时,会出现以下错误:
ProgrammingError: (psycopg2.ProgrammingError) column "task_updates.external_status" must appear in the GROUP BY clause or be used in an aggregate function
如果你能给我任何帮助,我将不胜感激。这很重要
更新1据我所知,这是最终工作的SQL,但在SQLAlchemy中工作之前,我无法测试前端:
SELECT t.* FROM (
SELECT DISTINCT ON (task_id) task_id, external_status
FROM task_updates
ORDER BY task_id, external_updated_at DESC NULLS LAST) tu
JOIN tasks t ON t.id = tu.task_id
JOIN services s ON s.id = t.service_id
WHERE s.client_id = '" + str(client_id) + "'
AND tu.external_status IN ('New', 'Open');
这是我的转换尝试,仍然不起作用:
sub = SESSION.query(TaskUpdate).distinct(TaskUpdate.task_id).order_by(TaskUpdate.task_id.desc().nullslast(), TaskUpdate.external_updated_at.desc().nullslast()).subquery()
tasks = SESSION.query(Task).join(Service).join(sub.c.task_id==Task.id).filter(TaskUpdate.external_status.in_(['New', 'Open']))
更新2:我下面的查询可以工作,但当我这样做时。计数它会返回任务更新的总数,而不是任务,我怀疑需要以不同的方式重新执行查询,除非有人知道如何处理此问题?正在执行此操作:
SELECT t.*
FROM (
SELECT DISTINCT ON (task_id)
task_id, external_status
FROM task_updates
ORDER BY task_id, external_updated_at DESC NULLS LAST
) tu
JOIN tasks t ON t.id = tu.task_id
WHERE tu.external_status IN ('New', 'Open');
首先获取每个任务的最后一行,然后仅选择具有正确外部_状态的任务
关于以下内容的详细说明:
如果每个任务有多行,则有更快的查询技术:
我要赞扬欧文,因为他把我带到了正确的轨道上,但这就是我最终使用的。效果很好。一旦我有一名或多名工程师与我一起工作,我将在以后进行优化: 谢谢
sub = SESSION.query(TaskUpdate.task_id, TaskUpdate.external_status).distinct(TaskUpdate.task_id).order_by(TaskUpdate.task_id.desc().nullslast(), TaskUpdate.external_updated_at.desc().nullslast()).subquery()
tasks = SESSION.query(Task).join(Service).join((sub, sub.c.task_id==Task.id)).filter(sub.c.external_status.in_(['New', 'Open', 'Pending']))
也许我转换的不正确,但当我计数时,它给出的是任务更新的数量,而不是任务。这会导致我的应用程序出现问题。这里有一种方法可以获得想要的结果: 在SQL测试中:
SELECT a.task_id, a.external_status, a.external_updated_at
FROM (
SELECT task_id, max(external_updated_at) AS last_updated_at
FROM task_updates
GROUP BY task_id
) b
JOIN task_updates a ON a.task_id = b.task_id
WHERE
a.external_updated_at = b.last_updated_at AND
a.external_status IN ('New', 'Open')
ORDER BY
a.task_id;
在Python/SQLAlchemy中,还没有测试过,现在还没有SQLAlchemy:
subq = session.query(
TaskUpdate.task_id, func.max(TaskUpdate.external_updated_at).label('last_updated_at')
).group_by(
TaskUpdate.task_id
).subquery()
q = session.query(
TaskUpdate.task_id, TaskUpdate.external_status, TaskUpdate.external_updated_at
).join(
TaskUpdate.task_id == subq.c.task_id)
).filter(
TaskUpdate.external_updated_at == sub.c.last_updated_at,
TaskUpdate.external_status.in_(['New', 'Open'])
).order_by(
TaskUpdate.task_id
)
选择t.*从任务上选择不同的任务id任务id,从任务中选择外部状态按任务id更新顺序,在DESC处选择外部状态为空最后一次tu加入任务t在t.id=tu.task\U id加入服务s在s.id=t.service\U id,其中s.client\U id='1'和tu.external\U状态在'New'、'Open';这是最后一个有效的问题,谢谢。现在我需要将其转换为SQLAlchemy。如果对该查询进行计数,它将返回TaskUpdate的总数。这不是理想的行为。有更干净的吗?@PhilSalesses:如果你对这个查询进行计数,它不会返回TaskUpdate的总数。您可以获得任务更新中至少有一个相关行的任务中的行数。一定有什么误会。