Если вы хотите избежать объединений, я бы предложил такую конструкцию
WITH cte AS (
SELECT DATE, ID, ScoreA, ScoreB, ScoreC,
row_number() over (partition by ID order by ScoreA desc) rnA,
row_number() over (partition by ID order by ScoreB desc) rnB,
row_number() over (partition by ID order by ScoreC desc) rnC,
FROM ...
WHERE DATE BETWEEN ... AND ...
), ids AS (
SELECT DISTINCT ID FROM cte
)
SELECT ID,
(SELECT ScoreA FROM cte t2 WHERE t2.ID = t.ID AND rnA = 1) ScoreA,
(SELECT DATE FROM cte t2 WHERE t2.ID = t.ID AND rnA = 1) Date_A,
(SELECT ScoreB FROM cte t2 WHERE t2.ID = t.ID AND rnB = 1) ScoreB,
(SELECT DATE FROM cte t2 WHERE t2.ID = t.ID AND rnB = 1) Date_B,
(SELECT ScoreC FROM cte t2 WHERE t2.ID = t.ID AND rnC = 1) ScoreC,
(SELECT DATE FROM cte t2 WHERE t2.ID = t.ID AND rnC = 1) Date_C
FROM ids t
Когда вам нужна дата или какой-либо другой атрибут значения max / min, разумно использовать нумерацию строк вместо агрегирующих функций:row_number() over (...) as rn
с последующим условием rn = 1
UPD
Как только @TaurusDang захочет создать код, я могу разрешить postgres выполнять почти всеработа:
WITH cols AS
(
SELECT column_name
FROM information_schema.columns
WHERE table_schema = 'your_schema'
AND table_name = 'your_table'
AND column_name like 'Score%'
)
-- first part: rows for cte subquery
SELECT ',row_number() over (partition by ID order by ' || column_name || ' desc) rn' || column_name
FROM cols
UNION ALL
-- second part: rows for final query
SELECT ',(SELECT ' || column_name || ' FROM cte t2 WHERE t2.ID = t.ID AND rn' || column_name || ' = 1) ' || column_name || ', (SELECT DATE FROM cte t2 WHERE t2.ID = t.ID AND rn' || column_name || ' = 1) Date_' || column_name
FROM cols
Просто скопируйте сгенерированные строки в начальный запрос: первую половину cte, а вторую - основной запрос