от всеки дял изберете редове с N отделни стойности

Имам таблица с три колони x, y, z. Бих искал да напиша заявка, която в рамките на всеки PARTITION BY x връща редовете, съдържащи първите n отделни стойности на y.

Ето пример за n = 2 -- първите 2 отделни стойности на y в първия дял са 1 и 2 и 4 и 5 във втория дял, така че всички редове с тези стойности на y са включени.

 x   y   z   included?
----------------------
 1   1   1    true
 1   1   2    true
 1   2   3    true
 1   2   4    true
 1   3   5    false
 1   3   6    false
 2   4   7    true
 2   4   8    true
 2   5   9    true
 2   5  10    true
 2   6  11    false
 2   6  12    false

Има свързан въпрос, който се занимава с избирането на n реда от всеки дял, но не се занимава с частта с отделните стойности.


person rcrogers    schedule 30.09.2014    source източник


Отговори (2)


Не съм сигурен какво имате предвид под „първи“. SQL таблиците представляват неподредени набори. Така че ще приема, че имате предвид „най-малкия“.

Можете да направите това с помощта на dense_rank():

select t.*
from (select t.*, dense_rank() over (partition by x order by y) as seqnum
      from atable t
     ) t
where seqnum <= 2;
person Gordon Linoff    schedule 30.09.2014
comment
Какво ще стане, ако вместо това избирам от подзаявка, която определя някакъв произволен ред, и искам да взема първото n от тях? - person rcrogers; 01.10.2014
comment
@rcrogers. . . Бихте поставили подреждането в клаузата order by. - person Gordon Linoff; 01.10.2014
comment
Има ли начин външната заявка да спазва реда на вътрешната заявка, без външната заявка също да го посочва? - person rcrogers; 01.10.2014
comment
@rcrogers. . . Не. Ако искате набор от резултати в определен ред, тогава трябва да използвате order by на най-външното ниво. - person Gordon Linoff; 01.10.2014

Можете да използвате комбинация от desnse_rank и row_number, за да премахнете дубликатите:

with a as (
  select
    x, y, z,
    dense_rank() over (partition by x order by y) rk,
    row_number() over (partition by x, y order by z) rn
  from
    t
) select
  x, y, z
from
  a
where
  rk <= 2 and
  rn = 1;

това генерира 1, 2, 4, 5

от това можете да получите желаните резултати, като се присъедините обратно към t:

with a as (
  select
    x, y, z,
    dense_rank() over (partition by x order by y) rk,
    row_number() over (partition by x, y order by z) rn
  from
    t
) select
  t.*
from
  t
where
  exists (
    select
      'x'
    from
      a
    where
      a.y = t.y and
      a.rk <= 2 and
      a.rn = 1
  );

Пример SQLFiddle

Въпреки че използването на exists по този начин прави дубликатите неуместни, така че можете просто да направите:

with a as (
  select
    x, y, z,
    dense_rank() over (partition by x order by y) rk
  from
    t
) select
  t.*
from
  t
where
  exists (
    select
      'x'
    from
      a
    where
      a.y = t.y and
      a.rk <= 2
  );

Пример SQLFiddle

person Laurence    schedule 30.09.2014