2009-12-10 107 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