【问题标题】:How do i get top n records of each category我如何获得每个类别的前 n 条记录
【发布时间】:2010-09-01 04:49:40
【问题描述】:

我是来按类别获取记录的。

我的表 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 条记录

【问题讨论】:

    标签: sql mysql sql-server tsql plsql


    【解决方案1】:

    对于 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
    

    【讨论】:

      【解决方案2】:

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

      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 是您需要获取的记录数量。

      【讨论】:

      • 嗯...非常有趣和不错的...单张唱片。可以修改前n条记录吗
      【解决方案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
      

      【讨论】:

      • 不,它来自 sql 2005 +。 SQL 2000 需要它吗?
      【解决方案4】:

      使用 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!-)。

      【讨论】:

        【解决方案5】:

        这是另一种方式

            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
        

        【讨论】:

          猜你喜欢
          • 2019-01-17
          • 2018-12-12
          • 1970-01-01
          • 2018-03-18
          • 2012-04-06
          • 2021-03-15
          相关资源
          最近更新 更多