А задача такая - есть в таблице комбинации по три строки с одинаковыми столбцами, кроме emission_sum и report_marker. Есть три вида report_marker, поэтому соответственно три строки. надо превратить эти три строки в одну, расширив таблицу колонками - свой emission_sum для каждого вида report_marker (emission_sum_dpnvos, emission_sum_dvos, emission_sum_pek). Как это сделать?
P.S. В запросе, в cte я убираю дубли ненужные, их я как раз с помощью этого cte вычищаю.
Я пытался делать через подзапросы, но работает оооочень долго. Даже для одного report_marker (как раз в запросе ниже). Как то можно по другому решить этот вопрос? Записей в таблице около 1.5 млн
WITH wd AS (
SELECT *
FROM
(SELECT *,
ROW_NUMBER() OVER (
PARTITION BY (pollutant_marker, onvos_code, pollutant_id, report_year)
ORDER BY sent_at DESC
) rn
FROM onvs_emissions_mass_tmp
) tmp WHERE rn = 1
)
SELECT (
SELECT emission_sum FROM wd m2
WHERE m2.onvos_code = tmp.onvos_code
AND m2.pollutant_id = tmp.pollutant_id
AND m2.report_year = tmp.report_year
AND m2.pollutant_marker = tmp.pollutant_marker
AND m2.report_marker = 'dpnvos'
) AS dpnvos
FROM wd tmp
GROUP BY pollutant_marker, onvos_code, pollutant_id, report_year
Ты хочешь развернуть часть таблички. Почитай про pivot.
Ну, какой пивот, о чём Вы. Пивот никогда не ускоряет никакие запросы. И вообще, его назначение — выгрузка во всякие не-реляцыонные структуры, с внешне похожэй на реляцыонную табличной организацыей. Да и то, чаще всего — это лучшэ поворачивать на клиенте, а пивот только позволяет исправить какие-то грубые ошыбки проектирования клиента.
Во-первых, https://t.me/pgsql/303899
Ну на клиенте можно все что угодно сделать. А если говорить про скорость и оптимизации - то это очень большая тема с кучей вариантов решения. Здесь со стороны базы решить её можно и с помощью изменение структуры таблицы, навешиванием триггирров и т.д. Этот канал все таки про реляционную БД - значит для начала пробуем решать средствами самой БД.
Во-вторых, удаление "дубликатов" делается через group by, а не вот этой этажэркой с оконным функцыями.
В-третьих, это тащемта делается одним group by с предикатами where для значений вашых трёх полей. Если postgres достаточно старый или рассчитываете на поддержку зоопарка sql-движков — то можно вместо where внутри агрегатной ф-ии вставить что-то вроде min(case when report_maker='dpnvos' then emission_sum else nullend) as emission_dpnvos, и аналогично для двух других.
В-третьих, пол-миллиона значений — это не так много обычно, но жэлательно чтобы оно не пыталось это через дисксортировать. Либо work_mem ему можно временно выдать несколько гиг, либо убедиться, что есть индэкс на все поля group by , что он лезет в память (лучшэ в shared_buffers) вместе со всеми данными таблички и заставить его сделать index scan.
В моем случае group by для устранения дублей не применимо, по причине того что я делаю order by по колонке которая в группировке в принципе не должна участвовать, вы же видите что я это делаю в "этажерке" в секции partition by + order by
И да, их не пол млн, их полтора. В общем спасибо всем за советы, выкрутился лаконично с помощью кейсов, отрабатывает секунд за 10, если кому то интересно решение такой проблемы WITH prp AS ( WITH wd AS ( SELECT * FROM (SELECT *, ROW_NUMBER() OVER ( PARTITION BY (pollutant_marker, onvos_code, pollutant_id, report_year) ORDER BY sent_at DESC ) rn FROM onvs_emissions_mass_tmp ) tmp WHERE rn = 1 ) SELECT id, pollutant_marker, onvos_code, pollutant_id, code, name, hazard_class, report_year, case when report_marker = 'dpnvos' then emission_sum else 0 end as dpnvos, case when report_marker = 'dvos' then emission_sum else 0 end as dvos, case when report_marker = 'pek' then emission_sum else 0 end as pek FROM wd ) SELECT ROW_NUMBER() OVER (), pollutant_marker, onvos_code, pollutant_id, code, name, hazard_class, report_year, sum(dpnvos) as emission_sum_dpnvos, sum(dvos) as emission_sum_dvos, sum(pek) as emission_sum_pek FROM prp GROUP BY pollutant_marker, onvos_code, pollutant_id, report_year, code, name, hazard_class, report_year
а чего критерии отбора не перенести в первый запрос?
о, пасиб за наводку )
Обсуждают сегодня