각 카테고리마다 상위 10 개 레코드를 선택하십시오.


207

하나의 쿼리로 각 섹션에서 상위 10 개의 레코드를 반환하고 싶습니다. 누구나 그것을하는 방법을 도울 수 있습니까? 섹션은 테이블의 열 중 하나입니다.

데이터베이스는 SQL Server 2005입니다. 입력 한 날짜별로 상위 10 개를 반환하려고합니다. 섹션은 비즈니스, 지역 및 기능입니다. 특정 날짜의 경우 상위 10 개 비즈니스 행 (가장 최근 항목), 상위 10 개 로컬 행 및 상위 10 개 기능 만 원합니다.


이 답변 중 어느 것이 당신에게 효과가 있었습니까?
Kyle Delaney

3
나는 우리가 결코 알지 못할 것 같다 ...
Denny

12 년이 지났는데 그 중 어느 것이 효과가 있었는지 우리는 모른다.
아로마

답변:


221

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

RankCriteria에 관계가 있으면 10 개가 넘는 행을 반환 할 수 있으며 Matt의 솔루션이 더 나을 수 있습니다.


31
정말로 상위 10 개를 원한다면 Rank () 대신 RowNumber ()로 변경하십시오. 그러면 아무런 유대가 없습니다.
Mike L

3
이것은 작동하지만 첫 번째 키가 RankCriteria 인 인덱스가 없으면 rank ()가 쿼리 플래너에 의해 전체 테이블 정렬로 바뀔 가능성이 있습니다 . 이 경우, 개별 구간을 선택하고 RankCriteria desc에서 주문한 상위 10 개 항목을 선택하기 위해 교차 신청하는 것이 좋습니다.
Joe Kearney

좋은 답변입니다! 내가 필요한 것을 거의 정확하게 얻었습니다. 나는 DENSE_RANK번호 매기기에 공백이없는 것으로 끝났습니다 . +1
Michael Stramel

1
@Facbed 테이블의 별칭 일뿐입니다.
Darrel Miller

15
Sql Server를 사용하는 사람이라면 Mike L이 언급 한 RowNumber () 함수는 ROW_NUMBER ()입니다.
randomraccoon

99

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

2
: 솔루션에 대해 더 자세히 설명하십시오. 참조 : 답변 방법
askmish

CTE의 select 쿼리에 where 절이 포함될 수 있습니까?
toha

1
@toha 그렇습니다
KindaTechy

1
"T-SQL에서"라고 말하지만 ROW_NUMBER함수를 구현하는 모든 데이터베이스에서 작동합니다. 예를 들어, SQLite에서이 솔루션을 사용했습니다.
Tony

postgres SQL에서도 작동합니다. 난 그냥 "[prioritise_field] 내림차순으로 순서"사용했다
Phun

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

2
그러나 Section이 null 인 행에서는 작동하지 않습니다. "tt. 섹션이 null이고 t.Section이 null 인 경우"또는 tt.Section = t.Section "이라고 말해야합니다.
Matt Hamilton

29
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

별칭이 'm'인 테이블은 무엇입니까?
Chalky

@Chalky 오타 r입니다. 결정된.
lorond

매력처럼 일했다. 감사합니다!
Ron Nuni

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;

업데이트 : 이 GROUP BY 예제는 MySQL 및 SQLite에서만 작동합니다. 데이터베이스는 GROUP BY에 관한 표준 SQL보다 더 관대합니다. 대부분의 SQL 구현에서는 집계 표현식의 일부가 아닌 선택 목록의 모든 열도 GROUP BY에 있어야합니다.


1
작동합니까? article_id를 제외한 기사의 모든 열에 대해 "a.somecolumn이 집계 함수 또는 그룹 별 절에 포함되어 있지 않기 때문에 선택 목록에서 유효하지 않습니다"라고 확신합니다.
Blorgbeard가

1
GROUP BY에 명명 된 열에 기능적으로 종속 된 다른 열을 포함 할 수 있어야합니다. 기능적으로 종속되지 않은 열은 모호합니다. 그러나 RDBMS 구현에 따라 맞습니다. MySQL에서는 작동하지만 InterBase / Firebird에서는 IIRC가 실패합니다.
Bill Karwin

1
섹션의 상위 11 개 레코드가 모두 같은 날짜를 가진 경우에이 작업이 작동합니까? 그들은 모두 카운트 11을 가지며 결과는 빈 세트가됩니다.
Arth

아니요, 날짜가 모두 같은 관계를 끊는 방법이 필요합니다. 예제는 stackoverflow.com/questions/121387/… 을 참조하십시오 .
Bill Karwin

1
@carlosgg, 기사가 섹션과 다 대다 관계인 경우 기사를 섹션에 매핑하려면 교차 테이블이 필요합니다. 그런 다음 쿼리는 m2m 관계에 대한 교차 테이블에 조인하고 article_id 및 섹션별로 그룹화해야합니다. 그것은 당신을 시작해야하지만 주석에 전체 솔루션을 쓰지는 않을 것입니다.
Bill Karwin

16

SQL Server> = 2005를 사용하면 한 번의 선택 만으로 작업을 해결할 수 있습니다 .

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;

1
+1이 솔루션이 간단하기 때문에이 솔루션이 마음에 들지만 0 또는 1을 반환 하는 절의 문에서 어떻게 top 1작동 하는지 설명 할 수 있습니까? caseorder by
Ceres

3
TOP 1은 WITH TIES와 함께 작동합니다. WITH TIES는 ORDER BY = 0 인 경우 SELECT가이 레코드 (TOP 1 때문에)와 ORDER BY = 0 인 WITH WITH TIES 때문에 모든 레코드를 가져옵니다.
Vadim Loboda 2016 년

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

3
이것이 가장 쉬운 방법입니다.
Hector Sosa Jr 2

3
그러나 150 개가 있거나 일, 주 등으로 범주가 가변적 인 경우 비효율적입니다.
Rafa Barragan

1
물론 OP를 인용하자면 "섹션은 비즈니스, 지역 및 기능"입니다. 정적 범주가 세 개인 경우 가장 좋은 방법입니다.
Blorgbeard는

9

이 스레드가 조금 오래되었다는 것을 알고 있지만 비슷한 문제가 발생했습니다 (각 범주에서 최신 기사를 선택하십시오). 이것은 내가 생각해 낸 해결책입니다.

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

이것은 Darrel의 솔루션과 매우 유사하지만 의도 한 것보다 많은 행을 리턴 할 수있는 RANK 문제를 극복합니다.


왜 CTE 선생님을 사용합니까? 메모리 소비를 줄입니까?
toha

CTE를 간단하고 쉽게 때문에 @toha 이해
반전 엔지니어

좋은 대답 !! 해당 레코드가 없으면 레코드가 없기 때문에 내부 JOIN대신를 사용하여 최적화 할 수 있습니다 . LEFT JOINTopCategoryArticlesArticle
리버스 엔지니어

6

다음을 시도하고 넥타이로도 작업했습니다.

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

5

섹션별로 그룹화 된 출력을 생성하려면 다음과 같이 각 섹션에서 최상위 n 개의 레코드 만 표시 하십시오.

SECTION     SUBSECTION

deer        American Elk/Wapiti
deer        Chinese Water Deer
dog         Cocker Spaniel
dog         German Shephard
horse       Appaloosa
horse       Morgan

... 다음은 모든 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;

설정하기:

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';

각 섹션의 첫 번째 레코드 만 원할 때 작동하지 않습니다. 레코드가 두 개 이상인 모든 섹션 그룹을 제거합니다.
14:52에

@nils 사슴, 개, 말의 세 가지 섹션 값만 있습니다. 쿼리를 <= 1로 변경하면 각 섹션마다 사슴에 대한 미국 엘크 / 와피 티, 개에 대한 코커 스패니얼, 말에 대한 애팔 로사와 같은 하위 섹션이 있습니다. 이것들은 또한 각 섹션에서 알파벳순으로 첫 번째 값입니다. 쿼리가되는 의미가 다른 값을 모두 제거 할 수 있습니다.
Craig

그러나 쿼리를 실행하려고하면 모든 것이 카운트 = 1이기 때문에 모든 것을 제거합니다. 각 섹션의 첫 번째 하위 섹션을 유지하지 않습니다. <= 1에 대한 쿼리를 실행하려고하면 각 섹션의 첫 번째 하위 섹션이 있는지 알려주십시오.
nils

@nils 안녕하세요, 스크립트 에서이 작은 테스트 데이터베이스를 다시 만들고 <= 1을 사용하여 쿼리를 실행했으며 각 섹션에서 첫 번째 하위 섹션 값을 반환했습니다. 어떤 데이터베이스 서버를 사용하고 있습니까? 항상 선택한 데이터베이스와 관련이있을 가능성이 있습니다. 편리하고 예상대로 작동하기 때문에 MySQL에서 방금 실행했습니다. 나는 처음으로 그것을했을 때 확신합니다 (내가 게시 한 내용이 실제로 디버그없이 작동하는지 확인하고 싶었습니다). 나는 Sybase SQL Anywhere 또는 MS SQL Server를 사용하여 그것을했다고 확신합니다.
Craig

그것은 mysql에서 완벽하게 작동했습니다. 나는 그가 사용 않았는지 쿼리 조금 확실하지 비트 변경 <= 하위 섹션의 VARCHAR 필드에 .. 나는 그것을 변경하고 x2.subsection = x1.subsection
Mahen Nakar

4

세력 연합 당신을 위해 운영자 작업은? 각 섹션에 대해 하나의 SELECT를 선택한 다음 UNION을 함께 사용하십시오. 그래도 고정 된 수의 섹션에서만 작동한다고 생각합니다.


4

Q) 각 그룹 (Oracle)에서 TOP X 레코드 찾기

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

  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

6 개 행이 선택되었습니다.



문제는 Oracle이 아니라 SQL Server에 관한 것입니다.
Craig

2

질문은 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

이 쿼리에는 2 개의 테이블이 포함됩니다. OP의 쿼리에는 1 개의 테이블 만 포함되며,이 경우 창 함수 기반 솔루션이 더 효율적일 수 있습니다.


1

이 방법을 시도해 볼 수 있습니다. 이 쿼리는 각 국가에서 가장 인구가 많은 도시 10 개를 반환합니다.

   SELECT city, country, population
   FROM
   (SELECT city, country, population, 
   @country_rank := IF(@current_country = country, @country_rank + 1, 1) AS country_rank,
   @current_country := country 
   FROM cities
   ORDER BY country, population DESC
   ) ranked
   WHERE country_rank <= 10;

예를 들어 인구가 9 인 한 국가의 레코드가있는 테이블이있는 경우이 솔루션은 테스트 사례를 통과하지 못합니다. 예를 들어 9 개의 사용 가능한 레코드를 모두 순서대로 반환하는 대신 null을 반환합니다. 이 문제를 해결하기위한 제안이 있습니까?
Mojgan Mazouchi
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.