2009-12-10 147 views
4

考虑像联盟和ORDER BY

tbl_ranks 
-------------------------------- 
family_id | item_id | view_count 
-------------------------------- 
1   10  101 
1   11  112 
1   13  109 

2   21  101 
2   22  112 
2   23  109 

3   30  101 
3   31  112 
3   33  109 

4   40  101 
4   51  112 
4   63  109 

5   80  101 
5   81  112 
5   88  109 

表我需要生成与前两名(2)行设置家庭ID的子集的结果(比如,1,2,3和4)按查看次数排序。 我想这样做

select top 2 * from tbl_ranks where family_id = 1 order by view_count 
union all 
select top 2 * from tbl_ranks where family_id = 2 order by view_count 
union all 
select top 2 * from tbl_ranks where family_id = 3 order by view_count 
union all 
select top 2 * from tbl_ranks where family_id = 4 order by view_count 

但是,当然,为了通过是无效的工会所有以该方式上下文。有什么建议么?我知道我可以运行一组4个查询,存储结果到一个临时表,并选择温度作为最终结果的内容,但我宁愿避免使用临时表如果可能的话。

注:在真正的应用程序,每个家庭的ID记录数是不确定的,并且view_counts,因为它们出现在上面的例子也并不固定。

回答

1

你可以尝试这样的事情

DECLARE @tbl_ranks TABLE(
     family_id INT, 
     item_id INT, 
     view_count INT 
) 

INSERT INTO @tbl_ranks SELECT 1,10,101 
INSERT INTO @tbl_ranks SELECT 1,11,112 
INSERT INTO @tbl_ranks SELECT 1,13,109 

INSERT INTO @tbl_ranks SELECT 2,21,101 
INSERT INTO @tbl_ranks SELECT 2,22,112 
INSERT INTO @tbl_ranks SELECT 2,23,109 

INSERT INTO @tbl_ranks SELECT 3,30,101 
INSERT INTO @tbl_ranks SELECT 3,31,112 
INSERT INTO @tbl_ranks SELECT 3,33,109 

INSERT INTO @tbl_ranks SELECT 4,40,101 
INSERT INTO @tbl_ranks SELECT 4,51,112 
INSERT INTO @tbl_ranks SELECT 4,63,109 

INSERT INTO @tbl_ranks SELECT 5,80,101 
INSERT INTO @tbl_ranks SELECT 5,81,112 
INSERT INTO @tbl_ranks SELECT 5,88,109 

SELECT * 
FROm (
      SELECT *, 
        ROW_NUMBER() OVER(PARTITION BY family_id ORDER BY view_count DESC) MyOrder 
      FROM @tbl_ranks 
     ) MyOrders 
WHERE MyOrder <= 2 
+0

我修改这个使用CTE来代替嵌套选择的,但除此之外,它是完美的。谢谢! – 2009-12-10 18:40:12

1

用途:

SELECT * 
    FROM (select *, 
       ROW_NUMBER() OVER (PARTITION BY family_id ORDER BY view_count DESC) 'rank' 
      from tbl_ranks) x 
    WHERE x.rank <= 2 
ORDER BY ... 

的理由是分配一个排名,然后在此基础上进行过滤。

+0

也许应该使用select子访问排名列。 – 2009-12-10 18:06:33

+0

我不相信在WHERE谓词中可以使用rank列。分析在*谓词之后进行评估*,因此您需要一个子选择才能使其工作。 – LBushkin 2009-12-10 18:46:09

1
SELECT tro.* 
FROM family 
CROSS APPLY 
     (
     SELECT TOP 2 * 
     FROM tbl_ranks tr 
     WHERE tr.family_id = family.id 
     ORDER BY 
       view_count DESC 
     ) tro 
WHERE family.id IN (1, 2, 3, 4) 

如果你没有一个实际的family表,您可以使用一组工会或者递归CTE构建它:

WITH family AS 
     (
     SELECT 1 AS id 
     UNION ALL 
     SELECT 2 AS id 
     UNION ALL 
     SELECT 3 AS id 
     UNION ALL 
     SELECT 4 AS id 
     ) 
SELECT tro.* 
FROM family 
CROSS APPLY 
     (
     SELECT TOP 2 * 
     FROM tbl_ranks tr 
     WHERE tr.family_id = family.id 
     ORDER BY 
       view_count DESC 
     ) tro 
WHERE family.id IN (1, 2, 3, 4) 

确保您在tbl_ranks (family_id, viewcount)有一个索引。

这将是有效的,如果你有大量的每个家庭行列的,因为如果与PARTITION BY使用像ROW_NUMBER解析功能将无法使用TOP方法。

1

如果您使用SQL Server 2005或更高版本,可以利用分析功能优势:

SELECT * FROM (
    SELECT rank() OVER (PARTITION BY family_id ORDER BY view_count) AS RNK, * FROM ... 
    ) 
WHERE RNK <= 2 
ORDER BY ... 
1

你只需要调整您建议SQL命令有点让它像你想要的那样工作。要绑定TOP和ORDER BY你可以把里面你从选择paranthesis的发言,并给出一个名称(这里没有使用,但需要)。

Adriaan Stander's answer的声明和INSERT语句下面

SELECT * FROM (SELECT TOP 2 * FROM @tbl_ranks WHERE family_id = 1 ORDER BY view_count) AS dummy1 UNION ALL 
SELECT * FROM (SELECT TOP 2 * FROM @tbl_ranks WHERE family_id = 2 ORDER BY view_count) AS dummy2 UNION ALL 
SELECT * FROM (SELECT TOP 2 * FROM @tbl_ranks WHERE family_id = 3 ORDER BY view_count) AS dummy3 UNION ALL 
SELECT * FROM (SELECT TOP 2 * FROM @tbl_ranks WHERE family_id = 4 ORDER BY view_count) AS dummy4 

family_id item_id view_count 
1 10 101 
1 13 109 
2 21 101 
2 23 109 
3 30 101 
3 33 109 
4 40 101 
4 63 109