Can somebody help me with SQL using pivot?
I asked before this using numbers but not now im using it in a string format
For example I have a list of Names shown below with 1 column:
and I want the result to become this. the will limit 10 column only
use row_number() to generate a running sequence, and then, use modulus % to get the column and divide by 10 to get the row. Use PIVOT() to get the required result
select *
from
(
select [Names],
[row] = row_number() over (order by [Names]) / 10 + 1,
[col] = (row_number() over (order by [Names]) - 1) % 10 + 1
from yourtable
) d
pivot
(
max([Names])
for col in ([1], [2],[3],[4],[5],[6],[7],[8],[9],[10])
) p
I would just use conditional aggregation:
select max(case when seqnum % 10 = 0 then name end) as name_0,
max(case when seqnum % 10 = 1 then name end) as name_1,
max(case when seqnum % 10 = 2 then name end) as name_2,
max(case when seqnum % 10 = 3 then name end) as name_3,
max(case when seqnum % 10 = 4 then name end) as name_4,
max(case when seqnum % 10 = 5 then name end) as name_5,
max(case when seqnum % 10 = 6 then name end) as name_6,
max(case when seqnum % 10 = 7 then name end) as name_7,
max(case when seqnum % 10 = 8 then name end) as name_8,
max(case when seqnum % 10 = 9 then name end) as name_9
from (select t.*,
row_number() over (order by (select null)) - 1 as seqnum
from t
) t
group by floor(seqnum / 10);
Note: The ordering of the names in the result set is not guaranteed. Each name will appear in one position. If the ordering matters, then you need an additional column that specifies the ordering (and you can use that in the group by.
This code solves your question using dynamic pivot and without sorting the data with select null
declare #groups varchar(max) set #groups = (select count(0)/2 from Mytable )
declare #columns varchar(max) set #columns = ''
select #columns = coalesce(#columns + '[' + cast(col as varchar(MAX)) + '],', '')
FROM (
select col from (
select (row_number() over (order by (SELECT NULL)) - 1) % (#groups) + 1 as col
from Mytable group by Names
) t
group by col
) m
set #columns = left(#columns,LEN(#columns)-1)
DECLARE #SQLString nvarchar(max);
set #SQLString = '
select * from
(
select Names, row/'+#groups+' +1 as row , col from (
select [Names],
[row] = row_number() over (order by (SELECT NULL))+1 / '+#groups+' -1,
[col] = (row_number() over (order by (SELECT NULL)) - 1) % '+#groups+' + 1
from Mytable
) x
) m
PIVOT
( MAX(Names)
FOR col in (' + #columns + ')
) AS PVT'
EXECUTE sp_executesql #SQLString
Trying to Convert Rows to Multiple columns in SQL Server as show below:
Current result:
PortCode CarCode
------------------------
AAB A5
ADR BH
SAN QQ
Expected result:
PortCode CarCode PortCode CarCode PortCode CarCode
-----------------------------------------------------------
AAB A5 ADR BH SAN QQ
Tried withPIVOT but didn't helped.
Can anyone please explain me, how to achieve it?
If I understand correctly,
select max(case when seqnum = 1 then portcode end) as portcode_1,
max(case when seqnum = 1 then carcode end) as carcode_1,
max(case when seqnum = 2 then portcode end) as portcode_2,
max(case when seqnum = 2 then carcode end) as carcode_2,
max(case when seqnum = 3 then portcode end) as portcode_3,
max(case when seqnum = 3 then carcode end) as carcode_3
from (select t.*, row_number() over (order by (select null)) as seqnum
from t
) t;
Notes:
This is not dynamic. It produces 6 columns (but you can use the same idea for a dynamic query).
The ordering of the results is indeterminate. SQL tables represent unordered sets. If you want the columns in a particular order, then replace (select null) with the appropriate column.
If you want to make it dynamic, you can use the following sql query.
Query
declare #sql as varchar(max);
select #sql = 'select ' + stuff((
select distinct ',max(case [PortCode] when ' + char(39) + [PortCode] + char(39) +
' then [PortCode] end) as [PortCode]'
+ ',max(case [CarCode] when ' + char(39) + [CarCode] + char(39) +
' then [CarCode] end) as [CarCode]'
from [your_table_name]
for xml path('')
), 1, 1, '');
select #sql += ' from [your_table_name];';
exec(#sql);
I need to create a formatted text (CSV) output with an even number of columns from a SQL query of two tables that do not have the same number of elements. I can't seem to pad the data to the required number of columns.
Sample Data
table Students(id, name)
values
(1, Alex),
(2, Bob),
(3, Charlie),
(4, David)
table Hobbies (studentId, hobby)
values
(2,'skating')
(2,'sailing')
(3,'reading')
(4,'video games')
(4,'paintball')
(4,'nascar')
(4,'baseball')
Desired Output
[ID][OutputString]
[0][Student,Hobby1,Hobby2,Hobby3,Hobby4]
[1][Alex,,,,]
[2][Bob,skating,sailing,]
[3][Charlie,reading,,,]
[4][David,video games,paintball,nascar,baseball]
Broken PseudoCode
DECLARE #maxHobbies INT = 4;
DECLARE #hobbyCount INT = 1;
DECLARE headerString varchar(max) = 'Student';
WHILE #hobbyCount <= #maxHobbies
BEGIN
set #headerString = #headerString + ',Hobby' + #hobbyCount
#hobbyCount = #hobbyCount + 1
END
DECLARE #outputString varchar(max)
SET #outputString = concat(#headerSting, char(13)) +
SELECT 0 AS ID, #headerString AS OutputString
UNION
SELECT
id,
name + (WHILE #hobbyCount <= #maxHobbies BEGIN
(CASE
WHEN EXISTS SELECT hobby FROM Hobbies where studentId = Students.id
THEN ',' + SELECT hobby FROM Hobbies where studentId = Students.id
ELSE ','
END)
END)
FROM Students
You want up to 4 hobbies. You can do this with conditional aggregation:
select s.id,
(s.name + ',' +
max(case when seqnum = 1 then h.hobby else '' end) + ',' +
max(case when seqnum = 2 then h.hobby else '' end) + ',' +
max(case when seqnum = 3 then h.hobby else '' end) + ',' +
max(case when seqnum = 4 then h.hobby else '' end)
) as outputstring
from students s left join
(select h.*,
row_number() over (partition by h.studentid order by (select null)) as seqnum
from hobbies h
) h
on h.studentid = s.id
group by s.id, s.name;
I have some data in SQL Server like this -
Num Alphabet
1 A
1 B
2 C
2 D
2 E
3 F
Can you help me make an SQL query that will display the data like this -
Alpha1 Alpha2 Alpha3
A C F
B D
E
You need to enumerate the values before you pivot them. Here is one method for getting the results you want:
select max(case when num = 1 then alphabet end) as alpha1,
max(case when num = 2 then alphabet end) as alpha2,
max(case when num = 3 then alphabet end) as alpha3
from (select t.*, row_number() over (partition by num order by alphabet) as seqnum
from table t
) t
group by seqnum;
Try this , this will take care of any number of alphabets in your column
A dynamic Pivot Query
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
SET #cols = STUFF((SELECT distinct ',' + QUOTENAME(c.[num])
FROM [YourTable] c
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT num, ' + #cols + ' from
(
select num, Alphabet, ROW_NUMBER() over (PARTITION BY num order by Alphabet asc) as uid
from [YourTable]
) x
pivot
(
MIN(Alphabet)
for [uid] in (' + #cols + ')
) p '
execute(#query)
print #query
You can achieve this by using a subquery and group by clause:
select max(case when num = 1 then Alphabet end) as alpha1,
max(case when num = 2 then Alphabet end) as alph2,
max(case when num = 3 then Alphabet end) as alph3
from (select *, row_number()
over (partition by num order by alphabet) as output
from tblTemp) temp
group by output;
I'm sure I'm missing something here.
I have a dataset like this:
FK RowNumber Value Type Status
1 1 aaaaa A New
1 2 bbbbb B Good
1 3 ccccc A Bad
1 4 ddddd C Good
1 5 eeeee B Good
2 1 fffff C Bad
2 2 ggggg A New
2 3 hhhhh C Bad
3 1 iiiii A Good
3 2 jjjjj A Good
I'd like to query the top 3 results and Pivot them as columns, so the end result set looks like this:
FK Value1 Type1 Status1 Value2 Type2 Status2 Value3 Type3 Status3
1 aaaaa A New bbbbb B Good ccccc A Bad
2 fffff C Bad ggggg A New hhhhh C Bad
3 iiiii A Good jjjjj A Good
How can I accomplish this in SQL Server 2005?
I have been attempting this using PIVOT, but I am still very unfamiliar with that keyword and cannot get it to work the way I want.
SELECT * --Id, [1], [2], [3]
FROM
(
SELECT Id, Value, Type, Status
, ROW_NUMBER() OVER (PARTITION BY Id ORDER Status, Type) as [RowNumber]
FROM MyTable
) as T
PIVOT
(
-- I know this section doesn't work. I'm still trying to figure out PIVOT
MAX(T.Value) FOR RowNumber IN ([1], [2], [3]),
MAX(T.Type) FOR RowNumber IN ([1], [2], [3]),
MAX(T.Status) FOR RowNumber IN ([1], [2], [3])
) AS PivotTable;
My actual data set is a bit more complex than this, and I need the top 10 records, not the top 3, so I don't want to simply do CASE WHEN RowNumber = X THEN... for each one.
Update
I tested all the answers below, and found most of them seem about the same with no apparent performance difference in smaller data sets (around 3k records), however there was a slight difference when running the queries against larger data sets.
Here are the results of my tests using 80,000 records and querying for 5 columns in the top 10 rows, so my end result set was 50 columns + the Id column. I'd suggest you test them on your own to decide which one works best for you and your environment.
bluefoot's answer of unpivoting and re-pivoting the data averaged the fastest at about 12 seconds. I also liked this answer because I found it easiest to read and maintain.
Aaron's answer and koderoid's answer both suggest using a MAX(CASE WHEN RowNumber = X THEN ...), and was close behind averaging at around 13 seconds.
Rodney's answer of using multiple PIVOT statements averaged around 16 seconds, although it might be faster with fewer PIVOT statements (my tests had 5).
And the first half of Aaron's answer that suggested using a CTE and OUTER APPLY was the slowest. I don't know how long it would take to run because I cancelled it after 2 minutes, and that was with around 3k records, 3 rows, and 3 columns instead of 80k records, 10 rows, and 5 columns.
You can do an UNPIVOT and then a PIVOT of the data. this can be done either statically or dynamically:
Static Version:
select *
from
(
select fk, col + cast(rownumber as varchar(1)) new_col,
val
from
(
select fk, rownumber, value, cast(type as varchar(10)) type,
status
from yourtable
) x
unpivot
(
val
for col in (value, type, status)
) u
) x1
pivot
(
max(val)
for new_col in
([value1], [type1], [status1],
[value2], [type2], [status2],
[value3], [type3])
) p
see SQL Fiddle with demo
Dynamic Version, this will get the list of columns to unpivot and then to pivot at run-time:
DECLARE #colsUnpivot AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX),
#colsPivot as NVARCHAR(MAX)
select #colsUnpivot = stuff((select ','+quotename(C.name)
from sys.columns as C
where C.object_id = object_id('yourtable') and
C.name not in ('fk', 'rownumber')
for xml path('')), 1, 1, '')
select #colsPivot = STUFF((SELECT ','
+ quotename(c.name
+ cast(t.rownumber as varchar(10)))
from yourtable t
cross apply
sys.columns as C
where C.object_id = object_id('yourtable') and
C.name not in ('fk', 'rownumber')
group by c.name, t.rownumber
order by t.rownumber
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query
= 'select *
from
(
select fk, col + cast(rownumber as varchar(10)) new_col,
val
from
(
select fk, rownumber, value, cast(type as varchar(10)) type,
status
from yourtable
) x
unpivot
(
val
for col in ('+ #colsunpivot +')
) u
) x1
pivot
(
max(val)
for new_col in
('+ #colspivot +')
) p'
exec(#query)
see SQL Fiddle with Demo
Both will generate the same results, however the dynamic is great if you do not know the number of columns ahead of time.
The Dynamic version is working under the assumption that the rownumber is already a part of the dataset.
You can try to do the pivot in three separate pivot statements. Please give this a try:
SELECT Id
,MAX(S1) [Status 1]
,MAX(T1) [Type1]
,MAX(V1) [Value1]
--, Add other columns
FROM
(
SELECT Id, Value , Type, Status
, 'S' + CAST(ROW_NUMBER() OVER (PARTITION BY Id ORDER BY Status, Type) AS VARCHAR(10)) [Status_RowNumber]
, 'T' + CAST(ROW_NUMBER() OVER (PARTITION BY Id ORDER BY Status, Type) AS VARCHAR(10)) [Type_RowNumber]
, 'V' + CAST(ROW_NUMBER() OVER (PARTITION BY Id ORDER BY Status, Type) AS VARCHAR(10)) [Value_RowNumber]
FROM MyTable
) as T
PIVOT
(
MAX(Status) FOR Status_RowNumber IN ([S1], [S2], [S3],[S4],[S5],[S6],[S7],[S8],[S9],[S10])
)AS StatusPivot
PIVOT(
MAX(Type) FOR Type_RowNumber IN ([T1], [T2], [T3],[T4],[T5],[T6],[T7],[T8],[T9],[T10])
)AS Type_Pivot
PIVOT(
MAX(Value) FOR Value_RowNumber IN ([V1], [V2], [V3],[V4],[V5],[V6],[V7],[V8],[V9],[V10])
)AS Value_Pivot
GROUP BY Id
I don't know the full scope of the criteria for selecting the top ten records, but this produces and output that may get you closer to your answer.
SQL Fiddle Example
Rodney's muli-pivot is clever, that's for sure. Here are two other alternatives that are of course less appealing when you get into the 10X vs. 3X area.
;WITH a AS
(
SELECT Id, Value, Type, Status,
n = ROW_NUMBER() OVER (PARTITION BY Id ORDER BY [Status], [Type])
FROM dbo.MyTable
)
SELECT a.Id,
Value1 = a.Value, Type1 = a.[Type], Status1 = a.[Status],
Value2 = b.Value, Type2 = b.[Type], Status2 = b.[Status],
Value3 = c.Value, Type3 = c.[Type], Status3 = c.[Status]
FROM a
OUTER APPLY (SELECT * FROM a AS T2 WHERE n = a.n + 1 AND id = a.id) AS b
OUTER APPLY (SELECT * FROM a AS T2 WHERE n = b.n + 1 AND id = b.id) AS c
WHERE a.n = 1
ORDER BY a.Id;
-- or --
;WITH a AS
(
SELECT Id, Value, [Type], [Status],
n = ROW_NUMBER() OVER (PARTITION BY Id ORDER BY [Status], [Type])
FROM dbo.MyTable
)
SELECT Id,
Value1 = MAX(CASE WHEN n = 1 THEN Value END),
Type1 = MAX(CASE WHEN n = 1 THEN [Type] END),
Status1 = MAX(CASE WHEN n = 1 THEN [Status] END),
Value2 = MAX(CASE WHEN n = 2 THEN Value END),
Type2 = MAX(CASE WHEN n = 2 THEN [Type] END),
Status2 = MAX(CASE WHEN n = 2 THEN [Status] END),
Value3 = MAX(CASE WHEN n = 3 THEN Value END),
Type3 = MAX(CASE WHEN n = 3 THEN [Type] END),
Status3 = MAX(CASE WHEN n = 3 THEN [Status] END)
FROM a
GROUP BY Id
ORDER BY a.Id;
This might work for you, though it's not elegant.
select aa.FK_Id
, isnull(max(aa.Value1), '') as Value1
, isnull(max(aa.Type1), '') as Type1
, isnull(max(aa.Status1), '') as Status1
, isnull(max(aa.Value2), '') as Value2
, isnull(max(aa.Type2), '') as Type2
, isnull(max(aa.Status2), '') as Status2
, isnull(max(aa.Value3), '') as Value3
, isnull(max(aa.Type3), '') as Type3
, isnull(max(aa.Status3), '') as Status3
from
(
select FK_Id
, case when RowNumber = 1 then Value else null end as Value1
, case when RowNumber = 1 then [Type] else null end as Type1
, case when RowNumber = 1 then [Status] else null end as Status1
, case when RowNumber = 2 then Value else null end as Value2
, case when RowNumber = 2 then [Type] else null end as Type2
, case when RowNumber = 2 then [Status] else null end as Status2
, case when RowNumber = 3 then Value else null end as Value3
, case when RowNumber = 3 then [Type] else null end as Type3
, case when RowNumber = 3 then [Status] else null end as Status3
from Table1
) aa
group by aa.FK_Id
try something like this:
declare #rowCount int
set #rowCount = 10
declare #isNullClause varchar(4024)
set #isnullClause = ''
declare #caseClause varchar(4024)
set #caseClause = ''
declare #i int
set #i = 1
while(#i <= #rowCount) begin
set #isnullClause = #isNullClause +
' , max(aa.Value' + CAST(#i as varchar(3)) + ') as Value' + CAST(#i as varchar(3)) +
' , max(aa.Type' + CAST(#i as varchar(3)) + ') as Type' + CAST(#i as varchar(3)) +
' , max(aa.Status' + CAST(#i as varchar(3)) + ') as Status' + CAST(#i as varchar(3)) + ' ';
set #caseClause = #caseClause +
' , case when RowNumber = ' + CAST(#i as varchar(3)) + ' then Value else null end as Value' + CAST(#i as varchar(3)) +
' , case when RowNumber = ' + CAST(#i as varchar(3)) + ' then Type else null end as Type' + CAST(#i as varchar(3)) +
' , case when RowNumber = ' + CAST(#i as varchar(3)) + ' then Status else null end as Status' + CAST(#i as varchar(3)) + ' '
set #i = #i + 1;
end
declare #sql nvarchar(4000)
set #sql = 'select aa.FK_Id ' + #isnullClause + ' from ( select FK_Id '
+ #caseClause + ' from Table1) aa group by aa.FK_Id '
exec SP_EXECUTESQL #sql