I have this kind of table :
Name Date Value
-----------------------
Test 1/1/2001 10
Test 2/1/2001 17
Test 3/1/2001 52
Foo 5/4/2011 15
Foo 6/4/2011 321
My 15/5/2005 36
My 25/7/2005 75
And I would like to show the results like this :
Name Date Value Name Date Value Name Date Value
---------------------------------------------------------------------
Test 1/1/2001 10 Foo 5/4/2011 15 My 15/5/2005 36
Test 2/1/2001 17 Foo 6/4/2011 321 My 25/7/2005 75
Test 3/1/2001 52
I need to show as many columns as what is present in my Name column
How could I do this in Sql ?
In order to get the result that you want, you are going to have to unpivot the columns in your table and apply the pivot function.
The unpivot can be done using either the UNPIVOT function or you can use CROSS APPLY with VALUES.
UNPIVOT:
select rn,
col +'_'+cast(dr as varchar(10)) col,
new_values
from
(
select name,
convert(varchar(10), date, 101) date,
cast(value as varchar(10)) value,
dense_rank() over(order by name) dr,
row_number() over(partition by name order by date) rn
from yourtable
) d
unpivot
(
new_values
for col in (name, date, value)
) un;
CROSS APPLY:
select rn,
col +'_'+cast(dr as varchar(10)) col,
c.value
from
(
select name,
convert(varchar(10), date, 101) date,
cast(value as varchar(10)) value,
dense_rank() over(order by name) dr,
row_number() over(partition by name order by date) rn
from yourtable
) d
cross apply
(
values
('Name', name), ('Date', date), ('Value', Value)
) c (col, value);
See SQL Fiddle with Demo of both versions. This gives the result:
| RN | COL | NEW_VALUES |
-----------------------------
| 1 | name_1 | Foo |
| 1 | date_1 | 04/05/2011 |
| 1 | value_1 | 15 |
| 2 | name_1 | Foo |
| 2 | date_1 | 04/06/2011 |
| 2 | value_1 | 321 |
| 1 | name_2 | My |
| 1 | date_2 | 05/15/2005 |
| 1 | value_2 | 36 |
These queries take your existing columns values and converts them to rows. Once they are in rows, you create the new column names by using the windowing function dense_rank.
Once the data has been converted to rows, you then use the new column names (created with the dense_rank value) and apply the PIVOT function.
PIVOT with UNPIVOT:
select name_1, date_1, value_1,
name_2, date_2, value_2,
name_3, date_3, value_3
from
(
select rn,
col +'_'+cast(dr as varchar(10)) col,
new_values
from
(
select name,
convert(varchar(10), date, 101) date,
cast(value as varchar(10)) value,
dense_rank() over(order by name) dr,
row_number() over(partition by name order by date) rn
from yourtable
) d
unpivot
(
new_values
for col in (name, date, value)
) un
) src
pivot
(
max(new_values)
for col in (name_1, date_1, value_1,
name_2, date_2, value_2,
name_3, date_3, value_3)
) piv;
See SQL Fiddle with Demo
PIVOT with CROSS APPLY:
select name_1, date_1, value_1,
name_2, date_2, value_2,
name_3, date_3, value_3
from
(
select rn,
col +'_'+cast(dr as varchar(10)) col,
c.value
from
(
select name,
convert(varchar(10), date, 101) date,
cast(value as varchar(10)) value,
dense_rank() over(order by name) dr,
row_number() over(partition by name order by date) rn
from yourtable
) d
cross apply
(
values
('Name', name), ('Date', date), ('Value', Value)
) c (col, value)
) src
pivot
(
max(value)
for col in (name_1, date_1, value_1,
name_2, date_2, value_2,
name_3, date_3, value_3)
) piv;
See SQL Fiddle with Demo.
Dyanmic PIVOT:
The above versions will work great if you have a limited or known number of columns, if not, then you will need to use dynamic SQL:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(col +'_'+cast(dr as varchar(10)))
from
(
select dense_rank() over(order by name) dr
from yourtable
) t
cross apply
(
values(1, 'Name'), (2, 'Date'), (3, 'Value')
) c (sort, col)
group by col, dr, sort
order by dr, sort
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT ' + #cols + '
from
(
select rn,
col +''_''+cast(dr as varchar(10)) col,
c.value
from
(
select name,
convert(varchar(10), date, 101) date,
cast(value as varchar(10)) value,
dense_rank() over(order by name) dr,
row_number() over(partition by name order by date) rn
from yourtable
) d
cross apply
(
values
(''Name'', name), (''Date'', date), (''Value'', Value)
) c (col, value)
) x
pivot
(
max(value)
for col in (' + #cols + ')
) p'
execute(#query)
See SQL Fiddle with Demo.
The result for each of the queries is:
| NAME_1 | DATE_1 | VALUE_1 | NAME_2 | DATE_2 | VALUE_2 | NAME_3 | DATE_3 | VALUE_3 |
-------------------------------------------------------------------------------------------------
| Foo | 04/05/2011 | 15 | My | 05/15/2005 | 36 | Test | 01/01/2001 | 10 |
| Foo | 04/06/2011 | 321 | My | 07/25/2005 | 75 | Test | 01/02/2001 | 17 |
| (null) | (null) | (null) | (null) | (null) | (null) | Test | 01/03/2001 | 52 |
By hand or with a program. Most people are accustomed to showing output in a linear fashion (the default). If someone is asking you to do this, you can tell them that's not how the application works. You can export the result set to csv and then import that into something like Excel and reformat it by hand or use a serverside language like ASP.net or PHP to format the results into a table.
When you're parsing the output you could check the last var Name against the current. If they're different then add a column. It would still be tricky to script it because they will more than likely come out of the database in order. So you would have a sequence like test, test, test, foo, foo which would mean that you need to create a multidimensional array to organize the data to get a column count. Then setup the table based on that with a counter that counts row names, then data underneath.
I'm not sure which apps you're familiar with, so in PHP the output would look something like this from the multidimensional array.
row [1]['name']=test
row [1][test][1]['date'] = 1/1/2001
This is more of a visual output though. The databases are designed to hold data and return it in an intuitive fashion.
Related
I'm new to SQL (more precisely T-SQL) and I can't seem to wrap my head around this one. I'm sure there is a simple solution and I'm just not thinking of (maybe involving subqueries and/or a table pivot). But I was hoping one of you SQL whizzes could help out a clueless newb.
Basically, I need to turn this data:
CaseNumber|DecisionNumber|Date |Decision
----------+--------------+-----------+--------
444 |29833 |04/05/2005 |Sell
444 |29777 |05/10/2006 |Sell
444 |29654 |08/19/2007 |Buy
468 |29230 |08/19/2006 |Sell
468 |29192 |08/19/2011 |Sell
Into this result:
CaseNumber|DecisionNumber1|Date1 |Decision1|DecisionNumber2|Date2 |Decision2|DecisionNumber3|Date3 |Decision3
----------+---------------+------------+---------+---------------+------------+---------+---------------+------------+---------
444 |29833 |04/05/2005 |Sell |29777 |05/10/2006 |Sell |29654 |08/19/2007 |Buy
468 |29230 |08/19/2006 |Sell |29192 |08/19/2011 |Sell |NULL |NULL |NULL
Any ideas would be MUCH appreciated.
The problem with what you are trying to do, is that you are trying to pivot more than one column at a time. This can be done with unpivot then pivot. Something like this:
WITH CTE
AS
(
SELECT
CAST(CaseNumber AS NVARCHAR(50)) AS CaseNumber
,CAST(DecisionNumber AS NVARCHAR(50)) AS DecisionNumber
,CAST(Date AS NVARCHAR(50)) AS [Date]
,ROW_NUMBER() OVER(PARTITION BY [CaseNumber] ORDER BY DecisionNumber DESC) AS RN
FROM Table1
), unpivoted
AS
(
SELECT CaseNumber, val, col + ' ' + CAST(RN AS NVARCHAR(50)) AS col
FROM CTE
UNPIVOT
(
val
FOR col IN(DecisionNumber, Date)
) AS u
)
SELECT *
FROM unpivoted AS u
PIVOT
(
MAX(val)
FOR col IN([DecisionNumber 1], [Date 1],
[DecisionNumber 2], [Date 2],
[DecisionNumber 3], [Date 3])
) AS p;
SQL Fiddle Demo
This will give you:
| CaseNumber | DecisionNumber 1 | Date 1 | DecisionNumber 2 | Date 2 | DecisionNumber 3 | Date 3 |
|------------|------------------|------------|------------------|------------|------------------|------------|
| 444 | 29833 | 2005-04-05 | 29777 | 2006-05-10 | 29654 | 2007-08-19 |
| 468 | 29230 | 2006-08-19 | 29192 | 2011-08-19 | (null) | (null) |
However, if you want to do this for any number of decisionnumber and date, you can do this:
DECLARE #cols AS NVARCHAR(MAX);
DECLARE #query AS NVARCHAR(MAX);
WITH CTE
AS
(
SELECT
CAST(CaseNumber AS NVARCHAR(50)) AS CaseNumber
,CAST(DecisionNumber AS NVARCHAR(50)) AS DecisionNumber
,CAST(Date AS NVARCHAR(50)) AS [Date]
,ROW_NUMBER() OVER(PARTITION BY [CaseNumber] ORDER BY DecisionNumber DESC) AS RN
FROM Table1
), Data
AS
(
SELECT col, MAX(RN) AS RN
FROM
(
SELECT RN, col + CAST(RN AS NVARCHAR(50)) AS col
FROM CTE
UNPIVOT
(
val
FOR col IN(DecisionNumber, Date)
) AS u
) AS t
GROUP BY col
)
select #cols = STUFF((SELECT ',' +
QUOTENAME(col)
FROM Data
ORDER BY RN
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
, 1, 1, '');
SELECT #query = 'WITH CTE
AS
(
SELECT
CAST(CaseNumber AS NVARCHAR(50)) AS CaseNumber
,CAST(DecisionNumber AS NVARCHAR(50)) AS DecisionNumber
,CAST(Date AS NVARCHAR(50)) AS [Date]
,ROW_NUMBER() OVER(PARTITION BY [CaseNumber] ORDER BY DecisionNumber DESC) AS RN
FROM Table1
), unpivoted
AS
(
SELECT CaseNumber, val, col + CAST(RN AS NVARCHAR(50)) AS col
FROM CTE
UNPIVOT
(
val
FOR col IN(DecisionNumber, Date)
) AS u
)
SELECT *
FROM unpivoted AS u
PIVOT
(
MAX(val)
FOR col IN('+ #cols + ')
) AS p;';
EXECUTE(#query);
Dynamic SQL Demo
I am trying to use a pivot to get information in a diff format.
Here is my table:
CREATE TABLE yourtable
([case] int, [category] varchar(4))
;
INSERT INTO yourtable
([case], [category])
VALUES
(1, 'xx'),
(1, 'xyx'),
(1, 'abc'),
(2, 'ghj'),
(2, 'asdf'),
(3, 'dfgh')
;
Here is my pivot command courtesy of bluefeet:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT distinct ',' + QUOTENAME('cat'+cast(seq as
varchar(10)))
from
(
select row_number() over(partition by [case]
order by category) seq
from yourtable
) d
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT [case],' + #cols + '
from
(
SELECT [case], category,
''cat''+
cast(row_number() over(partition by [case]
order by category) as varchar(10)) seq
FROM yourTable
) x
pivot
(
max(category)
for seq in (' + #cols + ')
) p '
execute sp_executesql #query;
The output is good, it is in the format I need.
CASE CAT1 CAT2 CAT3
1 abc xx xyx
2 asdf ghj (null)
3 dfgh (null) (null)
However, I also need to add additional columns to the table. The modified table would be as follows, but I'm not sure how to add this to the QUOTENAME.
CREATE TABLE yourtable
([case] int, [category] varchar(4), [status] varchar(4))
;
INSERT INTO yourtable
([case], [category], [status])
VALUES
(1, 'xx', '00'),
(1, 'xyx', '01'),
(1, 'abc', '00'),
(2, 'ghj', '01'),
(2, 'asdf', '00'),
(3, 'dfgh', '01')
;
How can this be done? Should I add an additional QUOTENAME command? Results should be:
CASE CAT1 status1 CAT2 status2 CAT3 status3
1 abc 00 xx 00 xyx 01
2 asdf 00 ghj 01 (null) (null)
3 dfgh 01 (null) (null) (null) (null)
Since you now have two columns that you want to PIVOT, you can first unpivot the category and status columns into a single column with multiple rows.
There are a few different ways you can unpivot the data, you can use UNPIVOT or CROSS APPLY. The basic syntax will be:
select [case],
col+cast(seq as varchar(10)) seq,
value
from
(
SELECT [case], status, category,
row_number() over(partition by [case]
order by status) seq
FROM yourTable
) d
cross apply
(
select 'cat', category union all
select 'status', status
) c (col, value)
See SQL Fiddle with Demo This will convert your multiple columns of data into something that looks like this:
| CASE | SEQ | VALUE |
|------|---------|-------|
| 1 | cat1 | xx |
| 1 | status1 | 00 |
| 1 | cat2 | abc |
| 1 | status2 | 00 |
| 1 | cat3 | xyx |
| 1 | status3 | 01 |
| 2 | cat1 | asdf |
| 2 | status1 | 00 |
Once the data is in this format, then you can apply the PIVOT function to it.
SELECT [case], cat1, status1, cat2, status2, cat3, status3
FROM
(
select [case],
col+cast(seq as varchar(10)) seq,
value
from
(
SELECT [case], status, category,
row_number() over(partition by [case]
order by status) seq
FROM yourTable
) d
cross apply
(
select 'cat', category union all
select 'status', status
) c (col, value)
) x
PIVOT
(
max(value)
for seq in (cat1, status1, cat2, status2, cat3, status3)
)p;
See SQL Fiddle with Demo
Then you can convert it to dynamic SQL:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(col+cast(seq as varchar(10)))
from
(
select row_number() over(partition by [case]
order by category) seq
from yourtable
) d
cross apply
(
select 'cat', 1 union all
select 'status', 2
) c (col, so)
group by seq, col, so
order by seq, so
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT [case],' + #cols + '
from
(
select [case],
col+cast(seq as varchar(10)) seq,
value
from
(
SELECT [case], status, category,
row_number() over(partition by [case]
order by status) seq
FROM yourTable
) d
cross apply
(
select ''cat'', category union all
select ''status'', status
) c (col, value)
) x
pivot
(
max(value)
for seq in (' + #cols + ')
) p '
execute sp_executesql #query;
See SQL Fiddle with Demo The final result will be:
| CASE | CAT1 | STATUS1 | CAT2 | STATUS2 | CAT3 | STATUS3 |
|------|------|---------|--------|---------|--------|---------|
| 1 | xx | 00 | abc | 00 | xyx | 01 |
| 2 | asdf | 00 | ghj | 01 | (null) | (null) |
| 3 | dfgh | 01 | (null) | (null) | (null) | (null) |
I have data that looks as follows:
I would like to pivot this data so that there is only a single row for the SubId. The columns would be SubId, 802Lineage, 802ReadTime, 1000Lineage, 1000ReadTime etc.
If it wasn't for the requirement to have the Lineage included, this would be pretty straightforward, as follows:
Select SubId, [800] as [800Time], [1190] as [1190Time], [1605] as [1605Time]
From
(Select SubId, ProcessNumber, ReadTime From MyTable) as SourceTable
PIVOT
(
Max(ReadTime)
For ProcessNumber IN ([800],[802],[1190],[1605])
) as PivotTable;
I'm just not sure how to do this with the Lineage included. This is for SQL Server 2012
you can pivot manually:
select
SubId,
max(case when ProcessNumber = 802 then ReadTime end) as [802Time],
max(case when ProcessNumber = 802 then Lineage end) as [802Lineage],
....
from SourceTable
group by SubId
Example of joining two pivot tables, as requested in comments.
CREATE TABLE #MyTable (SubId int, ProcessNumber int, Lineage varchar(16), ReadTime datetime)
INSERT INTO #MyTable
(SubID, ProcessNumber, Lineage, ReadTime)
VALUES
(1, 9, 'A', GETDATE()),
(1, 8, 'A', GETDATE()),
(1, 7, 'B', GETDATE()),
(2, 9, 'C', GETDATE()),
(2, 8, 'C', GETDATE())
SELECT *
FROM (
Select SubId, [9] as [9Time], [8] as [8Time], [7] as [7Time]
From
(Select SubId, ProcessNumber, ReadTime From #MyTable) as SourceTable
PIVOT(Max(ReadTime) For ProcessNumber IN ([9],[8],[7],[6])) as PivotTable1
) AS T1
INNER JOIN (
Select SubId, [9] as [9Lineage], [8] as [8Lineage], [7] as [7Lineage]
From
(Select SubId, ProcessNumber, Lineage From #MyTable) as SourceTable
PIVOT(Max(Lineage) For ProcessNumber IN ([9],[8],[7],[6])) as PivotTable1
) AS T2
ON T1.SubId = T2.SubId
GO
You can use the PIVOT function to get the result but you will have to unpivot the Lineage and ReadTime columns from the multiple columns into multiple rows.
Since you are using SQL Server 2012 you can unpivot the data using CROSS APPLY with VALUES:
select subid,
colname = cast(processNumber as varchar(10)) + colname,
value
from mytable
cross apply
(
values
('Lineage', Lineage),
('ReadTime', convert(varchar(20), readtime, 120))
) c (colname, value)
See SQL Fiddle with Demo. This will convert your current data into the format:
| SUBID | COLNAME | VALUE |
|-------------|--------------|---------------------|
| 12010231146 | 802Lineage | PBG12A |
| 12010231146 | 802ReadTime | 2012-01-02 21:44:00 |
| 12010231146 | 1000Lineage | PBG12A |
| 12010231146 | 1000ReadTime | 2012-01-02 21:43:00 |
| 12010231146 | 1190Lineage | PBG11B |
| 12010231146 | 1190ReadTime | 2012-01-03 14:36:00 |
Once the data is in this format, then you can easily apply the PIVOT function to get your final result:
select *
from
(
select subid,
colname = cast(processNumber as varchar(10)) + colname,
value
from mytable
cross apply
(
values
('Lineage', Lineage),
('ReadTime', convert(varchar(20), readtime, 120))
) c (colname, value)
) d
pivot
(
max(value)
for colname in ([802Lineage], [802ReadTime],
[1000Lineage], [1000ReadTime],
[1190Lineage], [1190ReadTime])
) piv;
See SQL Fiddle with Demo.
The above works great if you have a limited number of rows that you want to convert, but if you have an unknown number then you can use dynamic SQL:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(cast(processnumber as varchar(10))+col)
from mytable
cross apply
(
select 'Lineage', 0 union all
select 'ReadTime', 1
) c (col, so)
group by processnumber, col, so
order by processnumber, so
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT subid, ' + #cols + '
from
(
select subid,
colname = cast(processNumber as varchar(10)) + colname,
value
from mytable
cross apply
(
values
(''Lineage'', Lineage),
(''ReadTime'', convert(varchar(20), readtime, 120))
) c (colname, value)
) x
pivot
(
max(value)
for colname in (' + #cols + ')
) p '
execute sp_executesql #query;
See SQL Fiddle with Demo. This gives a result:
| SUBID | 802LINEAGE | 802READTIME | 1000LINEAGE | 1000READTIME | 1190LINEAGE | 1190READTIME | 1605LINEAGE | 1605READTIME | 1745LINEAGE | 1745READTIME | 1790LINEAGE | 1790READTIME | 1990LINEAGE | 1990READTIME | 2690LINEAGE | 2690READTIME | 2795LINEAGE | 2795READTIME | 2990LINEAGE | 2990READTIME | 3090LINEAGE | 3090READTIME | 3290LINEAGE | 3290READTIME |
|-------------|------------|---------------------|-------------|---------------------|-------------|---------------------|-------------|---------------------|-------------|---------------------|-------------|---------------------|-------------|---------------------|-------------|---------------------|-------------|---------------------|-------------|---------------------|-------------|---------------------|-------------|---------------------|
| 12010231146 | PBG12A | 2012-01-02 21:44:00 | PBG12A | 2012-01-02 21:43:00 | PBG11B | 2012-01-03 14:36:00 | PBG11B | 2012-01-03 15:15:00 | PBG11A | 2012-01-03 15:16:00 | PBG11A | 2012-01-03 15:19:00 | PBG11A | 2012-01-03 15:23:00 | PBG11A | 2012-01-03 15:32:00 | PBG11A | 2012-01-03 15:39:00 | PBG11A | 2012-01-03 15:41:00 | PBG11A | 2012-01-03 15:46:00 | PBG11A | 2012-01-03 15:47:00 |
I have data in a SQLServer table like this:
ID Name Year Value
-- ---- ---- -----
2 Ted 2013 2000
2 Ted 2012 1000
I need the view syntax to output this:
ID Name Yr1 Value1 Yr2 Value2
-- ---- --- ------ --- ------
2 Ted 2013 2000 2012 1000
No cursors if possible.
Any clues would be greatful.
In SQL Server there are several ways that you can get the result.
If you have a limited number of values, then you can easily hard-code the result. One way you can get the result would be using an aggregate function with a CASE expression:
select d.id,
d.name,
max(case when seq = 1 then year end) year1,
max(case when seq = 1 then value end) value1,
max(case when seq = 2 then year end) year2,
max(case when seq = 2 then value end) value2
from
(
select id, name, year, value,
row_number() over(partition by id order by year desc) seq
from yourtable
) d
group by d.id, d.name;
See SQL Fiddle with Demo. If you want to use the PIVOT function, then I would suggest first unpivoting the data in the year and value columns first. The process of unpivot converts the multiple columns into multiple rows. You can use the UNPIVOT function, but in my example I used CROSS APPLY with a UNION ALL query and the code is:
select t.id, t.name,
col = c.col+cast(seq as varchar(4)),
c.val
from
(
select id, name, year, value,
row_number() over(partition by id order by year desc) seq
from yourtable
) t
cross apply
(
select 'year', t.year union all
select 'value', t.value
) c (col, val)
See SQL Fiddle with Demo. This converts your multiple columns into a slightly different format with multiple rows:
| ID | NAME | COL | VAL |
| 2 | Ted | year1 | 2013 |
| 2 | Ted | value1 | 2000 |
| 2 | Ted | year2 | 2012 |
| 2 | Ted | value2 | 1000 |
You can then apply the PIVOT function on this to get your final desired result:
select id, name, year1, value1, year2, value2
from
(
select t.id, t.name,
col = c.col+cast(seq as varchar(4)),
c.val
from
(
select id, name, year, value,
row_number() over(partition by id order by year desc) seq
from yourtable
) t
cross apply
(
select 'year', t.year union all
select 'value', t.value
) c (col, val)
) d
pivot
(
max(val)
for col in (year1, value1, year2, value2)
) piv;
See SQL Fiddle with Demo. Finally if you have an unknown number of values that you want to transform from rows into columns, then you can use dynamic SQL inside a stored procedure:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(col+cast(seq as varchar(4)))
from
(
select row_number() over(partition by id order by year desc) seq
from yourtable
) d
cross apply
(
select 'year', 1 union all
select 'value', 2
) c (col, so)
group by seq, col, so
order by seq, so
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT id, name,' + #cols + '
from
(
select t.id, t.name,
col = c.col+cast(seq as varchar(4)),
c.val
from
(
select id, name, year, value,
row_number() over(partition by id order by year desc) seq
from yourtable
) t
cross apply
(
select ''year'', t.year union all
select ''value'', t.value
) c (col, val)
) x
pivot
(
max(val)
for col in (' + #cols + ')
) p '
execute sp_executesql #query;
See SQL Fiddle with Demo. All versions will give a result:
| ID | NAME | YEAR1 | VALUE1 | YEAR2 | VALUE2 |
| 2 | Ted | 2013 | 2000 | 2012 | 1000 |
I have a table customer in the database as below.
ID UID Address1 Name code
10 5 A Jac 683501
11 5 B Joe 727272
13 6 C mat 373737
first two records (10,11) have a common uID -5 . These two records can be considered as a single unit.
fourth record is having a separate UID , so it is a separate unit
I need to produce an output in a csv file such a way that
ID UID Name code Address1 Name2 Code2
10 5 jac 683501 A Joe 727272
13 6 mat 373737 C
Name2 and code2 values are from the second row, since UID is same for first two records, we can considered it as a single unit.
Can anyone give hints to query for generating these records.
This process to transform data from rows into columns is known as a PIVOT. There are several ways that this can be done.
You can use a row_number() along with an aggregate function with a CASE expression:
select min(id),
uid,
max(case when seq = 1 then name end) Name,
max(case when seq = 1 then code end) Code,
max(case when seq = 1 then Address1 end) Address1,
max(case when seq = 2 then name end) Name2,
max(case when seq = 2 then code end) code2,
max(case when seq = 2 then Address1 end) Address1_2
from
(
select id, uid, address1, name, code,
row_number() over(partition by uid order by id) seq
from yourtable
) d
group by uid;
See SQL Fiddle with Demo.
You could use both the UNPIVOT and the PIVOT function:
select id, uid,
name1, code1, address1, name2, code2, address2
from
(
select id, uid, col+cast(seq as varchar(10)) col, value
from
(
select
(select min(id)
from yourtable t2
where t.uid = t2.uid) id,
uid,
cast(address1 as varchar(20)) address,
cast(name as varchar(20)) name,
cast(code as varchar(20)) code,
row_number() over(partition by uid order by id) seq
from yourtable t
) d
unpivot
(
value
for col in (address, name, code)
) unpiv
) src
pivot
(
max(value)
for col in (name1, code1, address1, name2, code2, address2)
) piv;
See SQL Fiddle with Demo.
Finally if you have an unknown number of values for each uid, then you can use dynamic SQL to get the result:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(col+(cast(seq as varchar(10))))
from
(
select row_number() over(partition by uid order by id) seq
from yourtable
) d
cross apply
(
select 'name', 1 union all
select 'code', 2 union all
select 'address', 3
) c (col, so)
group by seq, col, so
order by seq, so
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT id, uid,' + #cols + '
from
(
select id, uid, col+cast(seq as varchar(10)) col, value
from
(
select
(select min(id)
from yourtable t2
where t.uid = t2.uid) id,
uid,
cast(address1 as varchar(20)) address,
cast(name as varchar(20)) name,
cast(code as varchar(20)) code,
row_number() over(partition by uid order by id) seq
from yourtable t
) d
unpivot
(
value
for col in (address, name, code)
) unpiv
) x
pivot
(
max(value)
for col in (' + #cols + ')
) p '
execute(#query);
See SQL Fiddle with Demo. All versions will give a result:
| ID | UID | NAME1 | CODE1 | ADDRESS1 | NAME2 | CODE2 | ADDRESS2 |
---------------------------------------------------------------------
| 10 | 5 | Jac | 683501 | A | Joe | 727272 | B |
| 13 | 6 | mat | 373737 | C | (null) | (null) | (null) |
Try inner join.
select u2.firstname,u2.col2,
from users u
inner join users u2 on u.userid=u2.userid
where u.firstname=u2.lastname