Grouping by and getting multiple rows to one cell separated by commas - sql

I know that title is ambigious and I know that there are similiar questions here. I looked and could not made anything work exactly as I need.
I need to Group By ReportNumber and those sharing one category add to one cell.
My DB:
ID | CategorySymbol | ReportNumber | NumberInCategory
1 A 31 101
2 B 31 107
3 C 31 121
4 A 32 191
5 A 33 165
6 B 32 156
7 C 32 127
8 A 31 166
9 B 31 177
Desired result:
ReporNumber | CategoryA | CategoryB | CategoryC
31 **101,166** **107,177** 121
32 191 156 127
33 165 NULL NULL
One of many different attempts:
select ReportNumber,
CategoryFirst, CategorySecond, CategoryThird, CategoryFourth
from
(
select NumberInCategory, Report.value('(/Report/#ReportNumber)[1]', 'nvarchar(max)') AS ReportNumber,
'Category' + cast(CategorySymbol as varchar(10)) CategorySymbol
from dbo.ReportDB t
) d
pivot
(
max(NumberInCategory)
for CategorySymbol in (CategoryFirst, CategorySecond, CategoryThird, CategoryFourth)
) piv;
My result:
ReporNumber | CategoryA | CategoryB | CategoryC
31 **166** **177** 121
32 191 156 127
33 165 NULL NULL
It is obvious why the result is how it is - max(NumberInCategory). The only question is how do I get to make a query that selects Number based on the CategorySymbol in for loop. I tried doing funtions that return one result like STUFF or simple SELECT but couldn't do them properly. It does not let me replace max(NumberInCategory).
E.g. something like that:
STUFF((SELECT ', ' + CAST(NumberInCategory AS VARCHAR(10)) [text()], Report.value('(/Report/#ReportNumber)[1]', 'nvarchar(max)') AS ReportNumber
FROM ReportDB
WHERE ReportNumber = t.ReportNumber
FOR XML PATH(''), TYPE)
.value('.','NVARCHAR(MAX)'),1,2,' ') List_Output

Using the stuff() with select ... for xml path ('') method of string concatenation before pivot():
;with t as (
select
NumberInCategory
, Report.value('(/Report/#ReportNumber)[1]', 'nvarchar(max)') AS ReportNumber
, CategorySymbol
from dbo.ReportDB
)
select ReportNumber, CategoryA, CategoryB, CategoryC
from (
select
t.ReportNumber
, CategorySymbol = 'Category'+convert(varchar(10),t.CategorySymbol)
, NumberInCategory = stuff((
select ', '+convert(varchar(13),i.NumberInCategory)
from t i
where i.ReportNumber = t.ReportNumber
and i.CategorySymbol = t.CategorySymbol
order by i.NumberInCategory
for xml path (''), type).value('(./text())[1]','nvarchar(max)')
,1,2,'')
from t
group by t.ReportNumber, t.CategorySymbol
) s
pivot (max(NumberInCategory)
for CategorySymbol in (CategoryA, CategoryB, CategoryC)
) piv;
rextester demo: http://rextester.com/OSAZ69656
returns:
+--------------+-----------+-----------+-----------+
| ReportNumber | CategoryA | CategoryB | CategoryC |
+--------------+-----------+-----------+-----------+
| 31 | 101, 166 | 107, 177 | 121 |
| 32 | 191 | 156 | 127 |
| 33 | 165 | NULL | NULL |
+--------------+-----------+-----------+-----------+

Here's a slightly different flavor...
IF OBJECT_ID('tempdb..#TestData', 'U') IS NOT NULL
DROP TABLE #TestData;
CREATE TABLE #TestData (
ID INT NOT NULL PRIMARY KEY CLUSTERED,
CategorySymbol CHAR(1) NOT NULL,
ReportNumber int NOT NULL,
NumberInCategory INT NOT NULL
);
INSERT #TestData(ID, CategorySymbol, ReportNumber, NumberInCategory) VALUES
(1, 'A', 31, 101),
(2, 'B', 31, 107),
(3, 'C', 31, 121),
(4, 'A', 32, 191),
(5, 'A', 33, 165),
(6, 'B', 32, 156),
(7, 'C', 32, 127),
(8, 'A', 31, 166),
(9, 'B', 31, 177);
-- SELECT * FROM #TestData td;
--==================================================================================
SELECT
td1.ReportNumber,
CategoryA = MAX(CASE WHEN td1.CategorySymbol = 'A' THEN STUFF(c.CSV, 1, 1, '') END),
CategoryB = MAX(CASE WHEN td1.CategorySymbol = 'B' THEN STUFF(c.CSV, 1, 1, '') END),
CategoryC = MAX(CASE WHEN td1.CategorySymbol = 'C' THEN STUFF(c.CSV, 1, 1, '') END)
FROM
#TestData td1
CROSS APPLY (
(SELECT
CONCAT(',', td2.NumberInCategory)
FROM
#TestData td2
WHERE
td1.CategorySymbol = td2.CategorySymbol
AND td1.ReportNumber = td2.ReportNumber
FOR XML PATH(''))
) c (CSV)
GROUP BY
td1.ReportNumber;
Results...
ReportNumber CategoryA CategoryB CategoryC
------------ ---------- ---------- ----------
31 101,166 107,177 121
32 191 156 127
33 165 NULL NULL

Related

Join on existing aggregate query to pivot result without id

On a Sql-Server instance, I have three tables:
ActionItem
Id
Name
1
Fish
2
Gravy
3
Pants
ActionData
Id
ActionId
Group
Field
Value
1
1
1
1
100
2
1
1
2
200
3
1
1
3
300
4
1
1
4
NULL
5
1
1
5
NULL
6
1
2
6
"Some Text"
7
2
1
1
50
8
2
1
2
60
9
2
1
3
70
Costing
Id
ActionId
Break
Cost
1
1
Normal
11.3
2
1
Sub
54
3
1
Premium
0.4
4
3
Normal
22
5
3
Premium
0.67
I have a query that sums the cost for each ActionItem:
select
ai.Id,
ai.Name,
sum(c.Cost)
from ActionItem ai
left join Costing c on ai.Id = c.ActionId
group by
ai.Id,
ai.Name
Nice and straight-forward:
Id
Name
(No column name)
1
Fish
65.7
2
Gravy
NULL
3
Pants
22.67
I created a pivot too:
select * from
(select [ActionId], [Group], [Field], [Value] from ActionData) src
pivot (max([Value]) for [ActionId] in ([1],[2],[3],[4])) ppp
Which gets me data in the right format:
Group
Field
1
2
3
4
1
1
100
50
NULL
NULL
1
2
200
60
NULL
NULL
1
3
300
70
NULL
NULL
1
4
NULL
NULL
NULL
NULL
1
5
NULL
NULL
NULL
NULL
2
6
"Some Text"
NULL
NULL
NULL
But I cannot join these two queries together because that PIVOT doesn't contain the ActionId ... even though I use Select * from - how can I get the ActionId col to show on my pivoted data, so I can join it to the rest of my original query?
I could not get sqlfiddle.com to work for MS SQL SERVER today but here are create and inserts if anyone's interested:
CREATE TABLE ActionItem
([Id] int, [Name] varchar(5));
INSERT INTO ActionItem
([Id], [Name])
VALUES
(1, 'Fish'),
(2, 'Gravy'),
(3, 'Pants');
CREATE TABLE ActionData
([Id] int, [ActionId] int, [Group] int, [Field] int, [Value] varchar(11));
INSERT INTO ActionData
([Id], [ActionId], [Group], [Field], [Value])
VALUES
(1, 1, 1, 1, '100'),
(2, 1, 1, 2, '200'),
(3, 1, 1, 3, '300'),
(4, 1, 1, 4, NULL),
(5, 1, 1, 5, NULL),
(6, 1, 2, 6, '"Some Text"'),
(7, 2, 1, 1, '50'),
(8, 2, 1, 2, '60'),
(9, 2, 1, 3, '70')
;
CREATE TABLE Costing (
[Id] int,
[ActionId] int,
[Break] VARCHAR(9),
[Cost] FLOAT);
INSERT INTO Costing
([Id], [ActionId], [Break], [Cost])
VALUES
('1', '1', 'Normal', '11.3'),
('2', '1', 'Sub', '54'),
('3', '1', 'Premium', '0.4'),
('4', '3', 'Normal', '22'),
('5', '3', 'Premium', '0.67');
Not sure what output you expect.
But here's an attempt to join the two queries in 1 pivot.
select pvt.*
from
(
select d.ActionId, ai.Name
--, d.[Group]
, cast(d.[Field] as varchar(30)) as [Col]
, try_cast(d.[Value] as float) as [Value]
from ActionData d
left join ActionItem ai on ai.Id = d.ActionId
where isnumeric(d.[Value]) = 1
union all
select c.ActionId, ai.Name
--, 1 as [Group]
, c.[Break] as [Col]
, sum(c.Cost) as TotalCost
from Costing c
left join ActionItem ai
on ai.Id = c.ActionId
group by c.ActionId, ai.Name, c.[Break]
) src
pivot (
max([Value])
for [Col] in ([1],[2],[3],[4],[Normal],[Premium],[Sub])
) pvt
GO
ActionId | Name | 1 | 2 | 3 | 4 | Normal | Premium | Sub
-------: | :---- | ---: | ---: | ---: | ---: | -----: | ------: | ---:
1 | Fish | 100 | 200 | 300 | null | 11.3 | 0.4 | 54
2 | Gravy | 50 | 60 | 70 | null | null | null | null
3 | Pants | null | null | null | null | 22 | 0.67 | null
db<>fiddle here

Split space separated values

I have many records like this
ID Tag Arr
1 A 87 34 92
2 A 34 35 38
3 A 39 88 92
4 B 24 49 39
5 B 38 88 23
6 C 39 37 99
I want the end result to look like this in a fast way
Tag Arr No
A 87 1
A 34 2
A 92 2
A 35 1
A 38 1
A 39 1
A 88 1
B 24 1
B 49 1
B 39 1
B 38 1
B 88 1
B 23 1
C 39 1
C 37 1
C 99 1
This is the query i built so far
SELECT DISTINCT T2.tag,
SUBSTRING(
(
SELECT ','+T1.Arr AS [text()]
FROM Tags T1
WHERE T1.tag = T2.tag
and filename = 1
and tag not in ('U')
ORDER BY T1.tag
FOR XML PATH ('')
), 2, 1000) [Ts]
FROM Tags T2
WHERE filename = 1
and tag not in ('U')
You can try this using STRING_SPLIT (Transact-SQL)
which works for SQL Server 2016 and later.
create table tblSampleValue(Id int, Tag Varchar(10), Arr Varchar(50))
insert into tblSampleValue Values
(1, 'A', '87 34 92'),
(2, 'A', '34 35 38'),
(3, 'A', '39 88 92'),
(4, 'B', '24 49 39'),
(5, 'B', '38 88 23'),
(6, 'C', '39 37 99')
Select * from tblSampleValue
select id
, Tag
, [value] as Arr
from tblSampleValue
cross apply string_split(Arr,' ')
Live db<>fiddle demo.
First, you need to use Split function to split Arr column with space.
CREATE FUNCTION udf_Split
( #Words nvarchar(MAX)
, #splitStr varchar(50)
)
RETURNS #Result_Table TABLE
(
[word] nvarchar(max) NULL
)
BEGIN
Declare #TempStr nvarchar(MAX)
WHILE (CHARINDEX(#splitStr,#Words)>0)
BEGIN
Set #TempStr=SUBSTRING(#Words,1,CHARINDEX(#splitStr,#Words)-1)
Insert into #Result_Table (word) Values (#TempStr)
Set #Words = REPLACE(#Words,#TempStr+#splitStr,'')
END/*End While*/
IF(LEN(RTRIM(LTRIM(#Words)))>0 And CHARINDEX(#splitStr,RTRIM(LTRIM(#Words)))=0)
Begin
Set #TempStr=#Words
Insert into #Result_Table (word) Values (#TempStr)
End /*End IF*/
RETURN
END
then use CROSS APPLY with Arr column
SELECT Tag,
word,
count(*) [no]
FROM Tags CROSS APPLY udf_Split(Arr,' ') v
GROUP BY Tag,
word
ORDER BY Tag
sqlfiddle
Note:
if your SQL server version higher than 2016 there is an official function you can use STRING_SPLIT

How to build up a sparse output from given records in SQL Server statements?

Given some records
declare #t table
(
idx varchar(10),
class varchar(10),
head varchar(10),
qty VARCHAR(10)
)
insert #t (idx, class, head, qty)
values ('row1', 'H1', 'C1', 1), ('row1', 'H1', 'C2', 2),
('row1', 'H1', 'C3', 3), ('row2', 'H2', 'D1', 2),
('row2', 'H2', 'D2', 3), ('row2', 'H2', 'D3', 4),
('row3', 'H1', 'C2', 8), ('row3', 'H2', 'D2', 9),
('row3', 'H2', 'D3', 10), ('row4', '', '', ''),
('row5', 'H2', 'D2', 10), ('row5', 'H2', 'D3', 11),
('row5', 'H3', 'E1', 12), ('row6', '','','')
SELECT * FROM #t
OUTPUT:
idx memo class head qty
--------------------------------
row1 ida H1 C1 1
row1 ida H1 C2 2
row1 ida H1 C3 3
row2 id H2 D1 2
row2 id H2 D2 3
row2 id H2 D3 4
row3 id H1 C2 8
row3 id H2 D2 9
row3 id H2 D3 10
row4 ida
row5 idf H2 D2 10
row5 idf H2 D3 11
row5 idf H3 E1 12
row6 id
How to make a spare array output like below efficiently? ZEROs could be replaced by blank string. A similar question and answer in Mathematica could be found here.https://mathematica.stackexchange.com/questions/186835/building-a-sparse-array-from-given-lists-the-2nd-case
"arrays" are not something SQL is generally known for although Postgres does have array features.
In T-SQL you can "pivot" you table, like this:
SELECT idx, [C1],[C2],[C3],[D1],[D2],[D3],[E1]
FROM (
SELECT
idx, head, qty
FROM #t
) sourcedata
pivot
(
max([qty])
FOR [head] IN ([C1],[C2],[C3],[D1],[D2],[D3],[E1])
) p
order by idx
which will produce this:
+----+------+------+------+------+------+------+------+------+
| | idx | C1 | C2 | C3 | D1 | D2 | D3 | E1 |
+----+------+------+------+------+------+------+------+------+
| 1 | row1 | 1 | 2 | 3 | NULL | NULL | NULL | NULL |
| 2 | row2 | NULL | NULL | NULL | 2 | 3 | 4 | NULL |
| 3 | row3 | NULL | 8 | NULL | NULL | 9 | 10 | NULL |
| 4 | row4 | NULL | NULL | NULL | NULL | NULL | NULL | NULL |
| 5 | row5 | NULL | NULL | NULL | NULL | 10 | 11 | 12 |
| 6 | row6 | NULL | NULL | NULL | NULL | NULL | NULL | NULL |
+----+------+------+------+------+------+------+------+------+
and you can even generate the pivot query if needed:
DECLARE #cols AS NVARCHAR(MAX)
DECLARE #query AS NVARCHAR(MAX)
SET #cols = STUFF((SELECT DISTINCT ',' + QUOTENAME(head)
FROM some_table s
where head is not null and head <> ''
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
SET #query = 'SELECT idx, ' + #cols + '
FROM (
SELECT
idx, head, qty
FROM some_table
) sourcedata
pivot
(
max([qty])
FOR [idx] IN (' + #cols + ')
) p
order by idx '
select #query -- use select to inspect the generated sql
--execute(#query) -- once satisfied that sql is OK, use execute
but you can't use #t as the data source when executing the #query.
I have no idea if this really helps because as there no columnar references back to Classes
EDIT
To replace NULLs in the final output requires changing the first query seen above to this:
SELECT
idx
, COALESCE( CAST( [C1] AS varchar ), '' ) -- converted into STRINGS
, COALESCE( CAST( [C2] AS varchar ), '' )
, COALESCE( CAST( [C3] AS varchar ), '' )
, COALESCE( CAST( [D1] AS varchar ), '' )
, COALESCE( CAST( [D2] AS varchar ), '' )
, COALESCE( CAST( [D3] AS varchar ), '' )
, COALESCE( CAST( [E1] AS varchar ), '' )
FROM (
SELECT
idx
, head
, qty
FROM #t
) sourcedata
PIVOT
(
MAX( [qty] )
FOR [head] IN ([C1], [C2], [C3], [D1], [D2], [D3], [E1])
) p
ORDER BY
idx

T-SQL results in to columns

I have a table (t1) like below
Id Name RelId
1 a 2
2 b 3
3 c 4
4 d 3
5 e 6
The other table (t2)
Id data FK Order
1 aa 2 2
2 bb 2 3
3 cc 2 1
4 dd 2 4
5 ee 2 5
6 ff 3 3
7 gg 3 2
8 hh 3 1
9 ii 4 7
10 jj 4 4
11 kk 4 1
12 ll 4 3
13 mm 6 1
14 nn 6 2
15 oo 6 3
16 pp 6 4
My output result am looking for is
+----+------+-------+-------+------+----------+
| id | name | RelId | Col 1 | Col2 | Col-Oth |
+----+------+-------+-------+------+----------+
| 1 | a | 2 | cc | aa | bb,dd,ee |
| 2 | b | 3 | hh | gg | ff |
| 3 | c | 4 | kk | ll | jj,ii |
| 4 | d | 3 | hh | gg | ff |
| 5 | e | 6 | mm | nn | oo,pp |
+----+------+-------+-------+------+----------+
based on the Relid in T1 table join with FK column in T2 and populate col1 with the least order data, col2 with the next higher order data and col-oth with remaining data comma separated ordered.
Need your help on same.
SELECT id,name,RelId, (select data,rownumber() (partition by data order by order asc) from t2 inner join t1 on t1.relid= t2.FK) from t1
Try following query:
DECLARE #TEMP TABLE
(
Id INT,
Name VARCHAR(10),
RelId INT
)
INSERT INTO #TEMP VALUES (1,'a',2),(2,'b',3),(3,'c',4),(4,'d',3),(5,'e',6)
DECLARE #TEMP1 TABLE
(
Id INT,
Data varchar(10),
FK INT,
[order] INT
)
INSERT INTO #TEMP1 VALUES
(1 ,'aa',2,2),(2 ,'bb',2,3),(3 ,'cc',2,1),(4 ,'dd',2,4),(5 ,'ee',2,5),
(6 ,'ff',3,3),(7 ,'gg',3,2),(8 ,'hh',3,1),(9 ,'ii',4,7),(10,'jj',4,4),
(11,'kk',4,1),(12,'ll',4,3),(13,'mm',6,1),(14,'nn',6,2),(15,'oo',6,3),(16,'pp',6,4)
SELECT
t1.*,
(SELECT Data FROM (SELECT ROW_NUMBER() OVER(ORDER BY t2.[order]) As RowNo,Data FROM #TEMP1 t2 WHERE t2.FK = t1.RelId)t3 WHERE t3.RowNo=1),
(SELECT Data FROM (SELECT ROW_NUMBER() OVER(ORDER BY t2.[order]) As RowNo,Data FROM #TEMP1 t2 WHERE t2.FK = t1.RelId)t3 WHERE t3.RowNo=2),
STUFF((SELECT DISTINCT ',' + Data FROM (SELECT ROW_NUMBER() OVER(ORDER BY t2.[order]) As RowNo,Data FROM #TEMP1 t2 WHERE t2.FK = t1.RelId)t3 WHERE t3.RowNo > 2 FOR XML PATH ('')), 1, 1, '')
FROM
#TEMP t1
Using PIVOT:
DECLARE #t1 TABLE
(
ID INT ,
Name CHAR(1) ,
RelID INT
)
DECLARE #t2 TABLE
(
ID INT ,
Data CHAR(2) ,
RelID INT ,
Ordering INT
)
INSERT INTO #t1
VALUES ( 1, 'a', 2 ),
( 2, 'b', 3 ),
( 3, 'c', 4 ),
( 4, 'd', 3 ),
( 5, 'e', 6 )
INSERT INTO #t2
VALUES ( 1, 'aa', 2, 2 ),
( 2, 'bb', 2, 3 ),
( 3, 'cc', 2, 1 ),
( 4, 'dd', 2, 4 ),
( 5, 'ee', 2, 5 ),
( 6, 'ff', 3, 3 ),
( 7, 'gg', 3, 2 ),
( 8, 'hh', 3, 1 ),
( 9, 'ii', 4, 7 ),
( 10, 'jj', 4, 4 ),
( 11, 'kk', 4, 1 ),
( 12, 'll', 4, 3 ),
( 13, 'mm', 6, 1 ),
( 14, 'nn', 6, 2 ),
( 15, 'oo', 6, 3 ),
( 16, 'pp', 6, 4 );
WITH cte1
AS ( SELECT t1.ID ,
t1.Name ,
t1.RelID ,
t2.Data ,
ROW_NUMBER() OVER ( PARTITION BY t1.ID ORDER BY t2.Ordering ) AS rn
FROM #t1 t1
JOIN #t2 t2 ON t1.RelID = t2.RelID
),
cte2
AS ( SELECT ID ,
Name ,
RelID ,
Data ,
rn ,
STUFF(( SELECT ',' + Data
FROM cte1 ci
WHERE co.ID = ci.ID
AND rn > 2
FOR
XML PATH('')
), 1, 1, '') AS Col3
FROM cte1 co
)
SELECT ID ,
Name ,
RelID ,
[1] AS Col1 ,
[2] AS Col2 ,
Col3
FROM cte2 PIVOT( MAX(data) FOR rn IN ( [1], [2] ) ) p
Output:
ID Name RelID Col1 Col2 Col3
1 a 2 cc aa bb,dd,ee
2 b 3 hh gg ff
3 c 4 kk ll jj,ii
4 d 3 hh gg ff
5 e 6 mm nn oo,pp
Execution plan of my statement
Execution plan of accepted statement:
Which is better? :)

sql server group columns as rows (pivot?)

I've got this result data in SQL Server 2008 R2
id Size Acted Sum Avg1 Avg2 A1 A2 A3
1 3921 39 690 17.69 0.18 NULL NULL NULL
40 11979 301 5944.26 19.75 0.5 10000.00 2000.00 1000.00
41 11714 289 5060 17.51 0.43 10000.00 3000.00 2000.00
42 11599 265 4107.98 15.5 0.35 10000.00 5000.00 500.00
And I would like to move the columns into rows according to the id so I will recieve this result:
id1 id40 id41 id42
1 40 41 42
3921 11979 11714 11599
39 301 289 265
690 5944 5060 4107
17.69 19.75 17.51 15.5
0.18 0.5 0.43 0.35
10000.00 2000.00 1000.00
10000.00 3000.00 2000.00
10000.00 5000.00 500.00
Is there a way to do that?
I tried pivot but as far as I tried I could only transform 1 column and not many as needed in this case.
In order to get this result, you will want to first unpivot the data from the columns to rows, and then apply the PIVOT function.
Since you are using SQL Server 2008, you can use CROSS APPLY and VALUES to unpivot the data. This takes the values from your numerous columns and converts them to rows:
select 'id'+cast(t.id as varchar(10)) p_id,
c.col,
c.value,
c.sort_order
from yourtable t
cross apply
(
values
(1, 'id', id),
(2, 'size', size),
(3, 'acted', acted),
(4, 'sum', sum),
(5, 'avg1', avg1),
(6, 'avg2', avg2),
(7, 'a1', a1),
(8, 'a2', a2),
(9, 'a3', a3)
) c (sort_order, col, value)
See SQL Fiddle with Demo. Once the data has been unpivoted, then you can pivot using the new columns which are the id values. So the full code is:
select col,
id1,
id40,
id41,
id42
from
(
select 'id'+cast(t.id as varchar(10)) p_id,
c.col,
c.value,
c.sort_order
from yourtable t
cross apply
(
values
(1, 'id', id),
(2, 'size', size),
(3, 'acted', acted),
(4, 'sum', sum),
(5, 'avg1', avg1),
(6, 'avg2', avg2),
(7, 'a1', a1),
(8, 'a2', a2),
(9, 'a3', a3)
) c (sort_order, col, value)
) src
pivot
(
max(value)
for p_id in (id1, id40, id41, id42)
) piv
order by sort_order;
See SQL Fiddle with Demo.
If you cannot use the CROSS APPLY and VALUES, then this can also be done, using the UNPIVOT function:
select col,
id1, id40, id41, id42
from
(
select 'id'+cast(id_piv as varchar(10)) id,
col,
value,
case col
when 'id' then 1
when 'size' then 2
when 'acted' then 3
when 'sum' then 4
when 'avg1' then 5
when 'avg2' then 6
when 'a1' then 7
when 'a2' then 8
when 'a3' then 9 end sort_order
from
(
select id id_piv,
cast(id as numeric(10, 2)) id,
cast(size as numeric(10, 2)) size,
cast(acted as numeric(10, 2)) acted,
sum, avg1, avg2, A1, A2, A3
from yourtable
) d
unpivot
(
value
for col in (id, size, acted, sum, avg1, avg2, a1, a2, a3)
) unpiv
) src
pivot
(
max(value)
for id in (id1, id40, id41, id42)
) piv
order by sort_order;
See SQL Fiddle with Demo
Finally, if you are going to have an unknown number of id values that you want to convert to columns, then you will need to use dynamic sql:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME('id'+cast(id as varchar(10)))
from yourtable
group by id
order by id
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT col, ' + #cols + '
from
(
select ''id''+cast(t.id as varchar(10)) p_id,
c.col,
c.value,
c.sort_order
from yourtable t
cross apply
(
values
(1, ''id'', id),
(2, ''size'', size),
(3, ''acted'', acted),
(4, ''sum'', sum),
(5, ''avg1'', avg1),
(6, ''avg2'', avg2),
(7, ''a1'', a1),
(8, ''a2'', a2),
(9, ''a3'', a3)
) c (sort_order, col, value)
) x
pivot
(
max(value)
for p_id in (' + #cols + ')
) p
order by sort_order'
execute(#query)
See SQL Fiddle with Demo
All versions the result:
| COL | ID1 | ID40 | ID41 | ID42 |
----------------------------------------------
| id | 1 | 40 | 41 | 42 |
| size | 3921 | 11979 | 11714 | 11599 |
| acted | 39 | 301 | 289 | 265 |
| sum | 690 | 5944.26 | 5060 | 4107.98 |
| avg1 | 17.69 | 19.75 | 17.51 | 15.5 |
| avg2 | 0.18 | 0.5 | 0.43 | 0.35 |
| a1 | (null) | 10000 | 10000 | 10000 |
| a2 | (null) | 2000 | 3000 | 5000 |
| a3 | (null) | 1000 | 2000 | 500 |