Как использовать подзапросы в SQLAlchemy для получения скользящей средней? - PullRequest
4 голосов
/ 22 сентября 2010

Моя проблема в том, что я хочу получить как список измерений, так и скользящее среднее этих измерений.Я могу сделать это с помощью этого оператора SQL (синтаксис интервала postgresql):

SELECT time, value,                
   (
       SELECT AVG(t2.value)
       FROM measurements t2
       WHERE t2.time BETWEEN t1.time - interval '5 days' AND t1.time
   ) moving_average
FROM measurements t1
ORDER BY t1.time;

Я хочу, чтобы код SQLAlchemy создавал оператор, аналогичный этому.В настоящее время у меня есть этот код Python:

moving_average_days = # configureable value, defaulting to 5
t1 = Measurements.alias('t1')
t2 = Measurements.alias('t2')
query = select([t1.c.time, t1.c.value, select([func.avg(t2.c.value)], t2.c.time.between(t1.c.time - datetime.timedelta(moving_average_days), t1.c.time))],
            t1.c.time > (datetime.datetime.utcnow() - datetime.timedelta(ndays))). \
        order_by(Measurements.c.time)

Это, однако, генерирует этот SQL:

SELECT t1.time, t1.value, avg_1
FROM measurements AS t1,
    (
        SELECT avg(t2.value) AS avg_1
        FROM measurements AS t2
        WHERE t2.time BETWEEN t1.time - %(time_1)s AND t1.time
    )
WHERE t1.time > %(time_2)s
ORDER BY t1.time;

Этот SQL имеет подзапрос как часть предложения FROM, где он не может иметь скалярный доступ кзначения столбцов значений верхнего уровня, то есть это заставляет PostgreSQL выложить эту ошибку:

ERROR:  subquery in FROM cannot refer to other relations of same query level
LINE 6:         WHERE t2.time BETWEEN t1.time - interval '5 days' AN...

То, что я хотел бы знать, это: как мне заставить SQLAlchemy переместить подзапрос в SELECTпредложение?

В качестве альтернативы можно использовать другой способ получения скользящего среднего (без выполнения запроса для каждой пары (время, значение)).

1 Ответ

5 голосов
/ 22 сентября 2010

Правильно, по-видимому, мне было нужно использование так называемого скалярного выбора .Используя их, я получаю этот код на python, который на самом деле работает так, как я хочу (генерирует эквивалент SQL для первого в моем вопросе, который был моей целью):

moving_average_days = # configurable value, defaulting to 5
ndays = # configurable value, defaulting to 90
t1 = Measurements.alias('t1') ######
t2 = Measurements.alias('t2')
query = select([t1.c.time, t1.c.value,
                    select([func.avg(t2.c.value)],
                        t2.c.time.between(t1.c.time - datetime.timedelta(moving_average_days), t1.c.time)).label('moving_average')],
            t1.c.time > (datetime.datetime.utcnow() - datetime.timedelta(ndays))). \
        order_by(t1.c.time)

Это даетSQL:

SELECT t1.time, t1.value,
    (
        SELECT avg(t2.value) AS avg_1
        FROM measurements AS t2 
        WHERE t2.time BETWEEN t1.time - :time_1 AND t1.time
    ) AS moving_average 
FROM measurements AS t1
WHERE t1.time > :time_2 ORDER BY t1.time;
...