2

我現在有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') 

所以我有一對多的從任務關係TaskUpdates和許多從任務到服務。

我試圖創建一個查詢來獲取所有任務,其最新的TaskUpdate(通過時間戳)具有「新建」或「打開」的external_status。

這裏就是我的了:

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:查詢下面我有工作,但是當我做.Count之間()返回總數TaskUpdates的,沒有任務,我懷疑查詢需要重做一種不同的方式,除非有人知道如何處理這個問題?

回答

1

在辦法做到這一點:

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'); 

首先得到每個任務的最後一行,則只能挑任務機智的權利external_status

詳細爲DISTINCT ON解釋:

如果每個任務很多行,有更快的查詢方法:

+0

選擇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任務T ON t.id = tu.task_id JOIN服務S ON秒。 .id = t.service _id WHERE s.client_id ='1'和tu.external_status IN('New','Open'); 這是最後的查詢工作,謝謝。現在我需要將其轉換爲SQLAlchemy。 –

+0

如果您對此查詢進行計數,它將返回TaskUpdates的總數。這不是理想的行爲。有更清潔的嗎? –

+0

@PhilSalesses:如果您對此查詢進行計數,它*不會*返回TaskUpdates的總數。你可以在task_updates中獲得至少有一個相關行的'tasks'中的行數。在某個地方肯定會有誤解。 –

0

自從他將我帶入正確的道路之後,我將功勞歸功於Erwin,但這是我最終使用的。工作得不錯。一旦我真的有一個或幾個工程師與我合作,將來會優化。 :)

謝謝!

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'])) 

也許我這個轉換錯誤,但是當我做一個計數,它給了我TaskUpdates,沒有任務的數量。這導致我的應用程序出現問題。

0

這裏得到需要的結果的一種方法:

在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 
)