寻找一个 PL/PGSQL 函数来返回 N 列

huangapple go评论61阅读模式
英文:

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, &#39;Rene&#39;, &#39;Python, Monkey_Bars&#39;,&#39;5&#39;),
(2, &#39;CJ&#39;, &#39;Trading, Python&#39;,&#39;25&#39;),
(3, &#39;Herlinda&#39;, &#39;Fashion&#39;,&#39;15&#39;),
(4, &#39;DJ&#39;, &#39;Consutling, Sales&#39;,&#39;35&#39;),
(5, &#39;Martha&#39;, &#39;Social_Media, Teaching&#39;,&#39;45&#39;),
(6, &#39;Doug&#39;, &#39;Leadership, Management&#39;,&#39;55&#39;),
(7, &#39;Mathew&#39;, &#39;Finance, Emp_Engagement&#39;,&#39;65&#39;),
(8, &#39;Mayers&#39;, &#39;Sleeping, Coding, Crossfit&#39;,&#39;75&#39;),
(9, &#39;Mike&#39;, &#39;YouTube, Athletics&#39;,&#39;85&#39;),
(10, &#39;Peter&#39;, &#39;Eat, Sleep, Python&#39;,&#39;95&#39;),
(11, &#39;Thomas&#39;, &#39;Read, Trading, Sales&#39;,&#39;105&#39;);
/****query****/
--1NF &lt;done&gt;
DROP TABLE IF EXISTS  split_clm_Nor;
CREATE TEMP TABLE     split_clm_Nor  AS	
SELECT id, name, unnest(string_to_array(hobby, &#39;, &#39;)) 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, &#39;, &#39;))) AS Ivalues , value,count(1)  as &quot;Case_Volume&quot;
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  = &#39;&#39;; 
BEGIN 
v_sqlstring := CONCAT( &#39;CREATE  TABLE  tmpTblTyp2 AS SELECT &#39;   
,(SELECT  STRING_AGG( CONCAT(&#39;NULL::int AS &#39; , ivalues )::TEXT , &#39; ,&#39; ORDER BY ivalues )::TEXT
FROM (SELECT DISTINCT ivalues  FROM  split_clm_Nor2) a
)
,&#39; LIMIT 0 &#39;    
) ; -- RAISE NOTICE &#39;%&#39;, 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 := &#39;select name, &#39;;
-- build the list of columns based on the distinct hobbies
select string_agg(format(&#39;%L = any(hobbies) as %I&#39;, x.hobby_name, x.hobby_name), &#39;, &#39; 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, &#39;\s*,\s*&#39;)) 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, &#39;\s*,\s*&#39;) as hobbies from split_clm ) t $sql$ ;
-- recreate the view
execute &#39;drop view if exists clm_view cascade&#39;;
execute &#39;create view clm_view as &#39;||l_sql;    
end;
$body$ 
language plpgsql;

This will build a view that looks something like this:

select name, 
&#39;Athletics&#39; = any(hobbies) as &quot;Ahtletics&quot;,
&#39;Coding&#39; = any(hobbies) as &quot;Coding&quot;,  
.....
from ( select name, regexp_split_to_array(hobby, &#39;\s*,\s*&#39;) 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.

Online example

huangapple
  • 本文由 发表于 2023年3月4日 08:49:09
  • 转载请务必保留本文链接:https://go.coder-hub.com/75632991.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定