База данных: выберите последние ненулевые записи
Вот вопрос, который я переживаю. Скажем, у меня есть таблица с серией временных меток и номер детали в качестве первичного ключа. В таблице хранятся инкрементные изменения, что означает, что для каждой отметки времени, если поле изменяется, это изменение записывается. Если поле не изменяется, то для новой метки времени оно равно NULL.
Вот основная идея.
part | timestamp | x-pos | y-pos | status
------+-----------+-------+-------+--------
a5 | 151 | 5 | 15 | g
a5 | 153 | NULL | 17 | NULL
(part, timestamp)
является основным ключом. NULL
во второй записи указывают значения, которые не изменяются со времени первой записи.
То, что я хочу сделать, - это выбрать самые последние значения для каждого поля, сгруппированного по части. Например, учитывая приведенные выше записи, результаты будут 153,5,17, g для части a5.
На данный момент у меня есть этот взломанный запрос.
((SELECT x-pos FROM part_changes WHERE x-pos IS NOT NULL
ORDER BY timestamp DESC
LIMIT 1)
UNION
(SELECT y-pos FROM part_changesWHERE y-pos IS NOT NULL
ORDER BY timestamp DESC
LIMIT 1)
UNION
(SELECT status FROM part_changes WHERE status IS NOT NULL
ORDER BY timestamp DESC
LIMIT 1))
Но это возвращает один столбец, что означает, что я могу использовать групповое для организации.
Там должен быть более элегантный способ делать вещи, например, используя COALESCE или IS NULL творческим способом. Но я застрял и не могу понять. У кого-нибудь появилась идея?
И нет, я не могу изменить структуру базы данных.
РЕДАКТОР: Руах имеет правильную идею. Единственная проблема теперь заключается в группировке по частям. Кажется, я не могу обойти LIMIT 1
для группировки несколькими частями. Есть идеи?
mdahlman, я не слишком хорошо знаком с аналитическими функциями в postgresql. Таким образом, если это решение было бы проще, чем сложный запрос, то обязательно отправьте свою идею.
РЕДАКТИРОВАТЬ 2: Спасибо всем за помощь. Я думаю, что у меня есть достаточно хорошее представление о том, что мне нужно делать.
Ответы
Ответ 1
Вместо того, чтобы использовать UNION
, похоже, что вам действительно нужны подзапросы в списке полей. То есть вместо (SELECT ...) UNION (SELECT ...) UNION (SELECT ...)
вы хотите SELECT (SELECT ...), (SELECT ...), (SELECT ...)
.
Например:
SELECT part,
( SELECT x_pos
FROM part_changes
WHERE part = pc.part
AND x_pos IS NOT NULL
ORDER
BY timestamp DESC
LIMIT 1
) AS x_pos,
( SELECT y_pos
FROM part_changes
WHERE part = pc.part
AND y_pos IS NOT NULL
ORDER
BY timestamp DESC
LIMIT 1
) AS y_pos,
( SELECT status
FROM part_changes
WHERE part = pc.part
AND status IS NOT NULL
ORDER
BY timestamp DESC
LIMIT 1
) AS status
FROM ( SELECT DISTINCT
part
FROM part_changes
) AS pc
;
Но в этот момент я бы действительно подумал о написании хранимой процедуры.
В качестве альтернативы:
SELECT DISTINCT
part,
FIRST_VALUE(x_pos) OVER
( PARTITION BY part
ORDER BY CASE WHEN x_pos IS NULL
THEN NULL
ELSE TIMESTAMP
END DESC NULLS LAST
) AS x_pos,
FIRST_VALUE(y_pos) OVER
( PARTITION BY part
ORDER BY CASE WHEN y_pos IS NULL
THEN NULL
ELSE TIMESTAMP
END DESC NULLS LAST
) AS y_pos,
FIRST_VALUE(status) OVER
( PARTITION BY part
ORDER BY CASE WHEN status IS NULL
THEN NULL
ELSE TIMESTAMP
END DESC NULLS LAST
) AS status
FROM part_changes
;
Ответ 2
Только для одной части это даст вам ответ.. благодаря ruakh
Но мне не нравится эта версия.
SELECT
(SELECT timestamp FROM part_changes WHERE part = $part
ORDER BY timestamp DESC
LIMIT 1) as timestamp,
(SELECT x-pos FROM part_changes WHERE part = $part and x-pos IS NOT NULL
ORDER BY timestamp DESC
LIMIT 1) as xpos,
(SELECT y-pos FROM part_changes WHERE part = $part and y-pos IS NOT NULL
ORDER BY timestamp DESC
LIMIT 1) as ypos,
(SELECT status FROM part_changes WHERE part = $part and status IS NOT NULL
ORDER BY timestamp DESC
LIMIT 1)) as status
Ответ 3
список соответствующих временных меток:
select max timestamp from part_changes where x_POS is not null group by part
Вы можете сделать это: разрешить вызов этого вида1
SELECT part_changes.part, part_changes.x-pos
FROM part_changes left join view1 on part_changes.part = view1.part
WHERE x-pos IS NOT NULL
AND part_changes.timestamp = view1.timestamp
GROUP BY part_changes.part
Посмотрите, куда я иду? Это должно дать вам полный список для x-pos.
Ответ 4
Руах прав. Альтернатива: Напишите пользовательский агрегат, используя SQL-CLR. Этот агрегат может выполнять нижний верхний верх над вашими строками и помнить первое ненулевое значение для каждого столбца.
Вы также можете сделать это в приложении. Сделайте свои строки запроса запроса в отсортированном порядке партиями, скажем, 10 строк. Совокупность этих партий, как описано выше. Если после выполнения текущей партии нулевой столбец получает следующую партию.