【问题标题】:SQL group table by "leading rows" without pl/sql没有 pl/sql 的“前导行”的 SQL 分组表
【发布时间】:2016-06-29 15:42:40
【问题描述】:

我有这个包含两列的表(简短示例)

1 a
2 a
3 a3
4 a
5 a
6 a6
7 a
8 a8
9 a

我想将它们分组/划分为由以“a”开头的组分隔的组,理想情况下添加这样的另一列,这样我就可以轻松地处理这些组。

1 a  0
2 a  0
3 a3 3
4 a  3
5 a  3
6 a6 6
7 a  6
8 a8 8
9 a  8

问题是表的设置是动态的,所以我不能使用静态滞后或领先功能,任何想法如何在没有 pl/sql 的情况下在 postgres 9.5 版中做到这一点

【问题讨论】:

  • 由于 PL/SQL 是 Oracle 过程语言,您会发现将其排除在 Postgres 查询之外非常简单。你的意思是 PL/pgSQL?
  • 是的,我提到了 PL/pgSQL,在为 sql 处理过程语言时,我只是习惯了 pl/sql

标签: sql postgresql group-by window-functions


【解决方案1】:

假设前导部分是一个单个字符。因此表达式right(data, -1) 可以提取组名。适应你的实际前缀。

解决方案使用两个window functions,不能嵌套。所以我们需要一个子查询或一个 CTE。

SELECT id, data
     , COALESCE(first_value(grp) OVER (PARTITION BY grp_nr ORDER BY id), '0') AS grp
FROM (
   SELECT *, NULLIF(right(data, -1), '') AS grp
        , count(NULLIF(right(data, -1), '')) OVER (ORDER BY id) AS grp_nr
   FROM   tbl
   ) sub;

准确地产生您想要的结果。

NULLIF(right(data, -1), '') 获取有效组名,如果没有则NULL

count() 只计算非空值,因此子查询中每个新组的计数都更高。

在外部查询中,我们将每个 grp_nr 的第一个 grp 值作为组名,默认为“0”,第一个没有名称的组使用 COALESCE(它有一个 NULL 作为组名,所以远)。

我们也可以使用min()max() 作为外部窗口函数,因为无论如何每个分区只有一个 非空值。 first_value() 可能是最便宜的,因为行已经排序。

注意组名grp 是数据类型text。如果它们是干净(且可靠)的整数,您可能希望转换为整数。

【讨论】:

    【解决方案2】:

    这可以通过将包含a 的行设置为特定值并将所有其他行设置为不同的值来实现。然后使用累积和来获得所需的行数。当在 val 列中遇到新值时,组号将设置为下一个数字,并且所有带有 a 的行将具有与之前相同的组号,并且继续下去。

    我假设每个组都需要一个不同的数字,而数字并不重要。

    select id, val, sum(ex) over(order by id) cm_sum
    from (select t.*
          ,case when val = 'a' then 0 else 1 end ex
          from t) x
    

    上面有问题数据的查询结果是

    id  val cm_sum
    --------------
    1   a   0
    2   a   0
    3   a3  1
    4   a   1
    5   a   1
    6   a6  2
    7   a   2
    8   a8  3
    9   a   3
    

    【讨论】:

    • 谢谢,你真的很简单............实际上不需要那个子查询,这种情况可以直接放入窗口函数sum中
    • @Baker:简单。而且不正确。该问题不要求提供序列号,而是要求列中包含的实际字符串(或数字)。
    • 是的@Erwin Brandstetter,你是对的,根据它的定义,你的答案是对我的问题的 100% 正确答案,......但是这个解决方案满足了我需要的目的,因为它提供了一种分离的方法那些组
    【解决方案3】:

    使用给定的数据,您可以使用累积最大值:

    select . . .,
           coalesce(max(substr(col2, 2)) over (order by col1), 0)
    

    如果您不严格要求最大值,那么它会变得有点困难。 ANSI 解决方案是在LAG() 上使用IGNORE NULLs 选项。但是,Postgres 不(还)支持这一点。另一种选择是:

    select . . ., coalesce(substr(reft.col2, 2), 0)
    from (select . . .,
                 max(case when col2 like 'a_%' then col1 end) over (order by col1) as ref_col1
          from t
         ) tt join
         t reft
         on tt.ref_col1 = reft.col1
    

    【讨论】:

      【解决方案4】:

      你也可以试试这个:

       with mytable as (select split_part(t,' ',1)::integer id,split_part(t,' ',2) myvalue 
             from (select unnest(string_to_array($$1 a;2 a;3 a3;4 a;5 a;6 a6;7 a;8 a8;9 a$$,
          ';'))t) a)
      
        select id,myvalue,myresult from mytable join (
           select COALESCE(NULLIF(substr(myvalue,2),''),'0') myresult,idmin id_down
                  ,COALESCE(lead(idmin) over (order by myvalue),999999999999) id_up 
         from (
           select myvalue,min(id) idmin from mytable group by 1
          ) a) b 
        on id between id_down and id_up-1
      

      【讨论】:

        猜你喜欢
        • 2020-05-20
        • 1970-01-01
        • 2019-08-12
        • 1970-01-01
        • 2015-09-29
        • 1970-01-01
        • 1970-01-01
        • 2020-01-25
        • 2014-08-25
        相关资源
        最近更新 更多