2016-11-22 2 views
0

파워 미터의 측정 값을 저장하는 PostgreSQL 테이블이 있습니다. SQLAlchemy와 psycopg2를 사용하여 데이터베이스를 쿼리합니다. 일부 대형 사이트는 여러 파워 미터를 가질 수있다, 나는 시설에서 집계 타임 스탬프 데이터를 반환하는 쿼리를 가지고 :SQLAlchemy의 쿼리 결과가있는 유니온 요약 통계?

원시 테이블 : 집계

timestamp | meter_id | facility_id | reading 
    1:00:00 |  1 |   1 |  1.0 
    1:00:00 |  2 |   1 |  1.5 
    1:00:00 |  3 |   2 |  2.1 
    1:00:30 |  1 |   1 |  1.1 
    1:00:30 |  2 |   1 |  1.6 
    1:00:30 |  3 |   2 |  2.2 

:

timestamp | facility_1 | facility_2 
    1:00:00 |  2.5 |  2.1 
    1:00:30 |  2.7 |  2.2 

내가 사용하는 쿼리 이는 다음과 같습니다.

SELECT 
    reading.timestamp, 
    sum(reading.reading) FILTER (WHERE reading.facility_id = 1) as facility_1, 
    sum(reading.reading) FILTER (WHERE reading.facility_id = 2) as facility_2 
FROM reading 
GROUP BY reading.timestamp 
WHERE 
    reading.timestamp >= 1:00:00 AND reading.timestamp < 1:01:00 
    AND reading.facility_id IN 1, 2 

(모든 SQL 오류로 인해 불편을 드려 죄송합니다. 약간의 명확성을 위해 문제). 종종 위의 쿼리를 FROM ... AS ... 절에 래핑하고 더 큰 시간 간격으로 데이터를 비닝하여 표시 할 데이터를 다운 샘플링해야합니다. 그 전에 필자는 this blog post에 설명 된 것과 유사한 필자의 파생 된 "시설"테이블에서 최소 읽기, 최대 읽기, 평균 읽기 등의 요약 통계를 얻고 싶습니다. 그러나, 나는이 데이터를 얻기 위해 SQLALchemy를 사용하는 방법을 알아낼 수 없다 - 결과 SQL에서 psycopg2 오류가 계속 발생한다. 위의 질의 내 SQLAlchemy의 버전은 다음과 같습니다

selects = [Reading.timestamp, 
    sqlalchemy.func.sum(Reading.reading).filter(Reading.facility_id==1), 
    sqlalchemy.func.sum(Reading.reading).filter(Reading.facility_id==2) 
] 
base_query = db.session.query(*selects). \ 
    group_by(Reading.timestamp). \ 
    filter(Reading.facility_id.in_([1, 2])). \ 
    filter(and_(Reading.timestamp>=start_time, Reading.timestamp<=end_time)). \ 
    order_by(Reading.timestamp) 

나는 이런 식으로 뭔가로 요약 통계를 얻을 수 있습니다 내 원래 쿼리의 모든 컬럼의 평균 하나의 행을 반환합니다

subq = base_query.subquery() 
avg_selects = [sqlalchemy.func.avg(col) for col in subq.columns] 
avg_query = db.session.query(*avg_selects) 

합니다. 그러나 원래의 쿼리를 사용하여이를 얻는 방법을 알 수는 없습니다. 통계를 따로 따로 가져야하는 번거 로움이 있습니다. 많은 비용이 듭니다. 이러한 쿼리는 많은 행을 처리 할 수 ​​있습니다. 내가 SQLAlchemy의의 하위 쿼리 시스템에 대한 이해 같은 느낌

all = base_query.union(avg_query).all() 

ProgrammingError: (psycopg2.ProgrammingError) syntax error at or near "UNION" 
LINE 4: ...reading.timestamp ORDER BY reading.timestamp UNION SELE... 

가 약한,하지만 난 SQLAlchemy의 설명서에있는 하위 쿼리 튜토리얼에서 진전을하지 못하고 다음과 같은 쿼리는 아래 항상 오류를 반환합니다. 아이디어?

답변

0

대답은 바로 오류 메시지입니다. 하위 쿼리에서 ORDER BY 절을 UNION 연산 외부로 제거하고 UNION 외부로 이동해야했습니다. 나는 요약 통계에 대해 더미 타임 스탬프를 사용하여 타임 스탬프를 주문한 후 예측 가능한 순서로 쿼리 결과의 맨 위에 있도록합니다. 다음 코드가 작동합니다.

from sqlalchemy.sql import expression, func 
from datetime import datetime 
from models import Reading 

selects = [Reading.timestamp.label("timestamp_"), 
    func.sum(Reading.reading).filter(Reading.facility_id==1), 
    func.sum(Reading.reading).filter(Reading.facility_id==2) 
] 

base_query = db.session.query(*selects). \ 
    group_by(Reading.timestamp). \ 
    filter(Reading.facility_id.in_([1, 2])). \ 
    filter(and_(Reading.timestamp>=start_time, Reading.timestamp<=end_time)) 

subq = base_query.subquery() 

avg_selects = [expression.bindparam('dummy_date', datetime(1980, 1, 1)).label("timestamp_") 
avg_selects += [func.avg(col) for col in subq.columns[1:] 
avg_query = db.session.query(*avg_selects) 

full_query = base_query.union(avg_query).order_by(asc("timestamp_")) 

이 작업을 수행하는 데 더 기쁜 방법이 있습니다. 쿼리는 임의의 시설 ID 목록을 사용하는 함수로 래핑됩니다. "열"트릭은 임의의 열 (첫 번째 열이 항상 타임 스탬프 인 한)에서 작동하도록 알아 낸 유일한 방법입니다.