将 VARCHAR 数据排列到 SQL 中的列中
Arranging VARCHAR data into Columns in SQL
我的数据在 SQL table 中,格式如下(每个 user_id 我有大约 20 个不同的答案):
USER_ID ANSWER
--------------------------
1 CAR
1 10-20 miles
1 SALES
2 TRAIN
2 0-10 miles
2 TEACHER
我想在 PostgreSQL 中创建一个视图,其中根据用户 ID 显示所有数据
USER_ID ANSWER1 ANSWER2 ANSWER3
-----------------------------------------------
1 CAR 10-20 miles SALES
2 TRAIN 0-10 miles TEACHER
谢谢!
这是一个很好的非关系数据的例子(语义取决于行号),尽管数据保存在关系数据库中。您不能从关系数据库内部的非关系中创建关系数据。没有任何机制可以安全地进行这种转换。从理论上讲,如果您的数据的物理顺序与图片中的相同,那么您可以编写查询(但您不应该使用 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)
主要问题出在输入数据的格式上。它对于文件和应用程序处理来说已经足够好了,但对于关系数据库中的存储(和处理)来说却非常糟糕。关系(或 table)是堆(不是文件)。
通过了解 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)
fiddle 对于正则表达式:https://regex101.com/r/oUgYbj/1
这仅在假设您对 q1、q2、q3 的顺序始终相同的情况下才有效
我相信您的数据应该有一些字段来排序,以避免混淆,比如新列:Question_rank 然后它显示值
1 表示汽车和火车,2 表示英里,3 表示工作,即销售/教师。
这样我们就可以在 order by 子句中将 Question_rank 作为第二列
with data as(
select
user_id,
STRING_AGG(answer,',') as all_answers
from table
group by 1
Order by id
)
select
user_id,
REGEXP_MATCHES (all_answers,'^([^,])+') AS answer_1,
REGEXP_MATCHES (all_answers,'\d+(-)\d+\s+\w+') AS answer_2
REGEXP_MATCHES (all_answers,'[^,]*$') AS answer_3
from data
我的数据在 SQL table 中,格式如下(每个 user_id 我有大约 20 个不同的答案):
USER_ID ANSWER
--------------------------
1 CAR
1 10-20 miles
1 SALES
2 TRAIN
2 0-10 miles
2 TEACHER
我想在 PostgreSQL 中创建一个视图,其中根据用户 ID 显示所有数据
USER_ID ANSWER1 ANSWER2 ANSWER3
-----------------------------------------------
1 CAR 10-20 miles SALES
2 TRAIN 0-10 miles TEACHER
谢谢!
这是一个很好的非关系数据的例子(语义取决于行号),尽管数据保存在关系数据库中。您不能从关系数据库内部的非关系中创建关系数据。没有任何机制可以安全地进行这种转换。从理论上讲,如果您的数据的物理顺序与图片中的相同,那么您可以编写查询(但您不应该使用 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)
主要问题出在输入数据的格式上。它对于文件和应用程序处理来说已经足够好了,但对于关系数据库中的存储(和处理)来说却非常糟糕。关系(或 table)是堆(不是文件)。
通过了解 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)
fiddle 对于正则表达式:https://regex101.com/r/oUgYbj/1
这仅在假设您对 q1、q2、q3 的顺序始终相同的情况下才有效 我相信您的数据应该有一些字段来排序,以避免混淆,比如新列:Question_rank 然后它显示值 1 表示汽车和火车,2 表示英里,3 表示工作,即销售/教师。
这样我们就可以在 order by 子句中将 Question_rank 作为第二列
with data as(
select
user_id,
STRING_AGG(answer,',') as all_answers
from table
group by 1
Order by id
)
select
user_id,
REGEXP_MATCHES (all_answers,'^([^,])+') AS answer_1,
REGEXP_MATCHES (all_answers,'\d+(-)\d+\s+\w+') AS answer_2
REGEXP_MATCHES (all_answers,'[^,]*$') AS answer_3
from data