这是非关系数据的一个很好的例子(语义取决于行号),尽管数据保存在关系数据库中。您不能从关系数据库内部的非关系中生成关系数据。没有任何机制可以安全地进行这种转换。从理论上讲,如果您的数据的物理顺序与图片中的相同,那么您可以编写查询(但您不应该使用ORDER BY 子句,因为它可以更改顺序,因为您的数据不包含正确排序所需的数据):
你需要的功能:
CREATE OR REPLACE FUNCTION public.x_transformed()
RETURNS TABLE(user_id integer, rowno integer, answer character varying)
LANGUAGE plpgsql
AS $function$
declare r record;
prev_user_id int;
begin
for r in select * from x
loop
if user_id is distinct from r.user_id then
rowno := 1;
else
rowno := rowno + 1;
end if;
user_id = r.user_id;
answer = r.answer;
return next;
end loop;
end;
$function$
此功能可以将缺少的订单信息添加到关系中。这是一个非常丑陋的解决方案,但是您的输入数据的格式非常不愉快,并且不可能有任何干净的解决方案(基于关系代数):
postgres=# select * from x_transformed();
┌─────────┬───────┬─────────────┐
│ user_id │ rowno │ answer │
╞═════════╪═══════╪═════════════╡
│ 1 │ 1 │ CAR │
│ 1 │ 2 │ 10-20 miles │
│ 1 │ 3 │ SALES │
│ 2 │ 1 │ TRAIN │
│ 2 │ 2 │ 0-10 miles │
│ 2 │ 3 │ TEACHER │
└─────────┴───────┴─────────────┘
(6 rows)
现在,转换为请求的格式很容易(搜索旋转):
select user_id,
max(answer) filter (where rowno = 1) as answer1,
max(answer) filter (where rowno = 2) as answer2,
max(answer) filter (where rowno = 3) as answer3
from x_transformed() group by user_id;
┌─────────┬─────────┬─────────────┬─────────┐
│ user_id │ answer1 │ answer2 │ answer3 │
╞═════════╪═════════╪═════════════╪═════════╡
│ 2 │ TRAIN │ 0-10 miles │ TEACHER │
│ 1 │ CAR │ 10-20 miles │ SALES │
└─────────┴─────────┴─────────────┴─────────┘
(2 rows)
主要问题在于输入数据的格式。它对于文件和应用程序处理可能已经足够好,但对于在关系数据库中存储(和处理)来说却是非常糟糕的。关系(或表)是堆(不是文件)。
了解 Postgres 的内部结构后,尽管您使用了一些关系操作,但您可以确保预期的顺序。您可以使用内部隐式列ctid。 ctid 是行的唯一地址。这不适用于其他数据库,也不适用于较旧的 Postgres 版本:
select user_id,
max(answer) filter (where rowno = 1) as answer1,
max(answer) filter (where rowno = 2) as answer2,
max(answer) filter (where rowno = 3) as answer3
from (select user_id,
answer,
row_number() over (partition by user_id
order by ctid) as rowno
from x) s
group by user_id
order by user_id;
┌─────────┬─────────┬─────────────┬─────────┐
│ user_id │ answer1 │ answer2 │ answer3 │
╞═════════╪═════════╪═════════════╪═════════╡
│ 1 │ CAR │ 10-20 miles │ SALES │
│ 2 │ TRAIN │ 0-10 miles │ TEACHER │
└─────────┴─────────┴─────────────┴─────────┘
(2 rows)