Làm thế nào để chọn hàng đầu tiên của mỗi nhóm?


57

Tôi có một cái bàn như thế này:

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

Tôi muốn thực hiện một SELECTsẽ chỉ trả lại hàng đầu tiên cho mỗi Val, đặt hàng theo Kind.

Đầu ra mẫu:

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

Làm thế nào tôi có thể xây dựng truy vấn này?


tại sao 3 | 3 | 4 mà không phải 4 | 3 | 4 - tie-break là gì hoặc bạn không quan tâm?
Jack Douglas

@JackDoumund Thật ra tôi có một ORDER BY ID DESC, nhưng điều đó không liên quan đến câu hỏi. Trong ví dụ này tôi không quan tâm.
BrunoLM

Câu trả lời:


38

Giải pháp này cũng sử dụng keep, nhưng valkindcũng có thể chỉ cần tính toán cho từng nhóm mà không có một subquery:

select min(id) keep(dense_rank first order by kind) id
     , val
     , min(kind) kind
  from mytable
 group by val;
ID | GIÁ TRỊ | TỐT BỤNG
-: | ---: | ---:
 3 | 3 | 4
 2 | 1337 | 1

dbfiddle ở đây

GIỮ ... FIRST và GIỮ ... LAST là một tính năng Oracle-cụ thể của uẩn - bạn có thể đọc về rồi đây trong các tài liệu Oracle, hoặc trên ORACLE_BASE :

Các hàm FIRST và LAST có thể được sử dụng để trả về giá trị đầu tiên hoặc cuối cùng từ một chuỗi được sắp xếp


62

Sử dụng biểu thức bảng chung (CTE) và chức năng cửa sổ / xếp hạng / phân vùng như ROW_NUMBER .

Truy vấn này sẽ tạo một bảng trong bộ nhớ có tên ORDERED và thêm một cột rn bổ sung, đó là một chuỗi các số từ 1 đến N. PHẦN THAM GIA BY chỉ ra rằng nó sẽ khởi động lại ở mức 1 mỗi khi giá trị của Val thay đổi và chúng tôi muốn đặt hàng hàng theo giá trị nhỏ nhất của 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;

Cách tiếp cận trên sẽ hoạt động với bất kỳ RDBMS nào đã triển khai hàm ROW_NUMBER (). Oracle có một số chức năng tao nhã như thể hiện trong câu trả lời của mik nói chung sẽ mang lại hiệu suất tốt hơn câu trả lời này.


25

Giải pháp của bilinkc hoạt động tốt, nhưng tôi nghĩ tôi cũng sẽ ném nó ra. Nó có cùng chi phí, nhưng có thể nhanh hơn (hoặc chậm hơn, tôi chưa thử nghiệm). Sự khác biệt là nó sử dụng First_Value thay vì Row_Number. Vì chúng ta chỉ quan tâm đến giá trị đầu tiên, nên trong suy nghĩ của tôi nó đơn giản hơn.

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;

Dữ liệu kiểm tra.

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

Nếu bạn thích, đây là tương đương 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 nhưng tôi chỉ nghĩ rằng đáng để nhấn mạnh rằng câu trả lời của bạn và billinkc không giống nhau về mặt logic trừ khi idlà duy nhất.
Jack Douglas

@Jack Douglas - Đúng, tôi cho rằng.
Leigh Riffel

14

Bạn có thể sử dụng keepđể chọn một idtừ mỗi nhóm:

select *
from mytable
where id in ( select min(id) keep (dense_rank first order by kind, id)
              from mytable
              group by val );
ID | GIÁ TRỊ | TỐT BỤNG
-: | ---: | ---:
 2 | 1337 | 1
 3 | 3 | 4

dbfiddle ở đây


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;

Điều đó sẽ kém hiệu quả hơn nhiều so với các câu trả lời khác do thực tế là cần phải quét hai lần trên MyTable.
a_horse_with_no_name

2
Điều đó chỉ đúng nếu trình tối ưu hóa thực hiện truy vấn bằng văn bản. Các trình tối ưu hóa nâng cao hơn có thể thấy mục đích (hàng trên mỗi nhóm) và tạo ra một kế hoạch với một truy cập bảng duy nhất.
Paul White
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.