How to Split Data in TSQL - sql

need some help figuring out how to split up some data
the Data Currently looks like this
╔═════════════════════════════════════════════╗
║ Name Data1 Data2 Data3 Field1 field2 Field3 ║
╠═════════════════════════════════════════════╣
║ a 1 2 3 x y z ║
╚═════════════════════════════════════════════╝
I need to split that data so that it looks like this
+-----------------+
| name data field |
+-----------------+
| a 1 x |
| a 2 y |
| a 3 z |
+-----------------+
can anyone help me with this

Depending on your version of SQL Server. Starting in SQL Server 2008, you can unpivot the data using CROSS APPLY:
SELECT t.name,
x.Data,
x.Field
FROM YourTable t
CROSS APPLY
(
VALUES
(t.Data1, t.Field1),
(t.Data2, t.Field2),
(t.Data3, t.Field3)
) x (Data, Field);
See SQL Fiddle with Demo.
This can also be done using the UNPIVOT and the PIVOT function in SQL Server 2005+:
select name, data, field
from
(
select name, left(col, len(col) -1) col, value,
row_number() over(partition by left(col, len(col) -1) order by col) rn
from
(
select name,
cast([Data1] as varchar(10)) Data1,
cast([Data2] as varchar(10)) Data2,
cast([Data3] as varchar(10)) Data3,
[Field1], [field2], [Field3]
from yourtable
) src
unpivot
(
value
for col in ([Data1], [Data2], [Data3],
[Field1], [field2], [Field3])
) unpiv
) u
pivot
(
max(value)
for col in (data,field)
) piv
See SQL Fiddle with Demo
Both give the result:
| NAME | DATA | FIELD |
-----------------------
| a | 1 | x |
| a | 2 | y |
| a | 3 | z |

There are many ways to do this (just look at bluefeet's answer), but a simple way can be using UNION ALL :
SELECT [Name], Data1 AS Data, Field1 AS Field
FROM YourTable
UNION ALL
SELECT [Name], Data2 AS Data, Field2 AS Field
FROM YourTable
UNION ALL
SELECT [Name], Data3 AS Data, Field3 AS Field
FROM YourTable
And here is a fiddle with a demo for this (courtesy of bluefeet).

Related

Sort an array of strings in SQL

I have a column of strings in SQL Server 2019 that I want to sort
Select * from ID
[7235, 6784]
[3235, 2334]
[9245, 2784]
[6235, 1284]
Trying to get the result below:
[6784, 7235]
[2334, 3235]
[2784, 9245]
[1284, 6235]
Given this sample data:
CREATE TABLE dbo.ID(ID int IDENTITY(1,1), SomeCol varchar(64));
INSERT dbo.ID(SomeCol) VALUES
('[7235, 6784]'),
('[3235, 2334]'),
('[9245, 2784]'),
('[6235, 1284]');
You can run this query:
;WITH cte AS
(
SELECT ID, SomeCol,
i = TRY_CONVERT(int, value),
s = LTRIM(value)
FROM dbo.ID CROSS APPLY
STRING_SPLIT(PARSENAME(SomeCol, 1), ',') AS s
)
SELECT ID, SomeCol,
Result = QUOTENAME(STRING_AGG(s, ', ')
WITHIN GROUP (ORDER BY i))
FROM cte
GROUP BY ID, SomeCol
ORDER BY ID;
Output:
ID
SomeCol
Result
1
[7235, 6784]
[6784, 7235]
2
[3235, 2334]
[2334, 3235]
3
[9245, 2784]
[2784, 9245]
4
[6235, 1284]
[1284, 6235]
Example db<>fiddle
The source table has a column with a JSON array.
That's why it is a perfect case to handle it via SQL Server JSON API.
SQL
-- DDL and sample data population, start
DECLARE #tbl TABLE (ID int IDENTITY PRIMARY KEY, jArray NVARCHAR(100));
INSERT #tbl (jArray) VALUES
('[7235, 6784]'),
('[3235, 2334]'),
('[9245, 2784]'),
('[6235, 1284]');
-- DDL and sample data population, end
SELECT t.*
, Result = QUOTENAME(STRING_AGG(j.value, ', ')
WITHIN GROUP (ORDER BY j.value ASC))
FROM #tbl AS t
CROSS APPLY OPENJSON(t.jArray) AS j
GROUP BY t.ID, t.jArray
ORDER BY t.ID;
Output
+----+--------------+--------------+
| ID | jArray | Result |
+----+--------------+--------------+
| 1 | [7235, 6784] | [6784, 7235] |
| 2 | [3235, 2334] | [2334, 3235] |
| 3 | [9245, 2784] | [2784, 9245] |
| 4 | [6235, 1284] | [1284, 6235] |
+----+--------------+--------------+

Column to rows, rows to rows in SQL Server

I have table like this
id | vname1 | vname2 | vname3
1 | vala | valb | valc
I want this to convert like this
id | vname | vals
1 | vname1 | vala
1 | vname2 | valb
1 | vname3 | valc
I thought about pivoting but here I think is not the case
Do a UNION ALL, with one SELECT for each vname column:
select id, 'vname1' as vname, vname1 as vals from tablename
union all
select id, 'vname2' as vname, vname2 as vals from tablename
union all
select id, 'vname3' as vname, vname3 as vals from tablename
You can use the UNPIVOT function to convert the columns into rows:
Sample Example:
select Id,
indicatorname,
from yourtable
unpivot
(
indicatorvalue
for indicatorname in (Indicator1, Indicator2, Indicator3)
) unpiv;
Link for reference: UnPivot

Oracle SQL Transpose

Before I begin, I know there is a whole bunch of questions on Stackoverflow on this topic but I could not find any of them relevant to my case because they involve something much more complicated than what I need.
What I want is a simple dumb transpose with no logic involved.
Here is the original table that my select query returns:
Name Age Sex DOB Col1 Col2 Col3 ....
A 12 M 8/7 aa bb cc
Typically, this is going to contain only 1 record i.e. for one person
Now what I want is
Field Value
Name A
Age 12
Sex M
DOB 8/7
Col1 aa
Col2 bb
Col3 cc
.
.
So there is no counting, summing or any complicated logic involved like most of the similar question on Stackoverflow.
How do I do it?
I read through the PIVOT and UNPIVOT help and it was not that helpful at all.
PS: By chance, if it contains more than one records, is it possible to return each record as a field somewhat like
Field Value1 Value2 Value3 ...
Name A B C ...
Age .. .. .. ...
.
.
I want to know how to to do this for Oracle 10g and 11g
PS:Feel free to tag as duplicate if you find a question that is truly similar to mine.
I would suggest applying the UNPIVOT function first to your multiple columns, then using row_number() to create your new column names that will be used in the PIVOT.
The basic syntax for the unpivot will be
select field,
value,
'value'||
to_char(row_number() over(partition by field
order by value)) seq
from yourtable
unpivot
(
value
for field in (Name, Age, Sex, DOB, col1, col2, col3)
) u;
See SQL Fiddle with Demo. This is going to convert your multiple columns of data into multiple rows. I used row_number() to create a unique value for your new column names, the data from this query looks like:
| FIELD | VALUE | SEQ |
|-------|-------------------------|--------|
| AGE | 12 | value1 |
| AGE | 15 | value2 |
| COL1 | aa | value1 |
| COL1 | xx | value2 |
Then you can apply the PIVOT function to this result:
select field, value1, value2
from
(
select field,
value,
'value'||
to_char(row_number() over(partition by field
order by value)) seq
from yourtable
unpivot
(
value
for field in (Name, Age, Sex, DOB, col1, col2, col3)
) u
) d
pivot
(
max(value)
for seq in ('value1' as value1, 'value2' as value2)
) piv
See SQL Fiddle with Demo. This gives a final result:
| FIELD | VALUE1 | VALUE2 |
|-------|-------------------------|-------------------------|
| AGE | 12 | 15 |
| COL1 | aa | xx |
| COL2 | bb | yy |
| COL3 | cc | zz |
| DOB | 07-Aug-2001 12:00:00 AM | 26-Aug-2001 12:00:00 AM |
| NAME | A | B |
| SEX | F | M |
Note, when you are applying the unpivot function the datatype of all of the columns must be the same so you might have to convert your data in a subquery before you can unpivot it.
The UNPIVOT/PIVOT function were introduced in Oracle 11g, if you are using Oracle 10g, then you can edit the query to use:
with cte as
(
select 'name' field, name value
from yourtable
union all
select 'Age' field, Age value
from yourtable
union all
select 'Sex' field, Sex value
from yourtable
union all
select 'DOB' field, DOB value
from yourtable
union all
select 'col1' field, col1 value
from yourtable
union all
select 'col2' field, col2 value
from yourtable
union all
select 'col3' field, col3 value
from yourtable
)
select
field,
max(case when seq = 'value1' then value end) value1,
max(case when seq = 'value2' then value end) value2
from
(
select field, value,
'value'||
to_char(row_number() over(partition by field
order by value)) seq
from cte
) d
group by field;
See SQL Fiddle with Demo

How do I Pivot Vertical Data to Horizontal Data SQL with Variable Row Lengths?

Okay I have the following table.
Name ID Website
Aaron | 2305 | CoolSave1
Aaron | 8464 | DiscoWorld1
Adriana | 2956 | NewCin1
Adriana | 5991 | NewCin2
Adriana | 4563 NewCin3
I would like to transform it into the following way.
Adriana | 2956 | NewCin1 | 5991 | NewCin2 | 4563 | NewCin3
Aaron | 2305 | CoolSave1 | 8464 | DiscoWorld | NULL | NULL
As you can see i am trying to take the first name from the first table and make a single row with all the IDs / Websites associated with that name. The problem is, there is a variable amount of websites that may be associated with each name. To handle this i'd like to just make a table with with the number of fields sequal to the max line item, and then for the subsequent lineitems, plug in a NULL where there are not enough data.
In order to get the result, you will need to apply both the UNPIVOT and the PIVOT functions to the data. The UNPIVOT will take the columns (ID, website) and convert them to rows, once this is done, then you can PIVOT the data back into columns.
The UNPIVOT code will be similar to the following:
select name,
col+'_'+cast(col_num as varchar(10)) col,
value
from
(
select name,
cast(id as varchar(11)) id,
website,
row_number() over(partition by name order by id) col_num
from yt
) src
unpivot
(
value
for col in (id, website)
) unpiv;
See SQL Fiddle with Demo. This gives a result:
| NAME | COL | VALUE |
-------------------------------------
| Aaron | id_1 | 2305 |
| Aaron | website_1 | CoolSave1 |
| Aaron | id_2 | 8464 |
| Aaron | website_2 | DiscoWorld1 |
As you can see I applied a row_number() to the data prior to the unpivot, the row number is used to generate the new column names. The columns in the UNPIVOT must also be of the same datatype, I applied a cast to the id column in the subquery to convert the data to a varchar prior to the pivot.
The col values are then used in the PIVOT. Once the data has been unpivoted, you apply the PIVOT function:
select *
from
(
select name,
col+'_'+cast(col_num as varchar(10)) col,
value
from
(
select name,
cast(id as varchar(11)) id,
website,
row_number() over(partition by name order by id) col_num
from yt
) src
unpivot
(
value
for col in (id, website)
) unpiv
) d
pivot
(
max(value)
for col in (id_1, website_1, id_2, website_2, id_3, website_3)
) piv;
See SQL Fiddle with Demo.
The above version works great if you have a limited or known number of values. But if the number of rows is unknown, then you will need to use dynamic SQL to generate the result:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME( col+'_'+cast(col_num as varchar(10)))
from
(
select row_number() over(partition by name order by id) col_num
from yt
) t
cross apply
(
select 'id' col union all
select 'website'
) c
group by col, col_num
order by col_num, col
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT name,' + #cols + '
from
(
select name,
col+''_''+cast(col_num as varchar(10)) col,
value
from
(
select name,
cast(id as varchar(11)) id,
website,
row_number() over(partition by name order by id) col_num
from yt
) src
unpivot
(
value
for col in (id, website)
) unpiv
) x
pivot
(
max(value)
for col in (' + #cols + ')
) p '
execute(#query);
See SQL Fiddle with Demo. Both versions give the result:
| NAME | ID_1 | WEBSITE_1 | ID_2 | WEBSITE_2 | ID_3 | WEBSITE_3 |
------------------------------------------------------------------------
| Aaron | 2305 | CoolSave1 | 8464 | DiscoWorld1 | (null) | (null) |
| Adriana | 2956 | NewCin1 | 4563 | NewCin3 | 5991 | NewCin2 |

Return values from multiple rows as columns [duplicate]

This question already has answers here:
Oracle sql to count instances of different values in single column
(2 answers)
Closed 10 years ago.
Is it possible to return multiple row values, per same id, as a column?
If my table is:
ID | Value |Column_data
--------------------------------
1 | a | DATA1
1 | b | DATA1
2 | c | DATA2
2 | x | DATA2
3 | y | DATA3
3 | z | DATA3
(Each Id has always 2 values)
The select should return:
1,a,b,DATA1
2,c,x,DATA2
3,y,z,DATA3
You did not state what version of Oracle you are using but if you are using Oracle 11g+, then you can transform this data into columns using the PIVOT function:
select id,
C1,
C2,
column_data
from
(
select id, value, column_data,
row_number() over(partition by id order by id, value) rn
from yourtable
)
pivot
(
max(value)
for rn in ('1' as C1, '2' as C2)
)
order by id
See SQL Fiddle with Demo.
Prior to Oracle 11g, you could use an aggregate function with a CASE expression to transform the rows into columns:
select id,
max(case when rn = 1 then value end) C1,
max(case when rn = 2 then value end) C2,
column_data
from
(
select id, value, column_data,
row_number() over(partition by id order by id, value) rn
from yourtable
)
group by id, column_data
order by id
See SQL Fiddle with Demo
The result of both queries is:
| ID | C1 | C2 | COLUMN_DATA |
------------------------------
| 1 | a | b | DATA1 |
| 2 | c | x | DATA2 |
| 3 | y | z | DATA3 |
Or listagg(col2) over (...) in 11g
You could use pivot, have a look here:
http://www.oracle.com/technetwork/articles/sql/11g-pivot-097235.html
You can try something like this:
with t as ( select id, Column_data, xmlagg(xmlelement("e", Value)) xl
from table1
group by id, Column_data)
select id,
extract(xl, 'e[1]/text()').getstringval() c1,
extract(xl, 'e[2]/text()').getstringval() c2,
Column_data
from t
Here is a sqlfiddle demo