#sql #postgresql
#sql #postgresql
Вопрос:
У меня есть БД следующим образом:
| company | timestamp | value |
| ------- | ---------- | ----- |
| google | 2020-09-01 | 5 |
| google | 2020-08-01 | 4 |
| amazon | 2020-09-02 | 3 |
Я хотел бы рассчитать среднее value
значение для каждой компании за последний год, если имеется> = 20 точек данных. Если существует менее 20 точек данных, то мне бы хотелось получить среднее значение за весь период времени. Я знаю, что могу выполнить два отдельных запроса и получить средние значения для каждого сценария. Я полагаю, вопрос в том, как мне объединить их обратно в одну таблицу на основе имеющихся у меня критериев.
select company, avg(value) from my_db GROUP BY company;
select company, avg(value) from my_db
where timestamp > (CURRENT_DATE - INTERVAL '12 months')
GROUP BY company;
Комментарии:
1. если имеется> = 20 точек данных, вы имеете в виду общую сумму значений?
Ответ №1:
WITH last_year AS (
SELECT company, avg(value), 'year' AS range -- optional tag
FROM tbl
WHERE timestamp >= now() - interval '1 year'
GROUP BY 1
HAVING count(*) >= 20 -- 20 rows in range
)
SELECT company, avg(value), 'all' AS range
FROM tbl
WHERE NOT EXISTS (SELECT FROM last_year WHERE company = t.company)
GROUP BY 1
UNION ALL TABLE last_year;
db<> скрипка здесь
Индекс on (timestamp)
будет использоваться только в том случае, если ваша таблица большая и содержит много лет.
Если в диапазоне большинства компаний более 20 строк (company)
, для 2-го будет использоваться индекс on SELECT
для извлечения нескольких выбросов.
Комментарии:
1. Здесь много отличных ответов, но это, казалось, самый ясный синтаксис.
2. @Zaheer Я удивлен, что вы видите самый ясный синтаксис в запросе, который агрегирует дважды в таблице, а также использует UNION ALL для результатов.
Ответ №2:
Используйте условную агрегацию:
select company,
case
when sum(case when timestamp > CURRENT_DATE - INTERVAL '12 months' then value end) >= 20 then
avg(case when timestamp > CURRENT_DATE - INTERVAL '12 months' then value end)
else avg(value)
end
from my_db
group by company
Если под 20 точками данных вы подразумеваете 20 строк за последние 12 месяцев для каждой компании, то:
select company,
case
when count(case when timestamp > CURRENT_DATE - INTERVAL '12 months' then value end) >= 20 then
avg(case when timestamp > CURRENT_DATE - INTERVAL '12 months' then value end)
else avg(value)
end
from my_db
group by company
Ответ №3:
Вы можете использовать оконные функции для предоставления информации для фильтрации:
select company, avg(value),
(count(*) = cnt_this_year) as only_this_year
from (select t.*,
count(*) filter (where date_trunc('year', datecol) = date_trunc('year', now()) over (partition by company) as cnt_this_year
from t
) t
where cnt_this_year >= 20 and date_trunc('year', datecol) = date_trunc('year', now()) or
cnt_this_year < 20
group by company;
В третьем столбце указывается, все ли строки относятся к этому году. С помощью фильтрации в where
предложении легко добавить и другие вычисления (такие как min()
, max()
, и так далее).