postgresql和mysql的一转多多转一

postgresql和mysql的一转多多转一

日常工作中常有出现如下图的情形,需要将一个数据转成多行或者多列,或者由多行或者多列转成一个数据,本文分别用postgresql和mysql处理这个问题。
postgresql和mysql的一转多多转一_第1张图片
首先,先建立测试数据。

create table test(
boy_name varchar(255),
girl_name varchar(255)
);
insert into test values('张无忌','赵敏,周芷若,蛛儿,小昭');
insert into test values('郭靖','黄蓉,华筝');
insert into test values('大胖橘','甄嬛,皇后,华妃');

create table test1(
name1 varchar(255),
d1 varchar(255),
d2 varchar(255),
d3 varchar(255)
);
insert into test1 values('n1','2022','11','08');
insert into test1 values('n2','2022','09','18');
insert into test1 values('n3','2022','07','19');

create table test2(
name2 varchar(255),
spr varchar(255)
);
insert into test2 values ('n1','a');
insert into test2 values ('n1','b');
insert into test2 values ('n2','b');
insert into test2 values ('n3','c');

postgresql

一转多——列

在postgresql中,转成多列使用的是regexp_split_to_array函数。

  • regexp_split_to_array(string text, pattern text [, flags text ])
select * from test;
-- 一变多
-- 转行
select boy_name,a[1],a[2],a[3],a[4] from (
select boy_name,regexp_split_to_array(girl_name,',') a from test
)tmps;

查看结果显示如下图。regexp_split_to_array最后得到的是一个数组,要取出数组中的值,分别用下标位置1,2…取值,注意下标位置从1开始,而不是0。
postgresql和mysql的一转多多转一_第2张图片

一转多——行

postgresql中,转成多行用的方法有两种。
第一种用regexp_split_to_table函数。

  • regexp_split_to_table(string text, pattern text [, flags text])
select boy_name,regexp_split_to_table(girl_name,',') from test;

第二种方法就是用unnest

  • unnest(anyarray)
-- 写法一
select boy_name,unnest(regexp_split_to_array(girl_name,',')) from test;
-- 写法二
select boy_name,t.girl from test cross join unnest(regexp_split_to_array(girl_name,',')) as t(girl);

不管用上面哪种写法,都可以得到由1行数据转成多行的结果,结果显示如下图。
postgresql和mysql的一转多多转一_第3张图片

多列转一

将多列转成一列,原理就是将多列拼接起来变成一列。所以,所有的拼接方法都可以使用。例如:||,concat(),concat_ws()。

  • string || string
select name1,d1||'-'||d2||'-'||d3 from test1;
  • concat(str “any” [, str “any” [, …] ])
select name1,concat(d1,'-',d2,'-',d3) from test1;
  • concat_ws(sep text, str “any” [, str “any” [, …] ])
select name1,concat_ws('-',d1,d2,d3) from test1;

最后得到的结果都如下图所示。
postgresql和mysql的一转多多转一_第4张图片

多行转一

方法一:string_agg()

  • string_agg(expression, delimiter)
select name2,string_agg(spr,',') from test2 group by name2;

方法二:array_agg()

  • array_agg(expression)
select name2,array_agg(spr) from test2 group by name2;

通过array_agg()函数得到的结果是一个array形式的结果。并且array_agg(distinct 字段名)得到的结果可以字段结果去重。若是不想要array结果,只想要字符串结果,还可以通过array_to_string() 函数将结果转换成字符串。

  • array_to_string(anyarray, text [, text])
select name2,array_to_string(array_agg(spr),',') from test2 group by name2;

最后的结果显示,如下图。
postgresql和mysql的一转多多转一_第5张图片
若是想要多行转多列,或多列转多行,其原理就是先转成一行或一列,然后一行一列进行转换,举个例子。

-- 多转多

-- 列转行
# 方法一
select name1,regexp_split_to_table(concat_ws('-',d1,d2,d3),'-') from test1;
# 方法二
select name1,unnest(string_to_array(d1||','||d2||','||d3,',')) from test1;


-- 行转列
select name2,a[1],a[2] from (
select name2,array_agg(spr) a from test2 group by name2
)tmp;

最后得到的结果如下。
postgresql和mysql的一转多多转一_第6张图片
postgresql和mysql的一转多多转一_第7张图片

mysql

一转多——行

在mysql中,没有找到对应的函数,我看他的方法是利用辅助表mysql.help_topic实现的,并配合字符串切割函数substring_index() 来实现这个功能。

  • SUBSTRING_INDEX(str,delim,count)
select boy_name,substring_index(substring_index(test.girl_name,',',b.help_topic_id+1),',',-1)
from test
left join mysql.help_topic b 
on b.help_topic_id<(length(test.girl_name)-length(replace(test.girl_name,',',''))+1);

结果显示和postgresql一致,所以不再重复显示。

一转多——列

一列转多列,就通过上述方法,多写几次字段引用,就会得到多列的结果,代码如下。

with test1 as (
select boy_name,substring_index(girl_name,',',1) col1,
case when help_topic_id>=1 then
substring_index(substring_index(girl_name,',',2),',',-1) end as col2,
case when help_topic_id>=2 then 
substring_index(substring_index(girl_name,',',3),',',-1) end as col3,
case when help_topic_id>=3 then 
substring_index(substring_index(girl_name,',',4),',',-1) end as col4,
help_topic_id
from test
left join mysql.help_topic b on b.help_topic_id<(length(test.girl_name)-length(replace(test.girl_name,',',''))+1)
)

select a.boy_name,a.col1,a.col2,a.col3,a.col4 from test1 a where (
select count(1) from test1 b
where a.boy_name=b.boy_name and a.help_topic_id<=b.help_topic_id)<=1
order by a.help_topic_id desc
;

其中,使用case when的原因是,如果超过切割长度,例如:对应郭靖的女生名字只有两个,在超过这个长度取值时,都会返回最后一个女生名字。
不使用case when的结果显示如下图。
postgresql和mysql的一转多多转一_第8张图片
在使用case…when之后,若是超过字符串长度的获取下标","时,不满足条件的都会得到空值。如下图。
postgresql和mysql的一转多多转一_第9张图片
但是此时得到的结果是有很多多余的无效行的。从上图中可以看出,我们要的是每组数据的最后一行,最后一行数据都是完整无缺的,所以采用自连的方式,筛选出每组的最后一条数据作为最后的结果。最后的结果和之前postgresql一致,就不再给出。

多列转一

mysql多列转成一列的方法和postgresql一样,就是多个字段的拼接。用concat()concat_ws() 函数。代码如下:

  • CONCAT(str1,str2,…)
  • CONCAT_WS(separator,str1,str2,…)
select name1,concat(d1,'-',d2,'-',d3) from test1;
select name1,concat_ws('-',d1,d2,d3) from test1;

多行转一

在mysql中,将多行数据转化为一行的方法是使用group_concat() 函数。

  • GROUP_CONCAT([DISTINCT] expr [,expr …]
    [ORDER BY {unsigned_integer | col_name | expr}
    [ASC | DESC] [,col_name …]]
    [SEPARATOR str_val])
select  name2,GROUP_CONCAT(spr) from test2 group by name2;

mysql中的多行与多列的互相转换可以使用先转成一个数据的思路,这边就不再继续给出相应的sql。

本文到这里就结束了,感谢您的阅读~~

你可能感兴趣的:(postgresql,mysql,数据库)