각 그룹의 첫 번째 행을 선택하는 방법은 무엇입니까?


57

나는 이와 같은 테이블을 가지고있다 :

 ID |  Val   |  Kind
----------------------
 1  |  1337  |   2
 2  |  1337  |   1
 3  |   3    |   4
 4  |   3    |   4

을 기준으로 순서대로 SELECT각 행의 첫 번째 행만 반환 하도록하고 싶습니다 .ValKind

샘플 출력 :

 ID |  Val   |  Kind
----------------------
 2  |  1337  |   1
 3  |   3    |   4

이 쿼리를 어떻게 만들 수 있습니까?


왜 3 | 3 | 4이 아닌 4 | 3 | 4-동점 무엇입니까?
Jack Douglas

@JackDouglas 실제로 나는을 가지고 ORDER BY ID DESC있지만 질문과 관련이 없습니다. 이 예에서는 상관하지 않습니다.
BrunoLM

답변:


38

이 용액은 사용 keep하지만 valkind단순히 부질없이 각 그룹에 대해 계산 될 수있다 :

select min(id) keep(dense_rank first order by kind) id
     , val
     , min(kind) kind
  from mytable
 group by val;
아이디 | 발 | 종류
-: | --- : | --- :
 3 | 3 | 4
 2 | 1337 | 1

여기 dbfiddle

KEEP… FIRST 및 KEEP… LAST는 오라클 고유의 집계 기능입니다. 여기서 Oracle 문서 또는 ORACLE_BASE 에서 읽을 수 있습니다 .

FIRST 및 LAST 함수를 사용하여 순서가 지정된 순서에서 첫 번째 또는 마지막 값을 반환 할 수 있습니다


62

용도 공통 테이블 식 (CTE)와 같은 윈도우 / 순위 / 파티션 기능 ROW_NUMBER를 .

이 쿼리는 ORDERED라는 메모리 내 테이블을 만들고 1에서 N까지의 일련의 숫자 인 rn의 추가 열을 추가합니다. PARTITION BY 는 Val 값이 변경 될 때마다 1에서 다시 시작해야한다는 것을 나타냅니다. Kind의 가장 작은 값으로 행.

WITH ORDERED AS
(
SELECT
    ID
,   Val
,   kind
,   ROW_NUMBER() OVER (PARTITION BY Val ORDER BY Kind ASC) AS rn
FROM
    mytable
)
SELECT
    ID
,   Val
,   Kind
FROM
    ORDERED
WHERE
    rn = 1;

위의 접근 방식은 ROW_NUMBER () 함수를 구현 한 모든 RDBMS에서 작동합니다. 오라클은 mik의 답변 으로 표현 된 우아한 기능을 가지고 있으며이 답변 보다 일반적으로 더 나은 성능을 제공합니다.


25

bilinkc의 해결책은 잘 작동하지만, 나는 광산을 던져 버릴 것이라고 생각했습니다. 비용은 같지만 더 빠를 수도 있습니다 (또는 느리게 테스트하지는 않았습니다). 차이점은 Row_Number 대신 First_Value를 사용한다는 것입니다. 우리는 첫 번째 가치에만 관심이 있기 때문에 내 생각에는 더 간단합니다.

SELECT ID, Val, Kind FROM
(
   SELECT First_Value(ID) OVER (PARTITION BY Val ORDER BY Kind) First, ID, Val, Kind 
   FROM mytable
)
WHERE ID = First;

테스트 데이터.

--drop table mytable;
create table mytable (ID Number(5) Primary Key, Val Number(5), Kind Number(5));

insert into mytable values (1,1337,2);
insert into mytable values (2,1337,1);
insert into mytable values (3,3,4);
insert into mytable values (4,3,4);

원하는 경우 CTE에 해당합니다.

WITH FirstIDentified AS (
   SELECT First_Value(ID) OVER (PARTITION BY Val ORDER BY Kind) First, ID, Val, Kind 
   FROM mytable
   )
SELECT ID, Val, Kind FROM FirstIdentified
WHERE ID = First;

1
+1 그러나 나는 당신의 대답과 billinkc id가 독특 하지 않으면 논리적으로 동일하지 않다는 것을 강조 할 가치가 있다고 생각했습니다 .
Jack Douglas

@ 잭 더글러스-사실, 나는 그것을 가정했다.
레이 리펠

14

각 그룹에서 keep를 선택하는 데 사용할 수 있습니다 id.

select *
from mytable
where id in ( select min(id) keep (dense_rank first order by kind, id)
              from mytable
              group by val );
아이디 | 발 | 종류
-: | --- : | --- :
 2 | 1337 | 1
 3 | 3 | 4

여기 dbfiddle


2
SELECT MIN(MyTable01.Id) as Id,
       MyTable01.Val     as Val,
       MyTable01.Kind    as Kind 
  FROM MyTable MyTable01,                         
       (SELECT Val,MIN(Kind) as Kind
          FROM MyTable                   
      GROUP BY Val) MyTableGroup
WHERE MyTable01.Val  = MyTableGroup.Val
  AND MyTable01.Kind = MyTableGroup.Kind
GROUP BY MyTable01.Val,MyTable01.Kind
ORDER BY Id;

MyTable에 대한 두 번의 스캔이 필요하기 때문에 다른 답변보다 훨씬 덜 효율적입니다.
a_horse_with_no_name

2
옵티마이 저가 문자 그대로 쿼리를 작성하는 경우에만 해당됩니다. 고급 최적화 프로그램은 의도 (그룹당 행)를보고 단일 테이블 액세스로 계획을 작성할 수 있습니다.
Paul White
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.