Postgres:将数组元素拆分为列

问题描述 投票:0回答:1

我是 postgres 的新手,我有一个要求,其中我需要 将整数数组拆分为单个元素(作为列)以便应用程序使用它。

考虑这个例子:

create table employees(age int, name text, scores int[]);
insert into employees values(25, 'Name1', ARRAY [10, 20, 30]);
insert into employees values(48, 'Name2', ARRAY [40, 50 , 60, 70]);
select scores[1], scores[2], scores[3] from employees where age > 40 and age < 50;

select 语句返回的输出为:

scores | scores | scores
-------------------------
  40   |   50   |   60

对于访问大数组的元素,上面的 select 语句的大小会相应增长,并且对于不同的数组大小来说不够通用。无论数组大小如何,我们是否可以有一个查询来优雅地执行此操作?

提前致谢!

postgresql
1个回答
0
投票

问题是,为什么您希望每个数组元素位于单独的列中?您可以

select * from employees cross join unnest(scores) with ordinality as u(score,score_number);
并在单独的 而不是列中获取每个分数,并使用
score_number
指示其来自数组中的位置。

select * from employees
  cross join unnest(scores) with ordinality as u(score,score_number);
年龄 名字 分数 分数 分数_数字
25 姓名1 {10,20,30} 10 1
25 姓名1 {10,20,30} 20 2
25 姓名1 {10,20,30} 30 3
48 姓名2 {40,50,60,70} 40 1
48 姓名2 {40,50,60,70} 50 2
48 姓名2 {40,50,60,70} 60 3
48 姓名2 {40,50,60,70} 70 4

否则,您将需要动态 SQL,并且需要间接使用它(两步调用)。 PostgreSQL 需要能够在查询开始时确定输出列的数量、名称和类型。您不可能有一个函数或语句能够完全动态地在不同时间以不同名称返回不同类型和数量的字段。

但是,您可以使用一个例程来动态构造查询并将其内容转储到表中,然后您可以在第二步中读取该表。您也可以将其卸载到前端,这通常很简单。

create function f_employee_scores_into_columns(employee_name text)returns text
language plpgsql as $f$
declare rec record;
        first_score_number int;
        last_score_number int;
        query_body text;
begin
  select array_lower(scores,1)
        ,array_upper(scores,1)
  from employees
  where employees.name=employee_name
  limit 1
  into first_score_number
      ,last_score_number;
  
  select string_agg(format('scores[%1$s] as "score_%1$s"',score_number)
                    ,',' order by score_number)
  from generate_series(first_score_number,last_score_number,1)score_number
  into query_body;

  execute format('drop table if exists output_f_employee_scores_into_columns;
                  create table output_f_employee_scores_into_columns as
                  select age,name,%s 
                  from employees 
                  where employees.name=$1',query_body) using employee_name;
  return 'output_f_employee_scores_into_columns';
end $f$;

--2-step call
select f_employee_scores_into_columns('Name2');
table output_f_employee_scores_into_columns;
f_employee_scores_into_columns
output_f_employee_scores_into_columns
年龄 名字 score_1 score_2 score_3 score_4
48 姓名2 40 50 60 70
--2-step call
select f_employee_scores_into_columns('Name1');
table output_f_employee_scores_into_columns;
f_employee_scores_into_columns
output_f_employee_scores_into_columns
年龄 名字 score_1 score_2 score_3
25 姓名1 10 20 30

小提琴

© www.soinside.com 2019 - 2024. All rights reserved.