英文:
Looking for a PL/PGSQL function to return N columns
问题
在PL/pgSQL中运行一个函数,返回N列并将其放入一个视图或其他允许在不需要人工干预的情况下获取包含N列的表。
使用以下代码:https://stackoverflow.com/questions/75594224/
---示例数据
DROP TABLE IF EXISTS split_clm;
CREATE TEMP TABLE split_clm(
id integer PRIMARY KEY,
name text,
hobby text,
value int
);
INSERT INTO split_clm (id, name, hobby, value) VALUES
(1, 'Rene', 'Python, Monkey_Bars', '5'),
(2, 'CJ', 'Trading, Python', '25'),
(3, 'Herlinda', 'Fashion', '15'),
(4, 'DJ', 'Consutling, Sales', '35'),
(5, 'Martha', 'Social_Media, Teaching', '45'),
(6, 'Doug', 'Leadership, Management', '55'),
(7, 'Mathew', 'Finance, Emp_Engagement', '65'),
(8, 'Mayers', 'Sleeping, Coding, Crossfit', '75'),
(9, 'Mike', 'YouTube, Athletics', '85'),
(10, 'Peter', 'Eat, Sleep, Python', '95'),
(11, 'Thomas', 'Read, Trading, Sales', '105');
/****查询****/
--1NF <done>
DROP TABLE IF EXISTS split_clm_Nor;
CREATE TEMP TABLE split_clm_Nor AS
SELECT id, name, unnest(string_to_array(hobby, ', ')) AS Ivalues, value
FROM split_clm
ORDER BY id;
--
--Select * from split_clm_Nor limit 6; ---
---版本2.0
DROP TABLE IF EXISTS split_clm_Nor2;
CREATE TEMP TABLE split_clm_Nor2 AS
SELECT id, name, lower(unnest(string_to_array(hobby, ', '))) AS Ivalues, value, count(1) as "Case_Volume"
FROM split_clm
GROUP BY 1,2,3,4
ORDER BY id
;
Select * from split_clm_Nor2 ;
---------------------------
DROP TABLE IF EXISTS tmpTblTyp2 CASCADE;
DO LANGUAGE plpgsql $$
DECLARE v_sqlstring VARCHAR = '';
BEGIN
v_sqlstring := CONCAT('CREATE TABLE tmpTblTyp2 AS SELECT '
,(SELECT STRING_AGG(CONCAT('NULL::int AS ', ivalues)::TEXT, ' ,')::TEXT
FROM (SELECT DISTINCT ivalues FROM split_clm_Nor2) a
)
,' LIMIT 0 '
) ; -- RAISE NOTICE '%', v_sqlstring ;
EXECUTE(v_sqlstring); END $$;
-----------------------------
DROP TABLE IF EXISTS tmpMoJson;
CREATE TEMP TABLE tmpMoJson AS
SELECT
name AS name
,(json_build_array( mivalues )) AS js_mivalues_arr
,json_populate_recordset ( NULL::tmpTblTyp2 /** use temp table as a record type!!*/
, json_build_array( mivalues ) /** builds row-type column that can be expanded with (jprs).* */
) jprs
FROM ( SELECT name
,json_object_agg(ivalues,value) AS mivalues
FROM split_clm_Nor2
GROUP BY 1
ORDER BY 1
) a
;
Select * from tmpMoJson ;
SELECT
name
,(ROW((jprs).*):: tmpTblTyp2).* -- explode the composite type row
--, js_mivalues_arr /** optional **/
FROM tmpMoJson ;
期望获取持续更新的表格。
英文:
Run in a PL/pgSQL a function to return N columns and put in a view or something that allow to get the table with the N column updated without human intervention.
Using the below code from:
https://stackoverflow.com/questions/75594224/
---sample data
DROP TABLE IF EXISTS split_clm;
CREATE TEMP TABLE split_clm(
id integer PRIMARY KEY,
name text,
hobby text,
value int
);
INSERT INTO split_clm (id, name, hobby,value) VALUES
(1, 'Rene', 'Python, Monkey_Bars','5'),
(2, 'CJ', 'Trading, Python','25'),
(3, 'Herlinda', 'Fashion','15'),
(4, 'DJ', 'Consutling, Sales','35'),
(5, 'Martha', 'Social_Media, Teaching','45'),
(6, 'Doug', 'Leadership, Management','55'),
(7, 'Mathew', 'Finance, Emp_Engagement','65'),
(8, 'Mayers', 'Sleeping, Coding, Crossfit','75'),
(9, 'Mike', 'YouTube, Athletics','85'),
(10, 'Peter', 'Eat, Sleep, Python','95'),
(11, 'Thomas', 'Read, Trading, Sales','105');
/****query****/
--1NF <done>
DROP TABLE IF EXISTS split_clm_Nor;
CREATE TEMP TABLE split_clm_Nor AS
SELECT id, name, unnest(string_to_array(hobby, ', ')) AS Ivalues , value
FROM split_clm
ORDER BY id;
--
--Select * from split_clm_Nor limit 6; ---
---ver 2.0
DROP TABLE IF EXISTS split_clm_Nor2;
CREATE TEMP TABLE split_clm_Nor2 AS
SELECT id, name, lower(unnest(string_to_array(hobby, ', '))) AS Ivalues , value,count(1) as "Case_Volume"
FROM split_clm
GROUP BY 1,2,3,4
ORDER BY id
;
Select * from split_clm_Nor2 ;
---------------------------
DROP TABLE IF EXISTS tmpTblTyp2 CASCADE ;
DO LANGUAGE plpgsql $$
DECLARE v_sqlstring VARCHAR = '';
BEGIN
v_sqlstring := CONCAT( 'CREATE TABLE tmpTblTyp2 AS SELECT '
,(SELECT STRING_AGG( CONCAT('NULL::int AS ' , ivalues )::TEXT , ' ,' ORDER BY ivalues )::TEXT
FROM (SELECT DISTINCT ivalues FROM split_clm_Nor2) a
)
,' LIMIT 0 '
) ; -- RAISE NOTICE '%', v_sqlstring ;
EXECUTE( v_sqlstring ) ; END $$;
--------------------------------------------
DROP TABLE IF EXISTS tmpMoJson ;
CREATE TEMP TABLE tmpMoJson AS
--CREATE TEMP TABLE tmpMoJson AS
SELECT
name AS name
,(json_build_array( mivalues )) AS js_mivalues_arr
,json_populate_recordset ( NULL::tmpTblTyp2 /** use temp table as a record type!!*/
, json_build_array( mivalues ) /** builds row-type column that can be expanded with (jprs).* */
) jprs
FROM ( SELECT name
,json_object_agg(ivalues,value) AS mivalues
FROM split_clm_Nor2
GROUP BY 1
ORDER BY 1
) a
;
Select * from tmpMoJson ;
SELECT
name
,(ROW((jprs).*):: tmpTblTyp2).* -- explode the composite type row
--, js_mivalues_arr /** optional **/
FROM tmpMoJson ;
Expecting get that table continue update
name | athletics | coding | consutling | crossfit | eat | emp_engagement | fashion | finance | leadership | management | monkey_bars | python | read | sales | sleep | sleeping | social_media | teaching | trading | youtube |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
CJ | null | null | null | null | null | null | null | null | null | null | null | 25 | null | null | null | null | null | null | 25 | null |
DJ | null | null | 35 | null | null | null | null | null | null | null | null | null | null | 35 | null | null | null | null | null | null |
Doug | null | null | null | null | null | null | null | null | 55 | 55 | null | null | null | null | null | null | null | null | null | null |
Herlinda | null | null | null | null | null | null | 15 | null | null | null | null | null | null | null | null | null | null | null | null | null |
Martha | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | 45 | 45 | null | null |
Mathew | null | null | null | null | null | 65 | null | 65 | null | null | null | null | null | null | null | null | null | null | null | null |
Mayers | null | 75 | null | 75 | null | null | null | null | null | null | null | null | null | null | null | 75 | null | null | null | null |
Mike | 85 | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | 85 |
Peter | null | null | null | null | 95 | null | null | null | null | null | null | 95 | null | null | 95 | null | null | null | null | null |
Rene | null | null | null | null | null | null | null | null | null | null | 5 | 5 | null | null | null | null | null | null | null | null |
Thomas | null | null | null | null | null | null | null | null | null | null | null | null | 105 | 105 | null | null | null | null | 105 | null |
答案1
得分: 1
你可以创建一个返回动态列数的函数,而无需在某个地方硬编码列的列表(无论是在调用函数时还是在定义函数时)。
我看到的最佳(唯一?)选择是创建一个基于表内容的视图。然后,每当表更改时,您可以调用该视图,以创建一个带有更新列列表的新视图。
create function create_clm_view()
returns void
as
$body$
declare
l_sql text;
l_columns text;
l_hobby_name text;
begin
l_sql := 'select name, ';
-- 基于不同爱好构建列的列表
select string_agg(format('%L = any(hobbies) as %I', x.hobby_name, x.hobby_name), ', ' order by hobby_name)
into l_columns
from (
select distinct t.hobby_name
from split_clm s
cross join unnest(regexp_split_to_array(s.hobby, '\s*,\s*')) as t(hobby_name)
) x;
-- 使用生成的列构建视图的最终SELECT语句
l_sql := l_sql || l_columns ||
$sql$ from ( select name, regexp_split_to_array(hobby, '\s*,\s*') as hobbies from split_clm ) t $sql$ ;
-- 重新创建视图
execute 'drop view if exists clm_view cascade';
execute 'create view clm_view as '||l_sql;
end;
$body$
language plpgsql;
这将创建一个类似于以下的视图:
select name,
'Athletics' = any(hobbies) as "Ahtletics",
'Coding' = any(hobbies) as "Coding",
.....
from ( select name, regexp_split_to_array(hobby, '\s*,\s*') as hobbies from split_clm ) t;
然后运行:
select create_normalized_view();
要查看内容:
select *
from clm_view;
根据表的更新频率,您可以从触发器或定期间隔更新视图的cron作业中调用该函数。
英文:
You can't create a function that returns a dynamic number of columns without hard coding the list of columns at some place (either when calling the function or when defining the function).
The best (only?) option I see, is to create a view that is based on the content of the table. You can then call that view each time the table changes to create a new view with the updated list of columns.
create function create_clm_view()
returns void
as
$body$
declare
l_sql text;
l_columns text;
l_hobby_name text;
begin
l_sql := 'select name, ';
-- build the list of columns based on the distinct hobbies
select string_agg(format('%L = any(hobbies) as %I', x.hobby_name, x.hobby_name), ', ' order by hobby_name)
into l_columns
from (
select distinct t.hobby_name
from split_clm s
cross join unnest(regexp_split_to_array(s.hobby, '\s*,\s*')) as t(hobby_name)
) x;
-- build the final SELECT of the view with the generated columns
l_sql := l_sql || l_columns ||
$sql$ from ( select name, regexp_split_to_array(hobby, '\s*,\s*') as hobbies from split_clm ) t $sql$ ;
-- recreate the view
execute 'drop view if exists clm_view cascade';
execute 'create view clm_view as '||l_sql;
end;
$body$
language plpgsql;
This will build a view that looks something like this:
select name,
'Athletics' = any(hobbies) as "Ahtletics",
'Coding' = any(hobbies) as "Coding",
.....
from ( select name, regexp_split_to_array(hobby, '\s*,\s*') as hobbies from split_clm ) t;
Then run:
select create_normalized_view();
and to see the content:
select *
from clm_view;
Depending on how often the table is updated, you could call that function from a trigger or a cron job that updates the view in regular intervals.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论