Split column to multiple columns in PostgreSQL - postgresql

I am going to split one column of my table to multiple columns. The fields are separated by comma and I used the split_part. The problem that I faced is the length of string are not same and after splitting they are located from column 1 to column N.
Column
a,b,c,d,e,f
b,c,e,f
c,d,e,f
my output is:
col1 col2 col3 col4 col5 col6
a b c d e f
b c e f
c d e f
but I want to have an output like this
col1 col2 col3 col4 col5 col6
a b c d e f
b c e f
c d e f

Table and data def shamelessly stolen from #xehpuk
CREATE TABLE split_column (
id integer PRIMARY KEY,
col text NOT NULL
)
data
INSERT INTO split_column (id, col) VALUES
(1, 'a,b,c,d,e,f'),
(2, 'b,c,e,f'),
(3, 'c,d,e,f')
Use Case statements in query:
select case when position('a' in col)::bool then 'a' end as col1,
case when position('b' in col)::bool then 'b' end as col2,
case when position('c' in col)::bool then 'c' end as col3,
case when position('d' in col)::bool then 'd' end as col4,
case when position('e' in col)::bool then 'e' end as col5,
case when position('f' in col)::bool then 'f' end as col6
from aaa.split_column
col1
col2
col3
col4
col5
col6
a
b
c
d
e
f
null
b
c
null
e
f
null
null
c
d
e
f

Given this table:
CREATE TABLE split_column (
id integer PRIMARY KEY,
col text NOT NULL
)
And this data:
INSERT INTO split_column (id, col) VALUES
(1, 'a,b,c,d,e,f'),
(2, 'b,c,e,f'),
(3, 'c,d,e,f')
The first row contains all values:
SELECT string_to_array(col, ',') AS values
FROM split_column
ORDER BY id
LIMIT 1
values
{a,b,c,d,e,f}
We use it to create a mapping from col1..colN to the values:
WITH complete AS (
SELECT string_to_array(col, ',') AS values
FROM split_column
ORDER BY id
LIMIT 1
)
SELECT 'col' || value.ordinality AS key, value
FROM complete, unnest(complete.values) WITH ORDINALITY AS value
key
value
col1
a
col2
b
col3
c
col4
d
col5
e
col6
f
We aggregate the rows to JSON objects:
WITH complete AS (
SELECT string_to_array(col, ',') AS values
FROM split_column
ORDER BY id
LIMIT 1
), map AS (
SELECT 'col' || value.ordinality AS key, value
FROM complete, unnest(complete.values) WITH ORDINALITY AS value
)
SELECT id, jsonb_object_agg(map.key, map.value) AS object
FROM split_column, string_to_table(col, ',') AS col
JOIN map
ON col = map.value
GROUP BY id
ORDER BY id
id
object
1
{"col1": "a", "col2": "b", "col3": "c", "col4": "d", "col5": "e", "col6": "f"}
2
{"col2": "b", "col3": "c", "col5": "e", "col6": "f"}
3
{"col3": "c", "col4": "d", "col5": "e", "col6": "f"}
And then we "destructure" them back into records:
WITH complete AS (
SELECT string_to_array(col, ',') AS values
FROM split_column
ORDER BY id
LIMIT 1
), map AS (
SELECT 'col' || value.ordinality AS key, value
FROM complete, unnest(complete.values) WITH ORDINALITY AS value
), agg AS (
SELECT id, jsonb_object_agg(map.key, map.value) AS object
FROM split_column, string_to_table(col, ',') AS col
JOIN map
ON col = map.value
GROUP BY id
)
SELECT agg.id, cols.*
FROM agg, jsonb_to_record(agg.object) AS cols(col1 text, col2 text, col3 text, col4 text, col5 text, col6 text)
ORDER BY id
id
col1
col2
col3
col4
col5
col6
1
a
b
c
d
e
f
2
b
c
e
f
3
c
d
e
f
db<>fiddle

Related

How to select and count pairs or triplets from 5 columns in PostgreSQL

I have a postgreSQL table where i store in five different columns (say col1, col2, col3, col4, col5) numbers. In each row the numbers stored are different from each other.
I want to make some select that gives me the pairs that exists and how many times they appear (count the times that they are present in rows).
Example:
col1
col2
col3
col4
col5
1
5
10
20
100
5
20
30
40
100
Results (something more or less like this):
pair
total
1,5
1
1,10
1
1,20
1
1,100
1
5,10
1
5,20
2
5,100
2
10,20
1
10,100
1
20,100
2
5,30
1
5,40
1
20,30
1
20,40
1
I can make some SQL to execute code in specific columns:
SELECT count (*) as total, col1, col2
FROM numbers
group by col1, col2;
But that won't give me all the combinations and I don't know how to get the rest of the pairs. Also, this may be inefficient in terms of performance.
Any help will be appreciated.
Regards,
Miguel.
You can use recursive query for generating the output you are expecting
Demo
-- You can change "select 3" to "select 2" or any number you want
with recursive col_join as (select 3),
numbers_row as (
select *, row_number() over () as row
from numbers
),
cte_r as (
select col1 as value, row, 1 as col
from numbers_row
union all
select col2 as value, row, 2 as col
from numbers_row
union all
select col3 as value, row, 3 as col
from numbers_row
union all
select col4 as value, row, 4 as col
from numbers_row
union all
select col5 as value, row, 5 as col
from numbers_row
),
cte as (
select array_agg(value order by col) as value,
row,
col
from cte_r
group by row, col
union all
select c.value || cr1.value as value,
cr1.row,
cr1.col
from cte c,
cte_r cr1
where c.row = cr1.row
and not c.value #> array [cr1.value]
and c.col < cr1.col
and array_length(c.value || cr1.value, 1) <= (select * from col_join)
)
select array_to_string(value, ','), count(*)
from cte
where array_length(value, 1) = (select * from col_join)
group by 1
order by 1
**Old scenario for joining two-column"
Demo
with recursive
numbers_row as (
select *, row_number() over () as row from numbers
),
cte_r as (
select col1 as value, row, 1 as col from numbers_row
union all
select col2 as value, row, 2 as col from numbers_row
union all
select col3 as value, row, 3 as col from numbers_row
union all
select col4 as value, row, 4 as col from numbers_row
union all
select col5 as value, row, 5 as col from numbers_row
),
cte as (
select
value as val1,
value as val2,
row,
col
from cte_r
union all
select
c.val1 as val1,
cr.value as val2,
cr.row,
cr.col
from
cte c,
cte_r cr
where c.row = cr.row and c.col = cr.col - 1
)
select val1 || ',' || val2, count(*)
from cte
where val1 <> val2
group by val1, val2, val1 || ',' || val2
order by val1, val2

Filter rows in PostgreSQL table based on column match conditions

I have following table in PostgreSQL 11.0
col1 col2 col3 col4
1 a a a
1 a a a_1
1 a a a_2
1 b b c
2 d d c
3 e d e
I would like to filter above table such that if col2 and col4 are equal, only this match should be selected and below two rows are excluded. When col2 and col4 are not equal, rows with col2 = col3 should be kept.
The desired output is:
col1 col2 col3 col4
1 a a a
1 b b c
2 d d c
3 e d e
I am trying following query with no success so far.
select * from table1
where col2=col4
union
select * from table1
where col2 != col4 and col2=col3
but this will include rows where there is already a match, which I want to exclude in the final output.
1 a a a_1
1 a a a_2
I would use
SELECT DISTINCT ON (col2) *
FROM table1
WHERE col2 = col4 OR col2 = col3
ORDER BY col2, col2 IS DISTINCT FROM col4;
This relies on FALSE < TRUE.
As per my understanding you want unique col2 in you result with given conditions:
Try this:
with cte as
(select *,
case
when col2=col4 then 2
when col2=col3 then 1
else 0
end "flag" from table1 )
select distinct on(col2) col1,col2,col3,col4 from cte where flag>0
order by col2, "flag" desc
Demo on Fiddle

PostgreSQL mass insert into a table?

I need to mass insert all the values from col_a into another table. I can do it one at a time like this:
INSERT INTO table_2 (col_a_id)
SELECT 'col_a_id'
FROM table_1
WHERE col_a = 'x';
But is there a way I can just insert all the columns?
EDIT
Lets say I have this table:
Col_a | Col_b |
------------------------
1 | a |
2 | b |
3 | c |
Instead of checking what is in col_a can I just insert each instance of col_a into a table? so I'll have 1, 2 & 3 in table_2?
INSERT INTO table_2 (col1, col2, col3, .... , coln)
SELECT col1, col2, col3, .... , coln
FROM table_1
WHERE col_a = 'x';
Note: String are separated by single quote
SELECT 'this is a string'
Fieldname use double quote:
SELECT "myFieldName", "col1"
EDIT:
If you want check all columns for 'x'
WHERE 'x' IN (col1, col2, col3, .... , coln)

How to write One query for multiple groupings?

In the queries below, how can I make just one query that will give me the results, instead of making copies with diff groupings and unioning them?
If possible.
Thanks in advance!!
`create table #temp1 (col1 varchar(50), col2 varchar(50), col3 varchar(50), col4 varchar(50), col5 varchar(50), sumit int)
insert into #temp1 values('AEAMS','CE Europe', 'Belarus', 'Govt', 'Int Fed Gvt', 1)
insert into #temp1 values('AEAMS','CE Europe', 'Belarus', 'Govt', 'Public Lib', 1)
insert into #temp1 values('AEDS','Japan', 'Japan C', 'Acad', 'CollUnive', 1)
insert into #temp1 values('AEDS','Japan', 'Japan F', 'Acad', 'Med', 1)
insert into #temp1 values('A- Regular Databases','UK and Ireland', 'Ireland', 'School', 'HIGH SCHOOL', 1)
Select col1 CC, null GM, null Terr, null Mkt, null Seg, sum(sumit) SS
from #temp1
group by col1
Union
Select col1 CC, col2 GM, null Terr, null Mkt, null Seg, sum(sumit) SS
from #temp1
group by col1, col2
Union
Select col1 CC, col2 GM, col3 Terr, null Mkt, null Seg, sum(sumit) SS
from #temp1
group by col1, col2, col3
Union
Select col1 CC, col2 GM, col3 Terr, col4 Mkt, null Seg, sum(sumit) SS
from #temp1
group by col1, col2, col3, col4, col5
Try using WITH ROLLUP:
Select col1 CC, col2 GM, col3 Terr, col4 Mkt, null Seg, sum(sumit) SS
from #temp1
group by col1, col2, col3, col4, col5
with rollup
SQL Fiddle Example

T-SQL -- convert comma-delimited column into multiple columns

From the table below, how can I convert the Values column into multiple columns, populated with individual values that are currently separated by commas? Before the conversion:
Name Values
---- ------
John val,val2,val3
Peter val5,val7,val9,val14
Lesli val8,val34,val36,val65,val71,val
Amy val3,val5,val99
The result of the conversion should look like:
Name Col1 Col2 Col3 Col4 Col5 Col6
---- ---- ---- ---- ---- ---- ----
John val val2 val3
Peter val5 val7 val9 val14
Lesli val8 val34 val36 val65 val71 val
Amy val3 val5 val99
First, what database product and version are you using? If you are using SQL Server 2005 and later, you can write a Split user-defined function like so:
CREATE FUNCTION [dbo].[Split]
(
#DelimitedList nvarchar(max)
, #Delimiter varchar(2) = ','
)
RETURNS TABLE
AS
RETURN
(
With CorrectedList As
(
Select Case When Left(#DelimitedList, DataLength(#Delimiter)) <> #Delimiter Then #Delimiter Else '' End
+ #DelimitedList
+ Case When Right(#DelimitedList, DataLength(#Delimiter)) <> #Delimiter Then #Delimiter Else '' End
As List
, DataLength(#Delimiter) As DelimiterLen
)
, Numbers As
(
Select TOP (Coalesce(Len(#DelimitedList),1)) Row_Number() Over ( Order By c1.object_id ) As Value
From sys.objects As c1
Cross Join sys.columns As c2
)
Select CharIndex(#Delimiter, CL.list, N.Value) + CL.DelimiterLen As Position
, Substring (
CL.List
, CharIndex(#Delimiter, CL.list, N.Value) + CL.DelimiterLen
, CharIndex(#Delimiter, CL.list, N.Value + 1)
- ( CharIndex(#Delimiter, CL.list, N.Value) + CL.DelimiterLen )
) As Value
From CorrectedList As CL
Cross Join Numbers As N
Where N.Value < Len(CL.List)
And Substring(CL.List, N.Value, CL.DelimiterLen) = #Delimiter
)
You can then split out the values in you want using something akin to:
Select Name, Values
From Table1 As T1
Where Exists (
Select 1
From Table2 As T2
Cross Apply dbo.Split (T1.Values, ',') As T1Values
Cross Apply dbo.Split (T2.Values, ',') As T2Values
Where T2.Values.Value = T1Values.Value
And T1.Name = T2.Name
)
Here is a solution that uses a recursive cte to generate a "table of numbers" (courtesy of Itzik Ben-Gan), which is useful for all manner of problems including string splitting, and PIVOT. SQL Server 2005 onwards. Full table create, insert and select script included.
CREATE TABLE dbo.Table1
(
Name VARCHAR(30),
[Values] VARCHAR(128)
)
GO
INSERT INTO dbo.Table1 VALUES ('John', 'val,val2,val3')
INSERT INTO dbo.Table1 VALUES ('Peter', 'val5,val7,val9,val14')
INSERT INTO dbo.Table1 VALUES ('Lesli', 'val8,val34,val36,val65,val71,val')
INSERT INTO dbo.Table1 VALUES ('Amy', 'val3,val5,val99')
GO
SELECT * FROM dbo.Table1;
GO
WITH
L0 AS(SELECT 1 AS c UNION ALL SELECT 1),
L1 AS(SELECT 1 AS c FROM L0 AS A, L0 AS B),
L2 AS(SELECT 1 AS c FROM L1 AS A, L1 AS B),
L3 AS(SELECT 1 AS c FROM L2 AS A, L2 AS B),
Numbers AS(SELECT ROW_NUMBER() OVER(ORDER BY c) AS n FROM L3)
SELECT Name, [1] AS Column1, [2] AS Column2, [3] AS Column3, [4] AS Column4, [5] AS Column5, [6] AS Column6, [7] AS Column7
FROM
(SELECT Name,
ROW_NUMBER() OVER (PARTITION BY Name ORDER BY nums.n) AS PositionInList,
LTRIM(RTRIM(SUBSTRING(valueTable.[Values], nums.n, charindex(N',', valueTable.[Values] + N',', nums.n) - nums.n))) AS [Value]
FROM Numbers AS nums INNER JOIN dbo.Table1 AS valueTable ON nums.n <= CONVERT(int, LEN(valueTable.[Values])) AND SUBSTRING(N',' + valueTable.[Values], n, 1) = N',') AS SourceTable
PIVOT
(
MAX([VALUE]) FOR PositionInList IN ([1], [2], [3], [4], [5], [6], [7])
) AS Table2
GO
--DROP TABLE dbo.Table1
Which converts this output
Name Values
John val,val2,val3
Peter val5,val7,val9,val14
Lesli val8,val34,val36,val65,val71,val
Amy val3,val5,val99
to
Name Column1 Column2 Column3 Column4 Column5 Column6 Column7
Amy val3 val5 val99 NULL NULL NULL NULL
John val val2 val3 NULL NULL NULL NULL
Lesli val8 val34 val36 val65 val71 val NULL
Peter val5 val7 val9 val14 NULL NULL NULL