为每个类别选择前10条记录

jbc*_*dge 189 sql sql-server sql-server-2005 greatest-n-per-group

我想在一个查询中返回每个部分的前10条记录.任何人都可以帮忙解决这个问题吗?Section是表中的一列.

数据库是SQL Server 2005.我想按输入的日期返回前10名.部分是业务,本地和功能.对于一个特定日期,我只想要前(10)个业务行(最近的条目),前(10)个本地行和前(10)个特征.

Dar*_*ler 201

如果你使用SQL 2005,你可以做这样的事情......

SELECT rs.Field1,rs.Field2 
    FROM (
        SELECT Field1,Field2, Rank() 
          over (Partition BY Section
                ORDER BY RankCriteria DESC ) AS Rank
        FROM table
        ) rs WHERE Rank <= 10
Run Code Online (Sandbox Code Playgroud)

如果您的RankCriteria有关联,那么您可能会返回超过10行,Matt的解决方案可能对您更好.

  • 如果你真的只想要前10名,请将其更改为RowNumber()而不是Rank().没关系. (29认同)
  • 对于使用Sql Server的任何人,Mike L提到的RowNumber()函数是ROW_NUMBER(). (12认同)
  • 这有效,但请注意,如果没有索引的*first*key是RankCriteria,则rank()可能会被查询规划器转换为完整的表排序.在这种情况下,您可以获得更好的里程数,选择不同的部分并交叉申请,以挑选RankCriteria desc排序的前10名. (3认同)

小智 83

在T-SQL中,我会这样做:

WITH TOPTEN AS (
    SELECT *, ROW_NUMBER() 
    over (
        PARTITION BY [group_by_field] 
        order by [prioritise_field]
    ) AS RowNo 
    FROM [table_name]
)
SELECT * FROM TOPTEN WHERE RowNo <= 10
Run Code Online (Sandbox Code Playgroud)

  • :请更详细地描述您的解决方案。参考:[如何回答](http://stackoverflow.com/questions/how-to-answer) (2认同)
  • 尽管您说“在 T-SQL 中”,但这适用于任何实现“ROW_NUMBER”函数的数据库。例如,我在 SQLite 中使用了这个解决方案。 (2认同)
  • 它也适用于 postgres sql。我只需使用“order by [prioritise_field] desc” (2认同)

Mat*_*ton 35

这适用于SQL Server 2005(编辑以反映您的澄清):

select *
from Things t
where t.ThingID in (
    select top 10 ThingID
    from Things tt
    where tt.Section = t.Section and tt.ThingDate = @Date
    order by tt.DateEntered desc
    )
    and t.ThingDate = @Date
order by Section, DateEntered desc
Run Code Online (Sandbox Code Playgroud)

  • 但是,对于Section为null的行,这不起作用.你需要说"where(tt.Section为null,t.Section为null)或tt.Section = t.Section" (2认同)

lor*_*ond 28

SELECT r.*
FROM
(
    SELECT
        r.*,
        ROW_NUMBER() OVER(PARTITION BY r.[SectionID] ORDER BY r.[DateEntered] DESC) rn
    FROM [Records] r
) r
WHERE r.rn <= 10
ORDER BY r.[DateEntered] DESC
Run Code Online (Sandbox Code Playgroud)


Bil*_*win 18

我是这样做的:

SELECT a.* FROM articles AS a
  LEFT JOIN articles AS a2 
    ON a.section = a2.section AND a.article_date <= a2.article_date
GROUP BY a.article_id
HAVING COUNT(*) <= 10;
Run Code Online (Sandbox Code Playgroud)

更新: GROUP BY的这个示例仅适用于MySQL和SQLite,因为这些数据库比关于GROUP BY的标准SQL更宽松.大多数SQL实现要求select-list中不属于聚合表达式的所有列也在GROUP BY中.


Vad*_*oda 11

如果我们使用SQL Server> = 2005,那么我们只能用一个select来解决任务:

declare @t table (
    Id      int ,
    Section int,
    Moment  date
);

insert into @t values
(   1   ,   1   , '2014-01-01'),
(   2   ,   1   , '2014-01-02'),
(   3   ,   1   , '2014-01-03'),
(   4   ,   1   , '2014-01-04'),
(   5   ,   1   , '2014-01-05'),

(   6   ,   2   , '2014-02-06'),
(   7   ,   2   , '2014-02-07'),
(   8   ,   2   , '2014-02-08'),
(   9   ,   2   , '2014-02-09'),
(   10  ,   2   , '2014-02-10'),

(   11  ,   3   , '2014-03-11'),
(   12  ,   3   , '2014-03-12'),
(   13  ,   3   , '2014-03-13'),
(   14  ,   3   , '2014-03-14'),
(   15  ,   3   , '2014-03-15');


-- TWO earliest records in each Section

select top 1 with ties
    Id, Section, Moment 
from
    @t
order by 
    case 
        when row_number() over(partition by Section order by Moment) <= 2 
        then 0 
        else 1 
    end;


-- THREE earliest records in each Section

select top 1 with ties
    Id, Section, Moment 
from
    @t
order by 
    case 
        when row_number() over(partition by Section order by Moment) <= 3 
        then 0 
        else 1 
    end;


-- three LATEST records in each Section

select top 1 with ties
    Id, Section, Moment 
from
    @t
order by 
    case 
        when row_number() over(partition by Section order by Moment desc) <= 3 
        then 0 
        else 1 
    end;
Run Code Online (Sandbox Code Playgroud)

  • TOP 1在这里与WITH TIES合作.WITH TIES表示当ORDER BY = 0时,SELECT接受此记录(因为TOP 1)和所有其他ORDER BY = 0(因为WITH TIES) (2认同)

Blo*_*ard 9

如果您知道这些部分是什么,您可以:

select top 10 * from table where section=1
union
select top 10 * from table where section=2
union
select top 10 * from table where section=3
Run Code Online (Sandbox Code Playgroud)

  • 当然,但引用OP:“部分是商业、本地和特色”。如果您有三个静态类别,这是最好的方法。 (5认同)
  • 这将是最简单的方法. (3认同)
  • 但是如果你有150或者类别按日,周等变化,这将是低效的. (3认同)

Dia*_*tis 8

我知道这个帖子有点旧,但我刚刚遇到了类似的问题(从每个类别中选择最新的文章),这就是我提出的解决方案:

WITH [TopCategoryArticles] AS (
    SELECT 
        [ArticleID],
        ROW_NUMBER() OVER (
            PARTITION BY [ArticleCategoryID]
            ORDER BY [ArticleDate] DESC
        ) AS [Order]
    FROM [dbo].[Articles]
)
SELECT [Articles].* 
FROM 
    [TopCategoryArticles] LEFT JOIN 
    [dbo].[Articles] ON
        [TopCategoryArticles].[ArticleID] = [Articles].[ArticleID]
WHERE [TopCategoryArticles].[Order] = 1
Run Code Online (Sandbox Code Playgroud)

这与Darrel的解决方案非常相似,但克服了可能返回比预期更多行的RANK问题.


小智 6

Q) 从每个组中查找 TOP X 条记录(Oracle)

SQL> select * from emp e 
  2  where e.empno in (select d.empno from emp d 
  3  where d.deptno=e.deptno and rownum<3)
  4  order by deptno
  5  ;

 EMPNO ENAME      JOB              MGR HIREDATE         SAL       COMM     DEPTNO
Run Code Online (Sandbox Code Playgroud)
  7782 CLARK      MANAGER         7839 09-JUN-81       2450                    10
  7839 KING       PRESIDENT            17-NOV-81       5000                    10
  7369 SMITH      CLERK           7902 17-DEC-80        800                    20
  7566 JONES      MANAGER         7839 02-APR-81       2975                    20
  7499 ALLEN      SALESMAN        7698 20-FEB-81       1600        300         30
  7521 WARD       SALESMAN        7698 22-FEB-81       1250        500         30
Run Code Online (Sandbox Code Playgroud)

选择了 6 行。



Cra*_*aig 6

如果要生成按部分分组的输出,则只显示每个部分的前n条记录,如下所示:

SECTION     SUBSECTION

deer        American Elk/Wapiti
deer        Chinese Water Deer
dog         Cocker Spaniel
dog         German Shephard
horse       Appaloosa
horse       Morgan
Run Code Online (Sandbox Code Playgroud)

...那么以下内容应该适用于所有 SQL 数据库。如果您想要前 10 名,只需在查询结束时将 2 更改为 10。

select
    x1.section
    , x1.subsection
from example x1
where
    (
    select count(*)
    from example x2
    where x2.section = x1.section
    and x2.subsection <= x1.subsection
    ) <= 2
order by section, subsection;
Run Code Online (Sandbox Code Playgroud)

建立:

create table example ( id int, section varchar(25), subsection varchar(25) );

insert into example select 0, 'dog', 'Labrador Retriever';
insert into example select 1, 'deer', 'Whitetail';
insert into example select 2, 'horse', 'Morgan';
insert into example select 3, 'horse', 'Tarpan';
insert into example select 4, 'deer', 'Row';
insert into example select 5, 'horse', 'Appaloosa';
insert into example select 6, 'dog', 'German Shephard';
insert into example select 7, 'horse', 'Thoroughbred';
insert into example select 8, 'dog', 'Mutt';
insert into example select 9, 'horse', 'Welara Pony';
insert into example select 10, 'dog', 'Cocker Spaniel';
insert into example select 11, 'deer', 'American Elk/Wapiti';
insert into example select 12, 'horse', 'Shetland Pony';
insert into example select 13, 'deer', 'Chinese Water Deer';
insert into example select 14, 'deer', 'Fallow';
Run Code Online (Sandbox Code Playgroud)


小智 5

尝试了以下内容,它也与关系有效.

SELECT rs.Field1,rs.Field2 
FROM (
    SELECT Field1,Field2, ROW_NUMBER() 
      OVER (Partition BY Section
            ORDER BY RankCriteria DESC ) AS Rank
    FROM table
    ) rs WHERE Rank <= 10
Run Code Online (Sandbox Code Playgroud)

  • 重复别人的回答有什么意义? (2认同)

Luk*_*der 5

虽然问题是关于 SQL Server 2005,但大多数人已经继续前进,如果他们确实找到了这个问题,那么在其他情况下,首选答案可能是使用这篇CROSS APPLY博客文章中所示的答案。

SELECT *
FROM t
CROSS APPLY (
  SELECT TOP 10 u.*
  FROM u
  WHERE u.t_id = t.t_id
  ORDER BY u.something DESC
) u
Run Code Online (Sandbox Code Playgroud)

该查询涉及2个表。OP 的查询仅涉及 1 个表,在这种情况下,基于窗口函数的解决方案可能会更有效。


sbl*_*ndy 4

UNION运算符可能适合您吗?每个部分都有一个 SELECT,然后将它们联合在一起。我猜它只适用于固定数量的部分。