TSQL难以加入问题

时间:2009-08-25 00:18:02

标签: tsql join

我遇到了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;

我在评论中发帖,但没有代码格式,所以看起来不太好。

5 个答案:

答案 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);