Як вибрати перший рядок кожної групи?


57

У мене така таблиця:

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

Я хочу зробити SELECTте, що поверне лише перший рядок для кожного Val, упорядкувавшись Kind.

Вибірка зразка:

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

Як я можу побудувати цей запит?


чому 3 | 3 | 4, а не 4 | 3 | 4 - що таке тай-брейк чи вам не байдуже?
Джек Дуглас

@JackDouglas Насправді у мене є ORDER BY ID DESC, але це не стосується питання. У цьому прикладі мені байдуже.
BrunoLM

Відповіді:


38

Це рішення також використовує keep, але valі kindможе також бути просто для кожної групи без підзапиту:

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 - про них можна прочитати тут, у документах Oracle або на ORACLE_BASE :

Функції FIRST і LAST можуть використовуватися для повернення першого або останнього значення з упорядкованої послідовності


62

Використовуйте вираз загальної таблиці (КТР) і віконну / ранжування / функцію поділу , як ROW_NUMBER .

Цей запит створить таблицю в пам'яті під назвою ORDERED та додасть додатковий стовпець rn, який є послідовністю чисел від 1 до N. PARTITION BY вказує, що він повинен перезапуститися о 1 раз, коли значення Val змінюється, і ми хочемо замовити рядки за найменшим значенням Виду.

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;

Вищенаведений підхід повинен працювати з будь-яким RDBMS, який реалізував функцію ROW_NUMBER (). Oracle має елегантну функціональність, виражену у відповіді mik, яка, як правило, дає кращі показники, ніж ця відповідь.


25

Рішення bilinkc працює чудово, але я думав, що я також викину шахту. Це однакова вартість, але може бути швидшою (або повільніше, я її не перевіряв). Різниця полягає в тому, що він використовує First_Value замість Row_Number. Оскільки нас цікавить лише перша цінність, на мою думку це більш прямо.

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
Це справедливо лише в тому випадку, якщо оптимізатор сприймає письмовий запит буквально. Більш просунуті оптимізатори можуть бачити наміри (рядок на групу) та створювати план із доступом до однієї таблиці.
Пол Білий
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.