我在 Postgres 13 版本中有一个带有递归的过程。 它使组织结构从副总裁变成简单的雇主
with recursive relations_recurs(
pos_id, boss_pos_id, level_num, link_type, link_type_array, pos_id_array
) as (
select l.pos_id,
l.boss_pos_id,
1 as level_num,
l.link_type,
l.link_type_array,
l.pos_id_array
from temp_loop l
union all
select l.pos_id,
l.boss_pos_id,
(r.level_num + 1) as level_num,
l.link_type,
(r.link_type_array || l.link_type) as link_type_array,
(r.pos_id_array || l.pos_id) as pos_id_array
from temp_pos_boss_with_min_link l
join relations_recurs r
on l.pos_id = r.boss_pos_id
and l.pos_id <> all (r.pos_id_array)
)
select distinct pos_id_array[1] as pos_id,
boss_pos_id as boss_pos_id,
level_num as level_id,
pos_id as pos_original_id,
(case
when array[1, 2] <@ t.link_type_array then 0
when array[1] <@ t.link_type_array then 1
else 2
end) link_type
from relations_recurs t;
但是速度很慢。 我正在尝试编写查询不使用递归以提高性能, 有人告诉我使用
WHILE
和 LOOP can
改进代码。
如何使用
WHILE
和 LOOP
将其变成程序?
递归 CTE 对于分层数据结构通常非常有效,尤其是当递归深度不是很深时。但是,如果您认为迭代方法可能更适合您在 PostgreSQL 中的特定用例,您可以尝试使用带循环的 PL/pgSQL 函数。
您必须手动管理递归 CTE 自动执行的迭代和数据积累。这很复杂,尤其是对于分层数据,并且可能不一定会带来性能改进。
这是一个简单的例子:
CREATE OR REPLACE PROCEDURE process_org_structure()
LANGUAGE plpgsql
AS $$
DECLARE
rec RECORD;
finished BOOLEAN := FALSE;
current_level INT := 1;
BEGIN
-- Temporary table to store intermediate results
CREATE TEMP TABLE IF NOT EXISTS iter_relations (
pos_id INT,
boss_pos_id INT,
level_num INT,
link_type INT,
link_type_array INT[],
pos_id_array INT[]
);
-- Initialize the temporary table with the base level data
INSERT INTO iter_relations(pos_id, boss_pos_id, level_num, link_type, link_type_array, pos_id_array)
SELECT pos_id, boss_pos_id, 1, link_type, link_type_array, pos_id_array
FROM temp_loop;
-- Loop until no more records are added
LOOP
-- Check if any new records were added in the last iteration
GET DIAGNOSTICS rec_count = ROW_COUNT;
IF rec_count = 0 THEN
finished := TRUE;
END IF;
EXIT WHEN finished;
-- Iterate to the next level
INSERT INTO iter_relations(pos_id, boss_pos_id, level_num, link_type, link_type_array, pos_id_array)
SELECT l.pos_id, l.boss_pos_id, current_level + 1, l.link_type, r.link_type_array || l.link_type, r.pos_id_array || l.pos_id
FROM temp_pos_boss_with_min_link l
JOIN iter_relations r ON l.boss_pos_id = r.pos_id
WHERE l.pos_id <> ALL(r.pos_id_array)
ON CONFLICT DO NOTHING;
-- Increment the level for the next iteration
current_level := current_level + 1;
END LOOP;
-- Select the final results from the temporary table
CREATE TEMP TABLE final_results AS
SELECT DISTINCT pos_id_array[1] as pos_id,
boss_pos_id,
level_num,
pos_id as pos_original_id,
(CASE
WHEN array[1,2] <@ link_type_array THEN 0
WHEN array[1] <@ link_type_array THEN 1
ELSE 2
END) AS link_type
FROM iter_relations;
-- Clean up the temporary table
DROP TABLE iter_relations;
-- Your logic here to utilize `final_results`
-- For example, you can return the results or insert them into another table
END;
$$;