Split single row value to multiple rows in Snowflake - sql

I have a table where the column data has a combination of values seperated by ';'. I would like to split them into rows for each column value.
Table data
Now I would like to split them into multiple rows for each value like
I have tried using the below SQL statement.
SELECT DISTINCT COL_NAME FROM "DB"."SCHEMA"."TABLE,
LATERAL FLATTEN(INPUT=>SPLIT(COL_NAME,';'))
But the output is not as expected. Attaching the query output below.
Basically the query does nothing to my data.

It could be achieved using SPLIT_TO_TABLE table function:
This table function splits a string (based on a specified delimiter) and flattens the results into rows.
SELECT *
FROM tab, LATERAL SPLIT_TO_TABLE(column_name, ';')

I was able to resolve this by using LATERAL FLATTERN like a joining table and selecting the value from it.
SELECT DISTINCT A.VALUE AS COL_NAME
FROM "DB"."SCHEMA"."TABLE",
LATERAL SPLIT_TO_TABLE(COL_NAME,';')A

Looks your data has multiple delimiters , We can leverage STRTOK_SPLIT_TO_TABLE function using multiple delimiters..
STRTOK_SPLIT_TO_TABLE
WITH data AS (
SELECT *
FROM VALUES
('Greensboro-High Point-Winston-Salem;Norfolk-Portsmouth-Newport News Washington, D.C. Roanoke-Lynchburg Richmond-Petersburg')
v( cities))
select *
from data, lateral strtok_split_to_table(cities, ';-')
order by seq, index;
Result:

Your first attempt was very close, you just need to access the out of the flatten, instead of the input to the flatten
so using this CTE for data:
WITH fake_data AS (
SELECT *
FROM VALUES
('Greensboro-High Point-Winston-Salem;Norfolk-Portsmouth-Newport News;Washington, D.C.;Roanoke-Lynchburg;Richmond-Petersburg'),
('Knoxville'),
('Knoxville;Memphis;Nashville')
v( COL_NAME)
)
if you had aliased you tables, and accessed the parts.
SELECT DISTINCT f.value::text as col_name
FROM fake_data d,
LATERAL FLATTEN(INPUT=>SPLIT(COL_NAME,';')) f
;
which is what you did in your provided answer, but via SPLIT_TO_TABLE
SELECT DISTINCT f.value as col_name
FROM fake_data d,
TABLE(SPLIT_TO_TABLE(COL_NAME,';')) f
;
STRTOK_SPLIT_TO_TABLE also is the same thing:
SELECT DISTINCT f.value as col_name
FROM fake_data d,
TABLE(strtok_split_to_table(COL_NAME,';')) f
;
Which can also be done via a strtok_to_array and FLATTEN that
SELECT DISTINCT f.value as col_name
FROM fake_data d,
TABLE(FLATTEN(input=>STRTOK_TO_ARRAY(COL_NAME,';'))) f
;
COL_NAME
Greensboro-High Point-Winston-Salem
Norfolk-Portsmouth-Newport News
Washington, D.C.
Roanoke-Lynchburg
Richmond-Petersburg
Knoxville
Memphis
Nashville

Related

Simplest way to unnest a BQ table

I can directly unnest an array from a literal in BQ as follows:
select * from unnest([1,2,3])
# [{"f0_": "1"},{"f0_": "2"},{"f0_": "3"}]
How can I do the same from a with statement? For example, something like:
with Table as (
select [1,2,3] as arr
) select * from unnest(Table.arr) ?? -- currently get unrecognized table name
What would be the correct syntax for the above? Here would be one example I came up with: is this the simplest way to do a general unnest?
with Movie as (
select "Spider-Man" as Title, ['Sci-Fi', 'Action'] as Genres
) select Movie.Title, Genre from Movie cross join unnest(Movie.Genres) Genre
If so, why can't the above be done without a cross join?
How can I do the same from a with statement?
with Table as (
select [1,2,3] as arr
)
select x from Table, unnest(arr) x
is this the simplest way to do a general unnest?
Yes!

Get Distinct Value in a List/Cell in PostgreSQL

it happens that I am having this type of data structure and trying to eliminate the duplicated values under Type in Postgres.
Initial Table
Index Type
1 A, B
2 A, A
3 B, B
Expected Table
Index Type
1 A, B
2 A
3 B
Thanks for the help!
You could use a CTE to split the comma separated values into rows using STRING_TO_ARRAY and UNNEST; then put the distinct values back together again using STRING_AGG:
WITH Types AS (
SELECT DISTINCT Index, UNNEST(STRING_TO_ARRAY(Type, ', ')) AS Type
FROM Data
)
SELECT Index, STRING_AGG(Type, ', ') AS Type
FROM Types
GROUP BY Index
ORDER BY Index
Output:
Index Type
1 A, B
2 A
3 B
Demo on SQLFiddle
Here is an alternative approach that doesn't use aggregation over the entire table:
SELECT Index,
(SELECT STRING_AGG(DISTINCT t, ', ')
FROM UNNEST(STRING_TO_ARRAY(Type, ', ')) AS t
) as types
FROM Data;
Here is a db<>fiddle.
Although I would expect Avoiding the outer aggregation would be a win on performance on larger data sets, it doesn't appear to be so.

How To Transpose / Convert Columns And Rows Into Single Column oracle sql?

How could you convert or transpose a range of data into a single column as shown above? Values could be ambiguous in data but output should contain unique values only.
(Updated after more information was provided in comments)
If your initial data comes from a query you could use a common table expression to do this:
with query_results (a,b,c) as (
... your original query that you have not shown us goes here ...
)
select a
from query_results
union
select b
from query_results
union
select c
from query_results
order by 1
The UNION operator will remove duplicates from the output
You can use UNPIVOT:
SELECT value
FROM your_table
UNPIVOT ( value FOR type IN ( a, b, c ) );

Unpivoting multiple columns

I have a table in SQL Server 2014 called anotes with the following data
and I want to add this data into another table named final as
ID Notes NoteDate
With text1, text2, text3, text4 going into the Notes column in the final table and Notedate1,notedate2,notedate3,notedate4 going into Notedate column.
I tried unpivoting the data with notes first as:
select createdid, temp
from (select createdid,text1,text2,text3,text4 from anotes) p
unpivot
(temp for note in(text1,text2,text3,text4)) as unpvt
order by createdid
Which gave me proper results:
and then for the dates part I used another unpivot query:
select createdid,temp2
from (select createdid,notedate1,notedate2,notedate3,notedate4 from anotes) p
unpivot (temp2 for notedate in(notedate1,notedate2,notedate3,notedate4)) as unpvt2
which also gives me proper results:
Now I want to add this data into my final table.
and I tried the following query and it results into a cross join :(
select a.createdid, a.temp, b.temp2
from (select createdid, temp
from (select createdid,text1,text2,text3,text4 from anotes) p
unpivot
(temp for note in(text1,text2,text3,text4)) as unpvt) a inner join (select createdid,temp2
from (select createdid,notedate1,notedate2,notedate3,notedate4 from anotes) p
unpivot (temp2 for notedate in(notedate1,notedate2,notedate3,notedate4)) as unpvt) b on a.createdid=b.createdid
The output is as follows:
Is there any way where I can unpivot both the columns at the same time?
Or use two select queries to add that data into my final table?
Thanks in advance!
I would say the most concise, and probably most efficient way to unpivot multiple columns is to use CROSS APPLY along with a table valued constructor:
SELECT t.CreatedID, upvt.Text, upvt.NoteDate
FROM anotes t
CROSS APPLY
(VALUES
(Text1, NoteDate1),
(Text2, NoteDate2),
(Text3, NoteDate3),
(Text4, NoteDate4),
(Text5, NoteDate5),
(Text6, NoteDate6),
(Text7, NoteDate7)
) upvt (Text, NoteDate);
Simplified Example on SQL Fiddle
ADDENDUM
I find the concept quite a hard one to explain, but I'll try. A table valued constuctor is simply a way of defining a table on the fly, so
SELECT *
FROM (VALUES (1, 1), (2, 2)) t (a, b);
Will Create a table with Alias t with data:
a b
------
1 1
2 2
So when you use it inside the APPLY you have access to all the outer columns, so it is just a matter of defining your constructed tables with the correct pairs of values (i.e. text1 with date1).
Used the link above mentioned by #AHiggins
Following is my final query!
select createdid,temp,temp2
from (select createdid,text1,text2,text3,text4,text5,text6,text7,notedate1,notedate2,notedate3,notedate4,notedate5,notedate6,notedate7 from anotes) main
unpivot
(temp for notes in(text1,text2,text3,text4,text5,text6,text7)) notes
unpivot (temp2 for notedate in(notedate1,notedate2,notedate3,notedate4,notedate5,notedate6,notedate7)) Dates
where RIGHT(notes,1)=RIGHT(notedate,1)
Treat each query as a table and join them together based on the createdid and the fieldid (the numeric part of the field name).
select x.createdid, x.textValue, y.dateValue
from
(
select createdid, substring(note, 5, len(note)) fieldId, textValue
from (select createdid,text1,text2,text3,text4 from anotes) p
unpivot
(textValue for note in(text1,text2,text3,text4)) as unpvt
)x
join
(
select createdid, substring(notedate, 9, len(notedate)) fieldId, dateValue
from (select createdid,notedate1,notedate2,notedate3,notedate4 from anotes) p
unpivot (dateValue for notedate in(notedate1,notedate2,notedate3,notedate4)) as unpvt2
) y on x.fieldId = y.fieldId and x.createdid = y.createdid
order by x.createdid, x.fieldId
The other answer given won't work if you have too many columns and the rightmost number of the field name is duplicated (e.g. text1 and text11).

Converting a pivot table to a flat table in SQL

I would like to transform a pivot table into a flat table, but in the following fashion: consider the simple example of this table:
As you can see, for each item - Address or Income -, we have a column for old values, and a column for new (updated values). I would like to convert the table to a "flat" table, looking like:
Is there an easy way of doing that?
Thank you for your help!
In order to get the result, you will need to UNPIVOT the data. When you unpivot you convert the multiple columns into multiple rows, in doing so the datatypes of the data must be the same.
I would use CROSS APPLY to unpivot the columns in pairs:
select t.employee_id,
t.employee_name,
c.data,
c.old,
c.new
from yourtable t
cross apply
(
values
('Address', Address_Old, Address_new),
('Income', cast(income_old as varchar(15)), cast(income_new as varchar(15)))
) c (data, old, new);
See SQL Fiddle with demo. As you can see this uses a cast on the income columns because I am guessing it is a different datatype from the address. Since the final result will have these values in the same column the data must be of the same type.
This can also be written using CROSS APPLY with UNION ALL:
select t.employee_id,
t.employee_name,
c.data,
c.old,
c.new
from yourtable t
cross apply
(
select 'Address', Address_Old, Address_new union all
select 'Income', cast(income_old as varchar(15)), cast(income_new as varchar(15))
) c (data, old, new)
See Demo
select employee_id,employee_name,data,old,new
from (
select employee_id,employee_name,adress_old as old,adress_new as new,'ADRESS' as data
from employe
union
select employee_id,employee_name,income_old,income_new,'INCOME'
from employe
) data
order by employee_id,data
see this fiddle demo : http://sqlfiddle.com/#!2/64344/7/0