Я понимаю, что вы хотите:
строка с самым ранним временем последнего дня для каждого пользователя
Дизайн таблицы
Для начала: опустите столбец Date
. Хранение этого избыточно добавляет больше затрат и сложностей, чем стоит:
CREATE TABLE userdata (
user_id int
, datetime timestamp
, input text
);
input
действительно должна быть дешевой реализацией перечисления (enum
, FK, ...).
timestamptz
может быть подходящим типом для datetime
. Зависит. См .:
Индекс
В любом случае, чтобы сделать вашу операцию быстро, это идеальный показатель:
CREATE INDEX userdata_special_idx ON userdata
(user_id, (datetime::date) DESC NULLS LAST, datetime);
datetime::date
- это очень дешевое приведение, заменяющее ваш лишний столбец дат. Я по-прежнему добавляю дату в индекс выражений из нескольких столбцов для повышения производительности. ( дата зависит от часового пояса при работе с timestamptz
. Если вы работаете с несколькими часовыми поясами, вам нужно сделать больше.)
Обратите внимание на добавленный NULLS LAST
: так как ничего в вашем вопросе говорится, что отметка времени равна NOT NULL
, это необходимо в запросе, чтобы предотвратить бессмысленные результаты - и индекс должен совпадать для достижения наилучших результатов. См .:
Запрос
Только для несколько строк на пользователя , DISTINCT ON
должен быть лучшим выбором (как уже предлагал GMB) - просто и быстро:
SELECT DISTINCT ON (user_id)
user_id, datetime, input
FROM userdata
ORDER BY user_id, datetime::date DESC NULLS LAST, datetime;
См .:
Для много строк на пользователя , этот альтернативный запрос должно быть (существенно) быстрее:
SELECT u.user_id, d.*
FROM users u
LEFT JOIN LATERAL (
SELECT d.datetime, d.input
FROM userdata d
WHERE d.user_id = u.user_id -- lateral reference
ORDER BY d.datetime::date DESC NULLS LAST, d.datetime
LIMIT 1
) d ON true;
Обычно - это путь к go для вашего сценария.
Обратите внимание на LEFT JOIN
: возвращается строка для каждого пользователя, даже без записей в userdata
. Если это нежелательно, используйте CROSS JOIN
. Похожие:
Это предполагает существование таблицы users
, который обычно существует. Если этого не произойдет, я предлагаю вам добавить его (по нескольким причинам). Если это не вариант, все еще есть быстрые обходные пути. См .:
db <> fiddle здесь
В сторону: я настоятельно рекомендую всегда использовать формат даты ISO (, как и руководство ). Региональный формат зависит от настроек текущего сеанса и может привести к ошибкам.