SQL table 组 "leading rows" 没有 pl/sql
SQL group table by "leading rows" without pl/sql
我有这个 table(简短示例)有两列
1 a
2 a
3 a3
4 a
5 a
6 a6
7 a
8 a8
9 a
并且我想 group/partition 将它们分成由前导 "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
问题是 table 的设置是动态的,所以我不能使用静态滞后或前导函数,关于如何在 postgres 9.5[=16= 版本中没有 pl/sql 的情况下执行此操作的任何想法]
根据给定的数据,您可以使用累积最大值:
select . . .,
coalesce(max(substr(col2, 2)) over (order by col1), 0)
如果您不严格要求最大值,那么它会变得有点困难。 ANSI 解决方案是在 LAG()
上使用 IGNORE NULL
s 选项。然而,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
这可以通过将包含 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
假设前导部分是单个字符。因此表达式 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
如果 none.
count()
只计算非空值,因此子查询中每个新组的计数都会增加。
在外部查询中,我们将每个 grp_nr
的第一个 grp
值作为组名,默认为 '0' 和 COALESCE
对于第一个没有名称的组(有a NULL
作为组名。
我们也可以使用 min()
或 max()
作为外部 window 函数,因为每个函数只有 一个 非空值无论如何分区。 first_value()
可能是最便宜的,因为行已经排序。
注意组名 grp
是数据类型 text
。如果这些是干净(且可靠)的整数,您可能希望转换为整数。
你也可以试试这个:
with mytable as (select split_part(t,' ',1)::integer id,split_part(t,' ',2) myvalue
from (select unnest(string_to_array($ 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
我有这个 table(简短示例)有两列
1 a
2 a
3 a3
4 a
5 a
6 a6
7 a
8 a8
9 a
并且我想 group/partition 将它们分成由前导 "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
问题是 table 的设置是动态的,所以我不能使用静态滞后或前导函数,关于如何在 postgres 9.5[=16= 版本中没有 pl/sql 的情况下执行此操作的任何想法]
根据给定的数据,您可以使用累积最大值:
select . . .,
coalesce(max(substr(col2, 2)) over (order by col1), 0)
如果您不严格要求最大值,那么它会变得有点困难。 ANSI 解决方案是在 LAG()
上使用 IGNORE NULL
s 选项。然而,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
这可以通过将包含 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
假设前导部分是单个字符。因此表达式 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
如果 none.
count()
只计算非空值,因此子查询中每个新组的计数都会增加。
在外部查询中,我们将每个 grp_nr
的第一个 grp
值作为组名,默认为 '0' 和 COALESCE
对于第一个没有名称的组(有a NULL
作为组名。
我们也可以使用 min()
或 max()
作为外部 window 函数,因为每个函数只有 一个 非空值无论如何分区。 first_value()
可能是最便宜的,因为行已经排序。
注意组名 grp
是数据类型 text
。如果这些是干净(且可靠)的整数,您可能希望转换为整数。
你也可以试试这个:
with mytable as (select split_part(t,' ',1)::integer id,split_part(t,' ',2) myvalue
from (select unnest(string_to_array($ 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