Как получить последние записи для комбинации из 2 столбцов? - PullRequest
2 голосов
/ 03 февраля 2020

У меня есть ситуация, которую, я думаю, можно сравнить с такими сервисами, как CamelCamelCamel, Keepa и так далее. Допустим, я отслеживаю цену за статью на каждый день для нескольких стран. Итак, моя таблица, назовем ее Trend, будет выглядеть примерно так

Id     Created      ArticleId    Country    Price
-------------------------------------------------
01     19/11/05     452          US         45.90
02     19/11/05     452          CA         52.99
03     19/11/05     452          MX         99.99
04     19/11/06     452          US         20.00
05     19/11/06     452          CA         25.00
06     19/11/06     452          MX         50.00
...                
97     19/11/05     738          US         12.99
98     19/11/05     738          CA         17.50
99     19/11/05     738          MX         45.50

Итак, это следующий день, и я хочу обновить таблицу Trend. Если цена в стране остается прежней, я пропускаю комбинацию артикул / страна. Если будет новая цена, я добавлю новую запись.

Теперь я хочу запросить таблицу, чтобы получить каждую комбинацию ArticleId / Country. Но только последняя запись (упорядоченная по метке времени). Поэтому, взяв приведенный выше пример, я ожидаю получить записи 04, 05 и 06 для ArticleId 452. Не 01, 02 и 03

Поэтому я начну с этого базового c запроса. Но как мне изменить его, чтобы получить ожидаемые результаты?

SELECT
    *
FROM
    Trend
ORDER BY 
    Created DESC

Ответы [ 2 ]

2 голосов
/ 03 февраля 2020

Один метод использует коррелированный подзапрос для фильтрации:

select t.*
from trend t
where t.created = (
    select max(t1.created) 
    from trend t1
    where t1.articleId = t.articleId and t1.country = t.country
)

Для производительности вам нужен индекс на (articleId, country, created).

Возможно, вы также захотите рассмотреть анти- left join подход:

select t.*
from trend t
left join trend t1 
    on  t1.articleId = t.articleId 
    and t1.country = t.country
    and t1.created > t.created
where t1.articleId is null

Наконец, еще одним типичным решением является объединение таблицы с помощью совокупного запроса:

select t.*
from trend t
inner join (
    select articleId, country, max(created) created
    from trend
    group by articleId, country
) t1 
    on  t1.articleId = t.articleId 
    and t1.country = t.country
    and t1.created = t.created

Какое решение работает лучше, зависит от размера и распределения ваших данных.

1 голос
/ 03 февраля 2020

Вы можете сделать это с помощью комбинации DISTINCT и CROSS APPLY.

SELECT DISTINCT ca.Id, ca.Created, t.ArticleId, t.Country, ca.Price
FROM Trend t
CROSS APPLY (SELECT TOP 1 Id, Created, Price
             FROM Trend
             WHERE ArticleId = t.ArticleId AND Country = t.Country
             ORDER BY Created DESC) ca

Часто при написании запросов с использованием APPLY ваши соединяющиеся столбцы (ArticleId и Country) делают уникальный ключ на другом столе. Если это относится к вашей базе данных, вы можете удалить DISTINCT и ускорить запрос.

SELECT ca.Id, ca.Created, a.ArticleId, a.Country, ca.Price
FROM Article a
CROSS APPLY (SELECT TOP 1 Id, Created, Price
             FROM Trend
             WHERE ArticleId = a.ArticleId AND Country = a.Country
             ORDER BY Created DESC) ca

Наконец, если у вас возникают проблемы с производительностью, вы можете создать индекс.

CREATE NONCLUSTERED INDEX [NC_Trend_ArticleId] ON [Trend]
(
    [ArticleId] ASC,
    [Country] ASC,
    [Created] ASC
)
INCLUDE ([Price])

Предположительно Id является PRIMARY KEY и уже покрывается CLUSTERED INDEX, если это так, вышеприведенное должно подходить для большинства решений.

...