#sql #postgresql #pivot-table #crosstab
#sql #postgresql #сводная таблица #перекрестная таблица
Вопрос:
У меня есть 2 таблицы measurement_timestamps и sensor_double_precision следующего вида:
id start_time stop_time
1 2020-02-22 2020-02-24
2 2020-02-25 2020-02-27
и
id sensor_name value_cal timestamp
1 start_freq 15 2020-02-23
2 stop_freq 18 2020-02-23
3 start_freq 15 2020-02-26
4 stop_freq 18 2020-02-26
Я хочу ПРЕДСТАВЛЕНИЕ, которое просматривает временные метки измерений и для каждой пары start_time — stop_time выполняет поворот (транспонирует?) столбец sensor_name такой, что start_freq и stop_freq становятся там собственными столбцами с соответствующим value_cal в качестве строк.
Итак, я в основном хочу, чтобы представление выглядело следующим образом:
id start_freq stop_freq timestamp
1 15 18 2020-02-23
2 15 18 2020-02-26
Обратите внимание, что временная метка, связанная с идентификатором 1 в ПРЕДСТАВЛЕНИИ, находится между start_time и stop_time для идентификатора 1 в таблице measurement_timestamps.
Каков разумный способ сделать это? Я не хочу создавать ПРЕДСТАВЛЕНИЕ для каждого отдельного sensor_name, потому что у меня намного больше датчиков, чем это, и это просто не кажется очень надежным. Ниже приведен подход, который мне рекомендовали, но, похоже, он не работает, потому что я, вероятно, делаю что-то неправильно.
SELECT *
FROM crosstab('with current_data as (
select distinct on (mt.id)
mt.id, sdp.sensor_name, sdp.value_cal
from measurement_timestamps mt, sensor_double_precision sdp
order by mt.id desc
),
ids as (
select distinct id from current_data
),
sensor_names as (
select distinct sensor_name from current_data
)
select ids.id, sensor_names.sensor_name, current_data.value_cal
from ids cross join sensor_names
left join current_data on (ids.id=current_data.id and sensor_names.sensor_name=current_data.sensor_name)
order by ids.id,sensor_names.sensor_name') final_data (id integer, start_freq double precision,
stop_freq double precision, timestamp timestamp)
Sidenote — start_freq и stop_freq не похожи на имена датчиков, но я работаю с таблицей, которая соответствует стандартизированной форме, из-за чего мы называем ее sensor_name. Есть также другие датчики, о которых я не беспокоюсь для этой задачи.
РЕДАКТИРОВАТЬ — результат запроса, который был предложен ниже:
id. start_freq stop_freq. timestamp
18 15 null "2020-07-09 20:03:38.937195 00"
19 null 18. "2020-07-09 20:03:39.051836 00"
20 null null "2020-07-09 20:03:39.171837 00"
21 null null "2020-07-09 20:03:39.287994 00"
22 null null "2020-07-09 20:03:39.287994 00"
23 15 null "2020-07-09 20:03:39.287994 00"
24 null 18 "2020-07-09 20:03:39.287994 00"
EDIT2 — Данные, которые я прикрепил к задаче, являются образцами данных, чтобы упростить обсуждение проблемы. Структура и все остальное аналогичны реальному набору данных.
Комментарии:
1. Если у вас есть только два имени датчиков, используйте
case
выражение. Вы упомянули что-то о нежелании нескольких представлений, так что, возможно, это не то, как выглядят фактические данные?2. В основном так выглядят данные. Есть и другие sensor_names, но они меня не особо волнуют, поэтому я их опустил. Я использовал
CASE
инструкции, но это оставляетNULL
значения между точками данных.3. Что, если более двух строк во второй таблице совпадают с первой таблицей?
4. @GordonLinoff в идеале игнорируйте их, но я всегда могу выбрать все, что мне нужно, поэтому я готов пойти на компромисс, если я должен преобразовать каждое sensor_name в столбец. Действительно, есть датчики, которые подпадают под то, что вы только что описали
Ответ №1:
Для этого есть 2 способа. (условно только один набор событий запуска и остановки с временным диапазоном measurement_timestamps
)
- используя обычный
aggregation
иfilter
select
m.id,
min(s.value_cal) filter (where sensor_name='start_freq'),
min(s.value_cal) filter (where sensor_name='stop_freq'),
min(s.timestamp) filter (where sensor_name='stop_freq')
from measurement_timestamps m
inner join sensor_double_precision s on s.timestamp between m.start_time and m.stop_time
group by m.id
Примечание: вы не указали, какую дату вы хотите, поэтому я взял stop_freq timestamp
. Вы можете изменить то, что хотите.
- Использование
Crosstab
(по-вашему)
select * from crosstab('select
m.id,
s.timestamp,
s.sensor_name,
s.value_cal
from measurement_timestamps m
inner join sensor_double_precision s on s.timestamp between m.start_time and m.stop_time',
'select ''start_freq'' union select ''stop_freq'' ') as (id int, timestamp date, start_freq varchar, stop_freq varchar)
Примечание: Приведенное выше будет работать корректно только тогда, когда start_freq timestamp
и stop_freq timestamp
одинаковы
Комментарии:
1. Я выполнил предложенный вами запрос. Хотя это не дало мне никакой ошибки, это также не сработало. Он продолжал пытаться загрузиться и, похоже, застрял. Я даже добавил ОГРАНИЧЕНИЕ 10 в конце, но это тоже не помогло.
2. вы можете проверить скрипту. Но в чем ошибка или разница
3. Я также не знаю, как заставить работать перекрестную таблицу. Могу ли я спросить, чем предложенное вами отличается от использования
case
инструкций, предложенных в другом ответе?4. Только одно отличие, если дата начала и окончания отличается, тогда моя будет работать, но другая не будет работать.
5. @Parashar Можете ли вы сказать, что именно не работает. Если у вас есть огромные данные, тогда поместите предложение where с вашими start_time и stop_time вместо limit
Ответ №2:
Я не понимаю, почему это не должно работать. Ваш комментарий о «нулевых значениях между точками данных» для меня не имеет смысла.
select
m.id,
min(case when m.sensor_name = 'start_freq' then m.value_cal end) as start_freq,
min(case when m.sensor_name = 'stop_freq' then m.value_cal end) as stop_freq,
s.timestamp
from measurement_timestamps m inner join sensor_double_precision s
on s.timestamp between m.start_time and m.stop_time
group by m.id, s.timestamp
Комментарии:
1. Я не могу действительно показать вам ошибку, потому что я не могу загрузить изображение здесь, но я получаю
null
значения между точками данных. Под этим я подразумеваю, что когда start_freq, скажем, равен 15, соответствующий stop_freq должен быть равен 18, но этот запрос возвращаетnull
. когда частота остановки равна 18, start_freq равенnull
2. Я только что отредактировал вопрос и опубликовал результат вашего запроса.
3. Ваши данные явно не соответствуют тому, что вы указали в вопросе.
4. ммм, это действительно совпадает. дополнительные нули внизу взяты из ‘other sensor_names’, которые меня на самом деле не волнуют. Я только что обновил результат вашего запроса еще несколькими строками. Я просто добавил то, что видел внизу. 3 строки в середине только
null
значений получены от датчиков, о которых я не забочусь