Python 查询一对多和多对一的链

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

我目前有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")

    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的总数。您可以获得任务更新中至少有一个相关行的任务中的行数。一定有什么误会。