Postgresql顺序add/remove缩减操作
Postgresql sequential add/remove reduction operation
我有一个带有行号的 table 和标识符的“定义”或“未定义”事件。示例:
line_no | def | undef
--------------------
1 | 'a' | NULL
2 | 'b' | NULL
3 | NULL| 'a'
...
42 | NULL| 'b'
我想计算每一行的“实时变量”信息。迭代地,我只编写如下伪代码的代码:
live = []
for each r in table:
if r.def:
live.append(r.def)
else:
live.remove(r.undef)
store(r.line_no, live)
预期结果是 table,如:
line_no | live
1. | ['a']
2. | ['a', 'b']
3. | ['b']
...
42. | []
我设法将等效的顺序循环编写为 plpgsql 函数。但是,我想知道是否有一种(可能更优雅的)方式作为 SQL 查询?我使用 lag() 尝试了各种方法,但不知何故从未导致我正在寻找的这种“减少”操作?
(如果查询还可以对字段进行分区,则加分,例如'filename')
如果您想要一个纯粹的 SQL 解决方案,请使用递归查询。
with recursive cte as (
select line_no, array[def] as list
from my_table
where line_no = 1
union all
select
t.line_no,
case when def is null
then array_remove(list, undef)
else array_append(list, def)
end
from my_table t
join cte c on c.line_no = t.line_no- 1
)
select *
from cte;
但是,在这种情况下,更有效、更灵活的解决方案可能是创建一个函数。
create or replace function list_of_vars()
returns table (line_no int, list text[])
language plpgsql as $$
declare
arr text[];
rec record;
begin
for rec in
select *
from my_table
order by line_no
loop
if rec.def is null then
arr := array_remove(arr, rec.undef);
else
arr := array_append(arr, rec.def);
end if;
line_no := rec.line_no;
list := arr;
return next;
end loop;
end
$$;
使用:
select *
from list_of_vars()
order by line_no
中测试
实现此目的的一种方法是使用递归查询,它最终与您正在考虑的迭代非常相似。
with recursive lines as (
select line_no,
(case when def is not null then jsonb_build_array(def) else '[]'::jsonb end) - coalesce(undef, '') live
from the_table
where line_no = 1
union all
select c.line_no,
(p.live || case when def is not null then jsonb_build_array(c.def) else '[]'::jsonb end) - coalesce(c.undef, '')
from the_table c
join lines p on c.line_no - 1 = p.line_no
)
select *
from lines;
jsonb_build_array
很乐意包含 NULL 值,这就是为什么我们需要有点复杂的 CASE 表达式来将单个值转换为数组(具有一个或零个元素)。
jsonb 的 -
运算符从数组中删除右侧的元素。然而,右侧的 null
值将删除 所有 元素,因此 coalesce()
这要求 line_no
的值没有任何间隙(并且从 1
开始)。如果不是这种情况,则需要另一个步骤来为每一行生成一个 gap-less 数字(这会使它变得更慢)
我怀疑这是否真的比“适当的”程序解决方案更快。
我有一个带有行号的 table 和标识符的“定义”或“未定义”事件。示例:
line_no | def | undef
--------------------
1 | 'a' | NULL
2 | 'b' | NULL
3 | NULL| 'a'
...
42 | NULL| 'b'
我想计算每一行的“实时变量”信息。迭代地,我只编写如下伪代码的代码:
live = []
for each r in table:
if r.def:
live.append(r.def)
else:
live.remove(r.undef)
store(r.line_no, live)
预期结果是 table,如:
line_no | live
1. | ['a']
2. | ['a', 'b']
3. | ['b']
...
42. | []
我设法将等效的顺序循环编写为 plpgsql 函数。但是,我想知道是否有一种(可能更优雅的)方式作为 SQL 查询?我使用 lag() 尝试了各种方法,但不知何故从未导致我正在寻找的这种“减少”操作?
(如果查询还可以对字段进行分区,则加分,例如'filename')
如果您想要一个纯粹的 SQL 解决方案,请使用递归查询。
with recursive cte as (
select line_no, array[def] as list
from my_table
where line_no = 1
union all
select
t.line_no,
case when def is null
then array_remove(list, undef)
else array_append(list, def)
end
from my_table t
join cte c on c.line_no = t.line_no- 1
)
select *
from cte;
但是,在这种情况下,更有效、更灵活的解决方案可能是创建一个函数。
create or replace function list_of_vars()
returns table (line_no int, list text[])
language plpgsql as $$
declare
arr text[];
rec record;
begin
for rec in
select *
from my_table
order by line_no
loop
if rec.def is null then
arr := array_remove(arr, rec.undef);
else
arr := array_append(arr, rec.def);
end if;
line_no := rec.line_no;
list := arr;
return next;
end loop;
end
$$;
使用:
select *
from list_of_vars()
order by line_no
中测试
实现此目的的一种方法是使用递归查询,它最终与您正在考虑的迭代非常相似。
with recursive lines as (
select line_no,
(case when def is not null then jsonb_build_array(def) else '[]'::jsonb end) - coalesce(undef, '') live
from the_table
where line_no = 1
union all
select c.line_no,
(p.live || case when def is not null then jsonb_build_array(c.def) else '[]'::jsonb end) - coalesce(c.undef, '')
from the_table c
join lines p on c.line_no - 1 = p.line_no
)
select *
from lines;
jsonb_build_array
很乐意包含 NULL 值,这就是为什么我们需要有点复杂的 CASE 表达式来将单个值转换为数组(具有一个或零个元素)。
jsonb 的 -
运算符从数组中删除右侧的元素。然而,右侧的 null
值将删除 所有 元素,因此 coalesce()
这要求 line_no
的值没有任何间隙(并且从 1
开始)。如果不是这种情况,则需要另一个步骤来为每一行生成一个 gap-less 数字(这会使它变得更慢)
我怀疑这是否真的比“适当的”程序解决方案更快。