Предположим, у меня есть таблица клиентов и таблица покупок. Каждая покупка принадлежит одному клиенту. Я хочу получить список всех клиентов вместе с их последней покупкой в одном операторе SELECT. Какова лучшая практика? Любой совет по созданию индексов?
Пожалуйста, используйте эти имена таблиц / столбцов в своем ответе:
- Заказчик: идентификатор, имя
- покупка: id, customer_id, item_id, дата
И в более сложных ситуациях было бы (с точки зрения производительности) выгодно денормализовать базу данных, поместив последнюю покупку в таблицу клиентов?
Если идентификатор (покупки) гарантированно отсортирован по дате, можно ли упростить выписки, используя что-то вроде LIMIT 1
?
Ответы:
Это пример
greatest-n-per-group
проблемы, которая регулярно появлялась в StackOverflow.Вот как я обычно рекомендую решить эту проблему:
Объяснение: для данной строки
p1
не должно быть строкиp2
с тем же клиентом и более поздней датой (или, в случае связей, более позднейid
). Когда мы обнаруживаем, что это правда,p1
это самая последняя покупка для этого клиента.Что касается индексов, я бы создать составной индекс в
purchase
течение столбцов (customer_id
,date
,id
). Это может позволить внешнему соединению быть выполненным, используя индекс покрытия. Обязательно протестируйте на своей платформе, потому что оптимизация зависит от реализации. Используйте функции вашей РСУБД для анализа плана оптимизации. Например,EXPLAIN
на MySQL.Некоторые люди используют подзапросы вместо решения, которое я показываю выше, но я считаю, что мое решение облегчает разрешение связей.
источник
Вы также можете попробовать сделать это с помощью суб-выбора
Выбор должен присоединиться ко всем клиентам и их дате последней покупки.
источник
INNER JOIN
кLEFT OUTER JOIN
.purchase
таблицы, - это дата и customer_id, но запрос запрашивает все поля из таблицы.Вы не указали базу данных. Если это та функция, которая допускает аналитические функции, возможно, этот подход будет быстрее, чем метод GROUP BY (определенно быстрее в Oracle, скорее всего быстрее в поздних выпусках SQL Server, о других не знаю).
Синтаксис в SQL Server будет:
источник
Другой подход заключается в использовании
NOT EXISTS
условия в вашем условии соединения для проверки последующих покупок:источник
AND NOT EXISTS
роль в простых словах?Я нашел эту тему как решение моей проблемы.
Но когда я попробовал их, производительность была низкой. Сильфон мое предложение для лучшей производительности.
Надеюсь, это будет полезно.
источник
top 1
иordered it by
MaxDatedesc
Если вы используете PostgreSQL, вы можете
DISTINCT ON
найти первую строку в группе.Документы PostgreSQL - четко определенные
Обратите внимание, что
DISTINCT ON
поле (поля) - здесьcustomer_id
- должно соответствовать крайнему левому полю (ам) вORDER BY
предложении.Предостережение: это нестандартное предложение.
источник
Попробуйте это, это поможет.
Я использовал это в своем проекте.
источник
Протестировано на SQLite:
Функция
max()
агрегирования будет следить за тем, чтобы в каждой группе была выбрана самая последняя покупка (но предполагается, что столбец даты имеет формат, в котором max () выдает самую последнюю - что обычно имеет место). Если вы хотите обрабатывать покупки с той же датой, вы можете использоватьmax(p.date, p.id)
.Что касается индексов, я бы использовал индекс покупок с (customer_id, date, [любые другие столбцы покупок, которые вы хотите вернуть в вашем выборе]).
LEFT OUTER JOIN
(В отличие отINNER JOIN
) будет убедиться , что клиенты , которые никогда не делали покупки, также включены.источник
Пожалуйста, попробуйте это,
источник