Как разделить строку в sql на столбцы по разделителю
Имеется таблица:
| СТ 1 | СТ 2 |
|---|---|
| А | Z;Y;X;W |
| А;B | Z;Y;X |
| А;B;C | Z;Y |
| А;B;C;D | Z |
Необходимо разбить строки по разделителю ";" на отдельные столбцы, чтобы таблица приняла следующий вид:
| CТ 1.1 | CТ 1.2 | CТ 1.3 | CТ 1.4 | CТ 2.1 | CТ 2.2 | CТ 2.3 | CТ 2.4 |
|---|---|---|---|---|---|---|---|
| A | NULL | NULL | NULL | Z | Y | X | W |
| A | B | NULL | NULL | Z | Y | X | NULL |
| A | B | C | NULL | Z | Y | NULL | NULL |
| A | B | C | D | Z | NULL | NULL | NULL |
Количество столбцов, значения в которых нужно поделить по разделителю, заранее неизвестно. Так же нет информации про максимальное количество разделителей в строке (привел пример, когда таких встречается максимум 3, соответственно каждый из столбцов поделился на 4 столбца). Вероятно путь к решению данной задаче лежит через представление строк в виде массивов с помощью string_to_array:
SELECT string_to_array('А;B;C;D', ';') -> {A,B,C,D}
Однако, как указал ранее, количество таких столбцов заранее неизвестно.
Использую PostgreSQL 14.
Ответы (1 шт):
Автор решения: ValNik
→ Ссылка
Пример
create table test (id int,ct1 varchar(100), ct2 varchar(100));
insert into test values
(1,'А','Z;Y;X;W')
,(2,'А;B','Z;Y;X')
,(3,'А;B;C','Z;Y')
,(4,'А;B;C;D','Z')
;
select * from test;
Процедура разборки и сборки
create or replace procedure Pvtd()
AS $$
declare sSql0 varchar(1000);
declare sSql1 varchar(1000);
declare sSql2 varchar(1000);
BEGIN
-- упростим исходные данные: сделаем структуру линейной по одному элементу в строке
-- и запишем во временную таблицу vals
-- содержание получившейся таблицы показано ниже в тексте
sSql0:='create temporary table vals (id int,ct1 varchar(100),ct2 varchar(100)
,elem1 varchar(10), nr1 int,col varchar(10));';
execute sSql0;
with t as(
select id,ct1,ct2,elem1,nr1,concat('ct1_',nr1) col
from test t
left join unnest(string_to_array(t.ct1, ';'))WITH ORDINALITY AS c1(elem1, nr1)
on 1=1
union all
select id,ct1,ct2,elem2,nr2,concat('ct2_',nr2) col
from test t
left join unnest(string_to_array(t.ct2, ';'))WITH ORDINALITY AS c2(elem2, nr2)
on 1=1
)
insert into vals
select * from t;
-- создадим временную таблицу - выходной результат
-- здесь только динамический SQL
-- список колонок таблицы создаем через string_agg
sSql1:=concat('create temporary table pvtd_table '
,'(id int, ct1 varchar(100),ct2 varchar(100),'
, (select string_agg(distinct concat(col,' varchar(10)')
,',' order by concat(col,' varchar(10)' )) from vals),')'
);
execute sSql1;
-- собственно вычисление нужных данных с использованием CROSSTAB
-- список выходных колонок результата также создаем через string_agg
sSql2:=(select
concat('insert into pvtd_table select * '
,'from crosstab(',' ''select id,ct1,ct2,col,elem1 from vals order by 1,2,3'' '
,', ''select distinct col from vals order by 1'' ',')'
,'as pvt(id int, ct1 varchar,ct2 varchar,'
, (select string_agg(distinct concat(col,' varchar')
,',' order by concat(col,' varchar' )) from vals),')'
));
execute sSql2;
END
$$
LANGUAGE plpgsql;
-- эта строка нужна, чтобы был доступен CROSSTAB
CREATE EXTENSION IF NOT EXISTS tablefunc;
-- вызываем процедуру, чтобы получить результат во временную таблицу
call pvtd();
select * from pvtd_table;
Вывод
CREATE TABLE
INSERT 0 4
| id | ct1 | ct2 |
|---|---|---|
| 1 | А | Z;Y;X;W |
| 2 | А;B | Z;Y;X |
| 3 | А;B;C | Z;Y |
| 4 | А;B;C;D | Z |
SELECT 4
CREATE PROCEDURE
CREATE EXTENSION
CALL
| id | ct1 | ct2 | ct1_1 | ct1_2 | ct1_3 | ct1_4 | ct2_1 | ct2_2 | ct2_3 | ct2_4 |
|---|---|---|---|---|---|---|---|---|---|---|
| 1 | А | Z;Y;X;W | А | null | null | null | Z | Y | X | W |
| 2 | А;B | Z;Y;X | А | B | null | null | Z | Y | X | null |
| 3 | А;B;C | Z;Y | А | B | C | null | Z | Y | null | null |
| 4 | А;B;C;D | Z | А | B | C | D | Z | null | null | null |
Промежуточная таблица vals
| id | ct1 | ct2 | col | elem1 |
|---|---|---|---|---|
| 1 | А | Z;Y;X;W | ct1_1 | А |
| 1 | А | Z;Y;X;W | ct2_1 | Z |
| 1 | А | Z;Y;X;W | ct2_2 | Y |
| 1 | А | Z;Y;X;W | ct2_3 | X |
| 1 | А | Z;Y;X;W | ct2_4 | W |
| 2 | А;B | Z;Y;X | ct2_3 | X |
| 2 | А;B | Z;Y;X | ct1_2 | B |
| 2 | А;B | Z;Y;X | ct1_1 | А |
| 2 | А;B | Z;Y;X | ct2_2 | Y |
| 2 | А;B | Z;Y;X | ct2_1 | Z |
| 3 | А;B;C | Z;Y | ct1_2 | B |
| 3 | А;B;C | Z;Y | ct2_1 | Z |
| 3 | А;B;C | Z;Y | ct2_2 | Y |
| 3 | А;B;C | Z;Y | ct1_1 | А |
| 3 | А;B;C | Z;Y | ct1_3 | C |
| 4 | А;B;C;D | Z | ct1_3 | C |
| 4 | А;B;C;D | Z | ct1_2 | B |
| 4 | А;B;C;D | Z | ct1_1 | А |
| 4 | А;B;C;D | Z | ct2_1 | Z |
| 4 | А;B;C;D | Z | ct1_4 | D |