Я боролся с проблемой, возникшей в TSQL, мне нужно получить 10 лучших результатов для каждого пользователя из таблицы, которая может содержать более 10 результатов.
Мой естественный (и процедурно-ориентированный) подход - «для каждого пользователя в таблице T выберите 10 лучших результатов, упорядоченных по дате».
Каждый раз, когда я пытаюсь сформулировать вопрос в своем уме с помощью подхода, основанного на множестве, я все время сталкиваюсь с термином «foreach».
Можно ли сделать что-то вроде этого:
SELECT *
FROM table AS t1
INNER JOIN (
SELECT TOP 10 *
FROM table AS t2
WHERE t2.id = t1.id
ORDER BY date DESC
)
Или даже
SELECT ( SELECT TOP 10 *
FROM table AS t2
WHERE t2.id = t1.id
ORDER BY date )
FROM table AS t1
Или есть другое решение с использованием временных таблиц, о которых мне следует подумать?
EDIT:
Просто чтобы быть совершенно ясным - мне нужно 10 лучших результатов для каждого пользователя в таблице, например, 10 * N , где N = количество пользователей.
EDIT:
В ответ на предложение RBarryYoung возникла проблема, которую лучше всего продемонстрировать с помощью кода:
CREATE TABLE #temp (id INT, date DATETIME)
INSERT INTO #temp (id, date) VALUES (1, GETDATE())
INSERT INTO #temp (id, date) VALUES (1, GETDATE())
SELECT *
FROM #temp AS t1
CROSS APPLY (
SELECT TOP 1 *
FROM #temp AS t2
WHERE t2.id = t1.id
ORDER BY t2.date DESC
) AS t2
DROP TABLE #temp
Запустив это, вы можете видеть, что это не ограничивает результаты ТОП 1 ... Я что-то здесь не так делаю?
EDIT:
Похоже, мой последний пример немного сбил с толку. Вот пример, показывающий, что я хочу сделать:
CREATE TABLE #temp (id INT, date DATETIME)
INSERT INTO #temp (id, date) VALUES (1, GETDATE())
INSERT INTO #temp (id, date) VALUES (1, GETDATE())
INSERT INTO #temp (id, date) VALUES (1, GETDATE())
INSERT INTO #temp (id, date) VALUES (2, GETDATE())
SELECT *
FROM #temp AS t1
CROSS APPLY
(
SELECT TOP 2 *
FROM #temp AS t2
WHERE t2.id = t1.id
ORDER BY t2.date DESC
) AS t2
DROP TABLE #temp
Это выводит:
1 2009-08-26 09:05:56.570 1 2009-08-26 09:05:56.583
1 2009-08-26 09:05:56.570 1 2009-08-26 09:05:56.583
1 2009-08-26 09:05:56.583 1 2009-08-26 09:05:56.583
1 2009-08-26 09:05:56.583 1 2009-08-26 09:05:56.583
1 2009-08-26 09:05:56.583 1 2009-08-26 09:05:56.583
1 2009-08-26 09:05:56.583 1 2009-08-26 09:05:56.583
2 2009-08-26 09:05:56.583 2 2009-08-26 09:05:56.583
Если я использую разные:
SELECT DISTINCT t1.id
FROM #temp AS t1
CROSS APPLY
(
SELECT TOP 2 *
FROM #temp AS t2
WHERE t2.id = t1.id
ORDER BY t2.date DESC
) AS t2
Я получаю
1
2
мне нужно
1
1
2
Кто-нибудь знает, возможно ли это?
EDIT:
Следующий код сделает это
WITH RowTable AS
(
SELECT
id, date, ROW_NUMBER() OVER (PARTITION BY id ORDER BY date DESC) AS RowNum
FROM #temp
)
SELECT *
FROM RowTable
WHERE RowNum <= 2;
Я написал в комментариях, но форматирование кода отсутствует, поэтому выглядит не очень хорошо.