2013-04-16 2 views
1

У меня есть запрос (PostgreSQL), где я хотел бы ограничить строки, используемые для вычисления среднейПредельное количество строк используется средний

SELECT username,avg(income),count(*) FROM 
     Events 
WHERE to_timestamp(eventtimestamp) >= '2008-02-23' AND 
     to_timestamp(eventtimestamp) <= '2009-01-03' and username='Joe' 
GROUP BY userid 

Джо имеет 40 записей, но я хочу, чтобы ограничить количество рядов, используемых для вычисления среднего значения его дохода. Я знаю о предел функции, которую я мог бы добавить в конце запроса, но это ограничивает вывод всего запроса вместо строк, рассмотренных командой в начале запроса. Любые подсказки, как я мог бы сказать avg использовать только первые n строк?

например. не работает

SELECT username,avg(income) limit 5,count(*) FROM 
     Events 
WHERE to_timestamp(eventtimestamp) >= '2008-02-23' AND 
     to_timestamp(eventtimestamp) <= '2009-01-03' and username='Joe' 
GROUP BY userid 

в среднем только в течение первых 5 строк.

Спасибо!

ответ

1

Вы можете просто использовать лимит в подзапросе;

SELECT username,avg(income),count(*) FROM 
    (SELECT * FROM Events 
    WHERE to_timestamp(eventtimestamp) >= '2008-02-23' AND 
     to_timestamp(eventtimestamp) <= '2009-01-03' and username='Joe' 
    order by to_timestamp(eventtimestamp) desc 
    LIMIT 10) sub 
GROUP BY userid; 
+0

В 'COUNT (*)' всегда будет <= 10. Это не ясно из вопроса, если это то, что хочет ОП. Я подозреваю, что это общий счет. –

2

Вы можете взять среднее значение внутреннего запроса:

SELECT username,avg(income),count(*) 
FROM (
    SELECT username, income 
    FROM Events 
    WHERE to_timestamp(eventtimestamp) BETWEEN '2008-02-23' AND '2009-01-03' 
    and username='Joe' 
    LIMIT 5) x 
GROUP BY userid; 

отметить также упрощение с помощью BETWEEN

+0

Хорошо - вам нужно добавить предложение ORDER BY, чтобы ограничить строки. – marcj

+0

'count (*)' всегда будет <= 5. Неясно, есть ли то, что хочет OP. Я подозреваю, что это общий счет. –

1

Если случайно вы предпочитаете (или не важно) в среднем по 5 рядов, заканчивающихся текущим, вы можете избежать подзапроса с помощью оконной функции:

select 
    username, 
    avg(income) over(rows 4 preceding), 
    count(*) 
from events 
where to_timestamp(eventtimestamp) >= '2008-02-23' and 
     to_timestamp(eventtimestamp) <= '2009-01-03' and username='joe' 
group by userid 

Если я правильно понял ваш комментарий вы действительно можете использовать count в качестве оконной функции:

count(*) over(rows 4 preceding) 

Или, если не хотите считать аннулирует:

count(income) over(rows 4 preceding) 
+0

Это был бы мой любимый способ, потому что у меня есть набор запросов, которые я должен изменить, как вы предложили. Единственная проблема с этим решением заключается в том, что count (*) не выполняется ограничением строки. К сожалению, добавление over() для count (*) не работает; ( – Durin

+0

@Durin Отредактировано, чтобы показать, как это сделать с 'count' –

+0

Я пробовал это, но * count * изменяется на 1, как только я добавляю ** count (*) над (строки X, предшествующие) **. Независимо от того, X = 1 или X = 1000000, столбцы count остаются 1 после добавления over() – Durin

0

Хорошие ответы уже опубликованы. Я бы предложил делать сравнения и сортировки, используя эпоху Unix следующим образом:

SELECT userid, username, avg(income), count(*) 
FROM (
    SELECT userid, username, income 
    FROM Events 
    WHERE eventtimestamp BETWEEN date_part('epoch', '2008-02-23'::date) 
     AND date_part('epoch', '2009-01-03'::date) 
    AND username='Joe' 
    ORDER BY eventtimestamp DESC LIMIT 10) AS q 
GROUP BY userid, username; 

Делая это, я не вызывая функцию преобразования для каждой строки. Альтернативой может быть создание функционального индекса на to_timestamp(eventtimestamp), но я думаю, что способ, которым я это сделал, более эффективен.

Обратите внимание, что я включил как userid, так и имя пользователя - исходный пример вызвал бы ошибку, поскольку имя пользователя не было в предложении GROUP BY.

Как в стороне, если вы хотите, чтобы вычислить из случайной выборки, а не последних п записи, вы можете изменить вид на ORDER BY random()

+0

Такая же проблема, как и другие ответы. Счетчик (*) всегда будет <= 10. Неясно, является ли это то, что OP хочет. Я подозреваю, что это общее количество –

+0

@ClodoaldoNeto: Спасибо - хорошее количество баллов (*). – marcj

3

Я добавляю ответ по двум причинам. Во-первых, большинство других ответов затрагивают count(*), а также avg(), что не является частью вопроса. Во-вторых, вы можете сделать это для нескольких пользователей.

Таким образом, вы можете попробовать следующее:

SELECT username, avg(case when seqnum <= 40 then income end), count(*) 
FROM (select e.*, ROW_NUMBER() over (partition by username order by eventtimestamp desc) as seqnum 
     from Events e 
     WHERE to_timestamp(eventtimestamp) >= '2008-02-23' AND 
      to_timestamp(eventtimestamp) <= '2009-01-03' 
    ) e 
GROUP BY username 
+0

Дело отсутствует 'else null конец' –

+0

@ClodoaldoNeto. , , Спасибо. Фактически, это просто отсутствует «конец», а затем «then NULL» является неявным. –

+0

Помимо этого, я думаю, что это единственный правильный ответ в соответствии с буквальными требованиями OP. –

Смежные вопросы