Я борюсь с проблемой в 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
Или есть другое решение с использованием временных таблиц, о котором я должен подумать?
ИЗМЕНИТЬ:
Просто чтобы быть совершенно ясным - мне нужно 10 лучших результатов для каждого пользователя в таблице, например. 10 * N, где N = количество пользователей.
ИЗМЕНИТЬ:
В ответ на предложение 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 ... Я делаю что-то не так?
ИЗМЕНИТЬ:
Кажется, мой последний пример вызвал некоторую путаницу. Вот пример, показывающий, что я хочу сделать:
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
Кто-нибудь знает возможно ли это?
ИЗМЕНИТЬ:
Следующий код сделает это
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;
Писал в комментариях, но форматирования кода нет, поэтому выглядит не очень красиво.