【问题标题】:from each partition, select rows with N distinct values从每个分区中,选择具有 N 个不同值的行
【发布时间】:2014-11-25 15:59:21
【问题描述】:

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

这是 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 行,但这不处理不同值部分。

【问题讨论】:

    标签: sql postgresql subquery window-functions


    【解决方案1】:

    我不确定您所说的“第一”是什么意思。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;
    

    【讨论】:

    • 如果我从一个指定任意顺序的子查询中进行选择,并且想要获取其中的第一个 n 怎么办?
    • @rcrogers 。 . .您可以将排序放入order by 子句中。
    • 有没有办法让外部查询尊重内部查询的顺序,而无需外部查询也指定它?
    • @rcrogers 。 . .不可以。如果你想要一个特定顺序的结果集,那么你应该在最外层使用order by
    【解决方案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

    【讨论】:

      猜你喜欢
      • 2020-02-11
      • 2017-10-10
      • 1970-01-01
      • 2010-10-11
      • 1970-01-01
      • 2019-07-17
      • 2021-02-17
      • 2022-08-14
      • 1970-01-01
      相关资源
      最近更新 更多