Как мне получить отдельные текстовые значения в группе при запуске агрегата?

#sql #postgresql

#sql #postgresql

Вопрос:

У меня есть данные, которые заполняются в несколько странном порядке, поэтому мне приходится выполнять множество дополнительных запросов, чтобы получить данные, отформатированные так, как мне нравится. Это уже трудно читать, поэтому мне интересно, есть ли какие-либо функции Postgres, которые я мог бы использовать, или просто лучший способ запросить это.

Соответствующие данные здесь представлены в двух таблицах:

observations

  -------- -------- ------------- 
| obs_id | person | observed_at |
 -------- -------- ------------- 
| 1a     | John   | 1/1/20      |
| 2b     | Barry  | 2/1/20      |
| 3c     | John   | 3/1/20      |
| 4d     | Barry  | 4/1/20      |
| 5e     | John   | 5/1/20      |
| 6f     | Zach   | 7/1/20      |
 -------- -------- ------------- 
  

measurements

  -------- --------- ------- ------- 
| obs_id | meas_id | score | arc   |
 -------- --------- ------- ------- 
| 1a     | f32f    | 1     | NULL  |
| 1a     | k8787   | NULL  | NULL  |
| 1a     | 93j2    | NULL  | ARC 1 |
| 1a     | 0fkd    | 2     | NULL  |
| 2b     | 92u4    | 4     | NULL  |
| 2b     | 10fl    | NULL  | ARC 1 |
| 2b     | 42u4    | 3     | NULL  |
| 3c     | 70fl    | NULL  | ARC 1 |
| 3c     | 70fl    | 4     | NULL  |
| 3c     | 8f09    | 1     | NULL  |
| 4d     | 0987    | 4     | NULL  |
| 4d     | 0192    | 4     | NULL  |
| 4d     | 1234    | NULL  | ARC 1 |
| 5e     | 8d0s    | NULL  | ARC 2 |
| 5e     | 1112    | 3     | NULL  |
| 6f     | 0981    | 1     | NULL  |
| 6f     | fdsf    | 2     | NULL  |
| 6f     | jy4s    | 3     | NULL  |
| 6f     | 876j    | NULL  | ARC 1 |
 -------- --------- ------- ------- 
  

На английском языке это то, какой должна быть запись в выходных данных: в КАЖДОЙ дуге для КАЖДОГО человека вычислите оценку ПОСЛЕДНЕГО наблюдения. Таким образом, на выходе у Джона будет 2 записи (по 1 строке как в ДУГЕ 1, так и в ДУГЕ 2), у Барри будет 1 запись (1 в ДУГЕ 1), а у Зака также будет только 1 запись (1 в ДУГЕ 1).

В более технических терминах я бы хотел, чтобы выходные данные были с детализацией person | arc | avg_score . Часть, с которой у меня возникли проблемы, заключается в группировании данных при получении avg(score) , а также получении arc s. Вот как должен выглядеть результат:

  -------- -------- ----------- 
| person |  arc   | avg_score |
 -------- -------- ----------- 
| John   | ARC 1  | 2.5       |
| John   | ARC 2  | 3         |
| Barry  | ARC 1  | 4         |
| Zach   | ARC 1  | 2         |
 -------- -------- ----------- 
  

Это неверно, но имеет общую суть того, что я пытаюсь сделать. Это дает только 1 запись на человека, но я бы хотел, чтобы вывод был более детализированным для разделения по дуге. Я знаю, что мог бы сделать еще avg(score) один проход и min(arc) снова получить и, но у меня есть еще 2 запроса поверх этого, и я хочу свести к минимуму количество запросов.

 SELECT
  row_number() over (partition by person order by observed_at desc) as row_num,
  *
FROM
(
SELECT
  obs_id,
  person,
  observed_at,
  avg(score),
  max(arc)
FROM observations as o
INNER JOIN measurements as m
  ON o.obs_id = m.obs_id
GROUP BY obs_id, person, observed_at
) a
  

Это слишком много для вопроса о том, как уменьшить количество проходов в таблице, но мне нужно выполнить еще несколько подзапросов, и любое меньшее количество, которое я могу сделать, — это победа.

Ответ №1:

вы можете попробовать что-то подобное

 with
t1l as (
    select t1.*, t2.arc, ROW_NUMBER() over (partition by arc, person order by observed_at desc) n
    from observations t1
    left join measurements t2 on t1.obs_id = t2.obs_id
    where arc is not null
)
select person, t1l.arc, avg(t2.score) avg_score
from t1l
join measurements t2 on t2.obs_id = t1l.obs_id
where n=1
group by person, t1l.arc
order by person, arc