我遇到了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
运行这个,你可以看到这并没有将结果限制在TOP 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
如果我使用distinct:
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;
我在评论中发帖,但没有代码格式,所以看起来不太好。
答案 0 :(得分:4)
是的,在2005年和2008年有几种不同的好方法可以做到这一点。与你已经尝试过的最相似的是CROSS APPLY:
SELECT T2.*
FROM (
SELECT DISTINCT ID FROM table
) AS t1
CROSS APPLY (
SELECT TOP 10 *
FROM table AS t2
WHERE t2.id = t1.id
ORDER BY date DESC
) AS t2
ORDER BY T2.id, date DESC
然后,对于每个不同的[id],它返回[table]中的十个最近的条目(或者尽可能多的条目,最多10个)。假设[id]对应于用户,那么这应该是你要求的。
(编辑:略有变化,因为我没有考虑T1和T2是相同的表,因此会有多个重复的t1.ID匹配多个重复的T2.ids。)
答案 1 :(得分:2)
select userid, foo, row_number() over (partition by userid order by foo) as rownum from table where rownum <= 10
答案 2 :(得分:0)
但是,使用嵌套查询可能会更慢。
以下内容还将找到您要查找的结果:
SELECT TOP 10 *
FROM table as t1
INNER JOIN table as t2
ON t1.id = t2.id
ORDER BY date DESC
答案 3 :(得分:0)
我相信这SO question会回答你的问题。它没有回答完全相同的问题,但我认为解决方案也适用于你。
答案 4 :(得分:0)
这是我用来执行“top-N-per-group”类型查询的技巧:
SELECT t1.id
FROM table t1 LEFT OUTER JOIN table t2
ON (t1.user_id = t2.user_id AND (t1.date > t2.date
OR t1.date = t2.date AND t1.id > t2.id))
GROUP BY t1.id
HAVING COUNT(*) < 10
ORDER BY t1.user_id, COALESCE(COUNT(*), 0);