从每个分区中,选择具有N个不同值的行

时间:2014-09-30 21:52:02

标签: sql postgresql subquery window-functions

我有一个包含三列x, y, z的表格。我想编写一个查询,在每个PARTITION BY x内,返回包含n的第一个y个不同值的行。

这里是n = 2的示例 - 第一个分区中y的前2个不同值是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

有一个related question处理从每个分区中选择n行,但这并不处理不同的值部分。

2 个答案:

答案 0 :(得分: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;

答案 1 :(得分:2)

您可以使用desnse_rankrow_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
  );

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

Example SQLFiddle