我如何获得每个类别的前n个记录

时间:2010-09-01 04:49:40

标签: sql mysql sql-server tsql plsql

我来这里是为了根据类别获取记录。

我的表 foo 包含字段 [id,name,class] 。我的记录可以是:

1, ram, 10
2, hari, 9
3, sita, 10
4, gita, 9
5, rita, 5
6, tina, 7
8, nita, 8
9, bita, 5
10,seta, 7

......还有更多......

现在我想得到来自不同班级的每条记录的结果......即类似

1, ram, 10
2, hari, 9
5, rita, 5
6, tina, 7
8, nita, 8

,即根据班级 1 记录

5 个答案:

答案 0 :(得分:6)

对于SQL Server 2005+和Oracle 9i +,请使用分析函数:

WITH summary AS (
  SELECT f.id,
         f.name,
         f.class,
         ROW_NUMBER() OVER (PARTITION BY f.class
                                ORDER BY f.name) AS rank
    FROM FOO f)
SELECT s.id,
       s.name,
       s.class
  FROM summary s
 WHERE s.rank = 1

这也使用公用表表达式(CTE),在Oracle中称为子查询因子...

MySQL没有分析功能支持,所以你必须使用:

SELECT x.id,
       x.name,
       x.class
  FROM (SELECT f.id,
               f.name,
               f.class,
               CASE 
                 WHEN @class = f.class THEN @rownum := @rownum + 1 
                 ELSE @rownum := 1
               END AS rank,
               @class := f.class
          FROM FOO f
          JOIN (SELECT @rownum := 0, @class := '') r
      ORDER BY f.class, f.name) x
 WHERE x.rank = 1

答案 1 :(得分:4)

这应该是最简单的方法,不涉及任何特定于数据库的选项:

select * 
  from foo 
 where id in (select min(id) 
                from foo 
               group by class);

upd:是的,当然只有你每个班级只需要一条记录时这才有效。

upd2:只是为了好玩,想出一个查询,向您显示TOP N并且不涉及分析。看起来有点凌乱,但似乎工作:)

select newfoo.id, newfoo.name, newfoo.class
  from (select class, max(r) top, min(r) bottom
          from (select f.*, rownum r
                  from (select id, name, class from foo order by class, id asc) f)
         group by class) minmax,
       (select id, name, class, r
          from (select f.*, rownum r
                  from (select id, name, class from foo order by class, id asc) f)) newfoo
 where newfoo.class = minmax.class
   and newfoo.r between minmax.bottom and
       least(minmax.bottom + (TOP_N-1), minmax.top);

其中TOP_N是您需要获取的记录数量。

答案 2 :(得分:3)

我在sql 2008中测试了这个并且对我有用,希望能以某种方式帮助你。

DECLARE @Class TABLE
(
    id INT
    ,Name NVARCHAR(120)
    ,Class INT

    PRIMARY KEY (id)
)

INSERT INTO @Class values (1, 'ram', 10)
INSERT INTO @Class values (2, 'hari', 9)
INSERT INTO @Class values (3, 'sita', 10)
INSERT INTO @Class values (4, 'gita', 9)
INSERT INTO @Class values (5, 'rita', 5)
INSERT INTO @Class values (6, 'tina', 7)
INSERT INTO @Class values (8, 'nita', 8)
INSERT INTO @Class values (9, 'bita', 5)
INSERT INTO @Class values (10, 'seta', 7)

SELECT A.id, A.Name, A.Class
FROM
(
    SELECT ROW_NUMBER() OVER (PARTITION BY Class ORDER BY ID) as Num, ID, Name, Class
    FROM @Class
) A
WHERE A.Num = 1
ORDER BY id

答案 3 :(得分:1)

使用SQL Server或Oracle(或实现该部分标准的任何其他引擎,包括免费的PostgreSQL),OVER子句中的“窗口函数”(例如,参见here对于MS的文档,关于它们)让它变得简单;例如,在this SO question中,请参阅@Darrel的答案(他选择了每个类别的前10名,你只需要前1名,变化应该是显而易见的; - )。

在MySql或其他不遵守有关OVER条款的标准的引擎中,您可以使用@Bill的答案(适用于MySql,不适用于其他人)或@Matt(可能需要稍微改编,因为他正在回答SQL Server等使用SELECT TOP 10 ... - 在MySql中SELECT ... LIMIT 10! - )。

答案 4 :(得分:0)

这是另一种方式

    DECLARE @foo TABLE(ID INT,Name VARCHAR(20),Class INT)
INSERT INTO @foo
SELECT 1,'ram', 10 UNION ALL
SELECT 2, 'hari', 9 UNION ALL 
SELECT 3, 'sita', 10  UNION ALL
SELECT 4, 'gita', 9  UNION ALL
SELECT 5, 'rita', 5  UNION ALL
SELECT 6, 'tina', 7  UNION ALL
SELECT 8, 'nita', 8  UNION ALL
SELECT 9, 'bita', 5  UNION ALL
SELECT 10,'seta', 7

SELECT DISTINCT X.*
FROM @foo f
CROSS APPLY(SELECT TOP 1 * FROM @foo WHERE Class = f.Class) AS X
相关问题