Dynamic Way To Transpose SQL Table Similar to Excel Functionality - sql

I've looked around a good amount and haven't found quite the correct solution to what I think is a very simple problem. Everything I find focuses on one value being tranposed, but here I have a bunch.
I have this table currently and I want to transpose it exactly how Excel would. What is the easiest way to do this?
What I have:
nodeid | Headcount | FTE | Count of Part-Time Employees | Count of Non-Permanent Employees |
Actuals | 12 | 13 | 23 | 35 |
Targets | 15 | 15 | 25 | 25 |
What I want:
nodeid | Actuals | Targets |
Headcount | 12 | 15 |
FTE | 13 | 15 |
Count of Part-Time Employees | 23 | 25 |
Count of Non-Permanent Employees | 35 | 25 |

If 2016+, you can use a bit of JSON to "dynamically" unpivot your data, and then simply apply a PIVOT
Example
Declare #YourTable Table ([nodeid] varchar(50),[Headcount] int,[FTE] int,[Count of Part-Time Employees] int,[Count of Non-Permanent Employees] int) Insert Into #YourTable Values
('Actuals',12,13,23,35)
,('Targets',15,15,25,25)
Select *
From (
Select A.NodeID
,B.*
From #YourTable A
Cross Apply (
Select [Node] = [Key]
,[Value] = try_convert(int,Value)
From OpenJson( (Select A.* For JSON Path,Without_Array_Wrapper )) ) B
Where try_convert(int,value) is not null
) src
Pivot ( max(Value) for [NodeID] in ([Actuals],[Targets]) ) pvt
Order By Node desc
Returns
Node Actuals Targets
Headcount 12 15
FTE 13 15
Count of Part-Time Employees 23 25
Count of Non-Permanent Employees 35 25
Note: If <2016 there is an similar XML approach.

You can use unpivot and pivot to achieve that.
try the following:
DECLARE #tab TABLE
(nodeid VARCHAR(100),
Headcount INT,
FTE INT,
[Count of Part-Time Employees] INT,
[Count of Non-Permanent Employees] INT
);
INSERT INTO #tab SELECT 'Actuals', 12, 13, 23, 35;
INSERT INTO #tab SELECT 'Targets', 15, 15, 25, 25;
SELECT *FROM #tab;
DROP TABLE IF EXISTS #temp;
SELECT *
INTO #temp
FROM
(
SELECT nodeid,
Headcount,
FTE,
[Count of Part-Time Employees],
[Count of Non-Permanent Employees]
FROM #tab
) t UNPIVOT(val FOR col IN(Headcount,
FTE,
[Count of Part-Time Employees],
[Count of Non-Permanent Employees])) upvt;
DECLARE #collist VARCHAR(MAX);
SELECT #collist = STUFF(
(
SELECT DISTINCT ',' + QUOTENAME(nodeid)
FROM #temp
ORDER BY ',' + QUOTENAME(nodeid) FOR XML PATH('')
), 1, 1, '');
DECLARE #sql VARCHAR(MAX)= 'select col as nodeid, Actuals, Targets
from #temp t
pivot
(
max(val) for nodeid IN (' + #collist + ')
)pvt';
EXEC (#sql);
Please see the db<>fiddle here

Related

How can I join the results of multiple pivot queries together horizontally?

I may not have phrased the question clearly so I'll clarify it here.
I've got a VB.NET system that displays data. To display data, I have the following SQL stored procedure that makes use of pivot:
DECLARE #cols AS NVARCHAR(MAX), #query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(form_column_id) from
GetFormColumns(#formTemplateId) FOR XML PATH(''),
TYPE).value('.', 'NVARCHAR(MAX)') ,1,1,'')
set #query = N' select *
from
(select row_number as Row,fc.form_column_id, fdd.data
from
form_data_h fdh
inner join form_data_d fdd on fdd.form_data_h_id = fdh.form_data_h_id
inner join form_column fc on fc.form_column_id = fdd.form_column_id
inner join column_header c on c.column_header_id=fc.column_header_id
where fdh.is_active = 1 and fdh.form_data_h_id= ' +
CONVERT(varchar(10),#formDataHId) + ' and fc.is_active = 1
) src
pivot(
min(data)
for form_column_id in (' + #cols + N')
) piv'
execute(#query);
I had to make use of pivot because user data entry needs to be dynamic. So from this...
table results
The results now look like this.
pivot results
This data has different results filtered by particular parameters. Right now I've displayed the crop data for 2017. I want to join it with the crop data for 2018 (will change based on the Stored Procedure parameter #formDataHId).
That should result in something that looks like this...
2017 and 2018 results
Is there any way I can go about this in SQL or do I have to do that in VB.net? If so, how can I go about it?
Any ideas would be welcome because I'm a bit stumped right now. If users need to see data from let's say 2016 to 2019, it should be presented like that.
Pivot in SQL should be enough. If the structure of data in each year is the same, you can use UNION ALL between each year's data. Then use your query to pivot the combined years' data.
In my pivot function I use a table named Test to generate the pivot. I have 2 other tables Source2017 and Source2018. I insert both Source2017 and Source2018 using this query.
INSERT #Test
SELECT Date, Item, Quantity FROM #Source2017
UNION ALL
SELECT Date, Item, Quantity FROM #Source2018
If I only want data from 2017, I remove the Source2018 from the insert statement.
INSERT #Test
SELECT Date, Item, Quantity FROM #Source2017
Let's say I have more tables, Source2015 and Source2016. If I want to pivot all of them, just add the tables using UNION ALL.
INSERT #Test
SELECT Date, Item, Quantity FROM #Source2015
UNION ALL
SELECT Date, Item, Quantity FROM #Source2016
UNION ALL
SELECT Date, Item, Quantity FROM #Source2017
UNION ALL
SELECT Date, Item, Quantity FROM #Source2018
Full query:
IF OBJECT_ID('tempdb..#Test') IS NOT NUll DROP TABLE #Test
IF OBJECT_ID('tempdb..#Source2017') IS NOT NUll DROP TABLE #Source2017
IF OBJECT_ID('tempdb..#Source2018') IS NOT NUll DROP TABLE #Source2018
CREATE TABLE #Test
(
Date DATE,
Item VARCHAR(100),
Quantity INT
)
CREATE TABLE #Source2017
(
Date DATE,
Item VARCHAR(100),
Quantity INT
)
CREATE TABLE #Source2018
(
Date DATE,
Item VARCHAR(100),
Quantity INT
)
INSERT #Source2017 VALUES
('2017/01/01', 'Mango', 5),
('2017/01/01', 'Orange', 6),
('2017/01/02', 'Mango', 7),
('2017/01/02', 'Orange', 8),
('2017/01/02', 'Cherry', 9)
INSERT #Source2018 VALUES
('2018/01/01', 'Durian', 15),
('2018/01/02', 'Orange', 28),
('2018/01/03', 'Cherry', 19)
INSERT #Test
SELECT Date, Item, Quantity FROM #Source2017
UNION ALL
SELECT Date, Item, Quantity FROM #Source2018
DECLARE #SQL AS VARCHAR(MAX)
DECLARE #Columns AS VARCHAR(MAX)
DECLARE #Columns2 AS VARCHAR(MAX)
SELECT #Columns = COALESCE(#Columns + ',','') + QUOTENAME(Date)
FROM (SELECT DISTINCT Date FROM #Test) AS B
ORDER BY B.Date
SELECT #Columns2 = COALESCE(#Columns2 + ',','') + 'ISNULL(' + QUOTENAME(Date) + ', 0) AS [' + CAST(Date AS VARCHAR(100)) + ']'
FROM (SELECT DISTINCT Date FROM #Test) AS B
ORDER BY B.Date
SET #SQL = '
WITH PivotData AS
(
SELECT Date, Item, Quantity FROM #Test
)
SELECT
Item, ' + #Columns2 + '
FROM PivotData
PIVOT
(
SUM(Quantity)
FOR Date
IN (' + #Columns + ')
) AS PivotResult
ORDER BY Item'
EXEC(#SQL);
DROP TABLE #Test
DROP TABLE #Source2017
DROP TABLE #Source2018
Result:
+--------+------------+------------+------------+------------+------------+
| Item | 2017-01-01 | 2017-01-02 | 2018-01-01 | 2018-01-02 | 2018-01-03 |
+--------+------------+------------+------------+------------+------------+
| Cherry | 0 | 9 | 0 | 0 | 19 |
| Durian | 0 | 0 | 15 | 0 | 0 |
| Mango | 5 | 7 | 0 | 0 | 0 |
| Orange | 6 | 8 | 0 | 28 | 0 |
+--------+------------+------------+------------+------------+------------+

How to calculate average in SQL?

lets say I have the following table:
**FOOD** | **AMOUNT**
Bread | 2
Banana | 5
Pizza | 4
Apple | 57
Mandarin| 9
Orange | 8
Final result:
Bread | Percentage Of Total
Banana | percentage of total
etc
etc
I tried it in every single way, but couldn't find a solution. I hope someone can help me.
Using ANSI SQL (and SQL Server supports this syntax), you can do:
select food, sum(amount),
sum(amount) / sum(sum(amount)) over () as proportion_of_total
from t
group by food;
Note: Some databases do integer division, so you may need to convert to a floating point or fixed point type.
We can also try like below-
DECLARE #tbl AS TABLE
(
food VARCHAR(15)
,amount INT
)
INSERT INTO #tbl VALUES
('bread', 2)
,('banana', 5)
,('pizza', 4)
,('apple', 57)
,('mandarin', 9)
,('orange', 8)
SELECT
DISTINCT
food
,SUM(amount) OVER() TotalAmount
,SUM(amount) OVER (PARTITION BY food) PerFoodTotal
,CAST(SUM(amount) OVER (PARTITION BY food) * 100. / (SUM(amount) OVER()) AS DECIMAL(10,2)) [Percentage Of Total]
FROM #tbl
OUTPUT
food TotalAmount PerFoodTotal Percentage Of Total
--------------- ----------- ------------ ---------------------------------------
apple 85 57 67.06
banana 85 5 5.88
bread 85 2 2.35
mandarin 85 9 10.59
orange 85 8 9.41
pizza 85 4 4.71
(6 row(s) affected)
You can try something like this:
declare #tbl as table (
food varchar(15)
,amount int
)
insert into #tbl values
('bread', 2)
,('banana', 5)
,('pizza', 4)
,('apple', 57)
,('mandarin', 9)
,('orange', 8)
select SUM(amount) from #tbl
select
food
,SUM(amount) as [food amount]
,(SUM(cast(amount as numeric(18,2))) / (select sum(cast(amount as numeric(18,2))) from #tbl)) * 100 as [Percentage Of Total]
,(select sum(amount) from #tbl) as total
from #tbl
group by food
Here you got a way fo getting the PercentageOfTotal, asuming that the sum of all will not be 0
DECLARE #total INT = (SELECT SUM(AMOUNT) FROM Table1)
SELECT FOOD, CAST((CAST((100 * AMOUNT) AS DECIMAL (18,2)) / #total ) AS DECIMAL(18,2)) AS PercentageOfTotal from Table1
SQL Fiddle
MS SQL Server 2014 Schema Setup:
CREATE TABLE MusicGenres (name varchar(10)) ;
INSERT INTO MusicGenres (name)
VALUES ('Pop'),('Techno'),('Trance'),('trap'),('Hardcore'),('Electro') ;
CREATE TABLE Table2 (SongID int, MusicGenres varchar(10)) ;
INSERT INTO Table2 (SongID, MusicGenres)
VALUES (1,'Hardcore')
,(2,'Hardcore')
,(3,'Pop')
,(4,'Trap')
,(5,'Hardcore')
,(6,'Pop')
,(7,'Electro')
,(8,'Electro')
,(9,'Pop')
,(10,'Pop')
,(11,'Pop')
;
Query 1:
SELECT s1.name
, s1.recCount
, ( s1.recCount / CAST( ( SUM(recCount) OVER() ) AS decimal(5,2) ) )*100 AS pct
FROM (
SELECT m.name
, count(t.SongID) AS recCount
FROM MusicGenres m
LEFT OUTER JOIN Table2 t ON m.name = t.MusicGenres
GROUP BY m.name
) s1
Could be shortened to
SELECT m.name
, count(t.SongID) AS recCount
, ( count(t.SongID) / CAST( ( SUM(count(t.SongID)) OVER() ) AS decimal(5,2) )
)*100 AS pct
FROM MusicGenres m
LEFT OUTER JOIN Table2 t ON m.name = t.MusicGenres
GROUP BY m.name
Results:
| name | recCount | pct |
|----------|----------|---------|
| Electro | 2 | 18.1818 |
| Hardcore | 3 | 27.2727 |
| Pop | 5 | 45.4545 |
| Techno | 0 | 0 |
| Trance | 0 | 0 |
| trap | 1 | 9.0909 |

SQL query create cross column

I have this table
customer | product | quantity
-------------------------------
CLI01 | A | 10
CLI01 | B | 20
CLI02 | A | 31
CLI03 | A | 10
CLI03 | C | 12
and I want to create in SQL Server this output:
customer | crossProduct | quantity
-----------------------------------
CLI01 | A+B | 30
CLI02 | Only A | 31
CLI03 | B+C | 22
Thanks in advance
Niko
If you only care about two products, then this is simple aggregation:
select customer,
(case when count(distinct product) > 2 then 'Lots of Products'
when min(product) = max(product) then 'Only ' + min(product)
else min(product) + '+' + max(product)
end) as crossproduct,
sum(quantity)
from t
group by customer;
If you care about more than two products, then you'll need to do aggregation string concatenation. That is a bit painful in SQL Server. Start by Googling "sql server aggregate string concatenation".
This is s sample:
----- Test Data ----------
DECLARE #TestData TABLE (customer VARCHAR(10),product VARCHAR(10),quantity INT)
INSERT INTO #TestData
SELECT 'CLI01','A',10 UNION ALL
SELECT 'CLI01','B',20 UNION ALL
SELECT 'CLI02','A',31 UNION ALL
SELECT 'CLI03','A',10 UNION ALL
SELECT 'CLI03 ','C',12
----- Query -------------
SELECT customer,CASE WHEN COUNT( DISTINCT t.product)=1 THEN 'Only ' ELSE '' END + LEFT(c.product,LEN(c.product)-1) AS Product,SUM(quantity) AS quantity
FROM #TestData AS t
CROSS APPLY(SELECT a.product+'+' FROM #TestData AS a WHERE a.customer=t.customer FOR XML PATH('')) c(product)
GROUP BY customer,c.product
ORDER BY t.customer
customer Product quantity
CLI01 A+B 30
CLI02 Only A 31
CLI03 A+C 22
Have you tried using stuff? This will give you what you need. Works with as many products as necessary, from sql 2008 onwards.
CREATE TABLE x (customer VARCHAR (20), product CHAR(1), quantity INT )
INSERT INTO x
VALUES( 'CLI01', 'A', 10),
( 'CLI01', 'B', 20),
( 'CLI02', 'A', 31),
( 'CLI03', 'A', 10),
( 'CLI03', 'C', 12)
SELECT x1.customer, x3.Products, SUM(x1.quantity)
FROM x x1
CROSS APPLY ( SELECT Products = STUFF( (select '+' + product AS [text()]
FROM x x2
WHERE x2.customer = x1.customer
FOR XML PATH ('') ), 1, 1,'') ) x3
GROUP BY x1.customer, x3.Products

SQL Server - Complex Dynamic Pivot multiple columns

FYI, this question is already answered but I have some new requirements which is very complex to implement so, I am posting it as a new question instead of editing the old question: (Previous Question)
I have two tables "Controls" and "ControlChilds" (in the ControlChilds table we have added a new column called ControlChildComments which we need to show in PIVOT output)
Parent Table Structure:
Create table Controls(
ProjectID Varchar(20) NOT NULL,
ControlID INT NOT NULL,
ControlCode Varchar(2) NOT NULL,
ControlPoint Decimal NULL,
ControlScore Decimal NULL,
ControlValue Varchar(50)
)
Sample Data
ProjectID | ControlID | ControlCode | ControlPoint | ControlScore | ControlValue
P001 1 A 30.44 65 Invalid
P001 2 C 45.30 85 Valid
Child Table Structure:
Create table ControlChilds(
ControlID INT NOT NULL,
ControlChildID INT NOT NULL,
ControlChildValue Varchar(200) NULL,
ControlChildComments Varchar(200) NULL
)
Sample Data
ControlID | ControlChildID | ControlChildValue | ControlChildComments
1 100 Yes Something
1 101 No NULL
1 102 NA Others
1 103 Others NULL
2 104 Yes New one
2 105 SomeValue NULL
Based on my previous question (Previous Question) I got this output (You can refer to the PIVOT queries which produces this output in the answer given by #bluefeet. Thanks again #bluefeet.)
But now my requirement is changed and I need ControlChildComments after each Child values. For example, A_Child1, A_Child1Comments, A_Child2, A_Child2Comments etc...
Another tricky thing is I need to show the comments only when they are not null otherwise I shouldn't show the column. For example, in this case, it should be like this:
A_Child1, A_Child1Comments, A_Child2, A_Child3, A_Child3Comments, A_Child4, C_Child1, C_Child1Comments, C_Child2
Is this possible? I tried lot of things but the results are not accurate.
Since you now have multiple columns in your ControlChilds table that you need to PIVOT, you will need to use the similar method of unpivoting them first that you applied with the Controls table.
You will need to unpivot both the ChildControlValue and ChildControlComments using code similar to:
select
projectId,
col = ControlCode+'_'+subCol+cast(seq as varchar(10)),
value
from
(
select c.ProjectId,
c.ControlCode,
cc.ControlChildValue,
cc.ControlChildComments,
row_number() over(partition by c.ProjectId, c.ControlCode
order by cc.ControlChildId) seq
from controls c
inner join controlchilds cc
on c.controlid = cc.controlid
) d
cross apply
(
select 'ChildValue', ControlChildValue union all
select 'ChildComments', ControlChildComments
) c (subCol, value);
See SQL Fiddle with Demo. This gets your data in the format:
| PROJECTID | COL | VALUE |
|-----------|------------------|-----------|
| P001 | A_ChildValue1 | Yes |
| P001 | A_ChildComments1 | Something |
| P001 | A_ChildValue2 | No |
| P001 | A_ChildComments2 | (null) |
| P001 | A_ChildValue3 | NA |
You then use this code in your existing query:
select ProjectId,
A_ControlPoint, A_ControlScore, A_ControlValue,
A_ChildValue1, A_ChildComments1, A_ChildValue2,
A_ChildComments2, A_ChildValue3, A_ChildComments3,
A_ChildValue4, A_ChildComments4,
C_ControlPoint, C_ControlScore, C_ControlValue,
C_Child1, C_Child2
from
(
select
ProjectId,
col = ControlCode +'_'+col,
val
from
(
select
c.ProjectId,
c.ControlCode,
c.ControlPoint,
c.ControlScore,
c.ControlValue
from controls c
) d
cross apply
(
select 'ControlPoint', cast(controlpoint as varchar(10)) union all
select 'ControlScore', cast(ControlScore as varchar(10)) union all
select 'ControlValue', ControlValue
) c (col, val)
union all
select
projectId,
col = ControlCode+'_'+subCol+cast(seq as varchar(10)),
value
from
(
select c.ProjectId,
c.ControlCode,
cc.ControlChildValue,
cc.ControlChildComments,
row_number() over(partition by c.ProjectId, c.ControlCode
order by cc.ControlChildId) seq
from controls c
inner join controlchilds cc
on c.controlid = cc.controlid
) d
cross apply
(
select 'ChildValue', ControlChildValue union all
select 'ChildComments', ControlChildComments
) c (subCol, value)
) src
pivot
(
max(val)
for col in (A_ControlPoint, A_ControlScore, A_ControlValue,
A_ChildValue1, A_ChildComments1, A_ChildValue2,
A_ChildComments2, A_ChildValue3, A_ChildComments3,
A_ChildValue4, A_ChildComments4,
C_ControlPoint, C_ControlScore, C_ControlValue,
C_Child1, C_Child2)
) piv;
See SQL Fiddle with Demo. Finally, you'll implement this in your dynamic SQL script:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(col)
from
(
select ControlCode,
col = ControlCode +'_'+col,
seq,
so
from controls
cross apply
(
select 'ControlPoint', 0, 0 union all
select 'ControlScore', 0, 1 union all
select 'ControlValue', 0, 2
) c (col, seq, so)
union all
select ControlCode,
col = ControlCode+'_'+subcol+cast(rn as varchar(10)),
rn,
so
from
(
select ControlCode,
row_number() over(partition by c.ProjectId, c.ControlCode
order by cc.ControlChildId) seq
from controls c
inner join controlchilds cc
on c.controlid = cc.controlid
) d
cross apply
(
select 'ChildValue', seq, 3 union all
select 'ChildComments', seq, 4
) c (subcol, rn, so)
) src
group by ControlCode, seq, col, so
order by ControlCode, seq, so
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT ProjectId, ' + #cols + '
from
(
select ProjectId,
col = ControlCode +''_''+col,
val
from
(
select
c.ProjectId,
c.ControlCode,
c.ControlPoint,
c.ControlScore,
c.ControlValue
from controls c
) d
cross apply
(
select ''ControlPoint'', cast(controlpoint as varchar(10)) union all
select ''ControlScore'', cast(ControlScore as varchar(10)) union all
select ''ControlValue'', ControlValue
) c (col, val)
union all
select
projectId,
col = ControlCode+''_''+subCol+cast(seq as varchar(10)),
value
from
(
select c.ProjectId,
c.ControlCode,
cc.ControlChildValue,
cc.ControlChildComments,
row_number() over(partition by c.ProjectId, c.ControlCode
order by cc.ControlChildId) seq
from controls c
inner join controlchilds cc
on c.controlid = cc.controlid
) d
cross apply
(
select ''ChildValue'', ControlChildValue union all
select ''ChildComments'', ControlChildComments
) c (subCol, value)
) x
pivot
(
max(val)
for col in (' + #cols + ')
) p '
exec sp_executesql #query;
See SQL Fiddle with Demo. Both of these gives a result:
| PROJECTID | A_CONTROLPOINT | A_CONTROLSCORE | A_CONTROLVALUE | A_CHILDVALUE1 | A_CHILDCOMMENTS1 | A_CHILDVALUE2 | A_CHILDCOMMENTS2 | A_CHILDVALUE3 | A_CHILDCOMMENTS3 | A_CHILDVALUE4 | A_CHILDCOMMENTS4 | C_CONTROLPOINT | C_CONTROLSCORE | C_CONTROLVALUE | C_CHILDVALUE1 | C_CHILDCOMMENTS1 | C_CHILDVALUE2 | C_CHILDCOMMENTS2 |
|-----------|----------------|----------------|----------------|---------------|------------------|---------------|------------------|---------------|------------------|---------------|------------------|----------------|----------------|----------------|---------------|------------------|---------------|------------------|
| P001 | 30.44 | 65.00 | Invalid | Yes | Something | No | (null) | NA | Others | Others | (null) | 45.30 | 85.00 | Valid | Yes | New one | SomeValue | (null) |
Here is an example of a dynamic crosstab. Since you have multiple columns you would need to adjust the dynamic portion of this to suit.
if OBJECT_ID('Something') is not null
drop table Something
create table Something
(
ID int,
Subject1 varchar(50)
)
insert Something
select 10868952, 'NUR/3110/D507' union all
select 10868952, 'NUR/3110/D512' union all
select 10868952, 'NUR/4010/D523' union all
select 10868952, 'NUR/4010/HD20' union all
select 12345, 'asdfasdf'
declare #MaxCols int
declare #StaticPortion nvarchar(2000) =
'with OrderedResults as
(
select *, ROW_NUMBER() over(partition by ID order by Subject1) as RowNum
from Something
)
select ID';
declare #DynamicPortion nvarchar(max) = '';
declare #FinalStaticPortion nvarchar(2000) = ' from OrderedResults Group by ID order by ID';
with E1(N) AS (select 1 from (values (1),(1),(1),(1),(1),(1),(1),(1),(1),(1))dt(n)),
E2(N) AS (SELECT 1 FROM E1 a, E1 b), --10E+2 or 100 rows
E4(N) AS (SELECT 1 FROM E2 a, E2 b), --10E+4 or 10,000 rows max
cteTally(N) AS
(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4
)
select #DynamicPortion = #DynamicPortion +
', MAX(Case when RowNum = ' + CAST(N as varchar(6)) + ' then Subject1 end) as Subject' + CAST(N as varchar(6)) + CHAR(10)
from cteTally t
where t.N <=
(
select top 1 Count(*)
from Something
group by ID
order by COUNT(*) desc
)
select #StaticPortion + #DynamicPortion + #FinalStaticPortion
--declare #SqlToExecute nvarchar(max) = #StaticPortion + #DynamicPortion + #FinalStaticPortion;
--exec sp_executesql #SqlToExecute

Represent multiple rows of data in one column as multiple columns in one row SQL server

I have a table with the following structure and data:
batsman | runs | year
1 | 800 | 2012
1 | 950 | 2011
1 | 1050 | 2010
2 | 550 | 2012
2 | 650 | 2011
2 | 400 | 2010
3 | 900 | 2012
This data needs to be Selected through a sql query as:
batsman | 2012 | 2011 | 2010
1 | 800 | 950 | 1050
2 | 550 | 650 | 400
3 | 900 | - | -
I'm trying to do this through a stored proc. The assumption can be made that the number of columns (in terms of years) is fixed: 3.
Also note, there are no arithmetic operations necessary - all the numbers I need are already there, they just need to be represented column-wise.
There are several ways that you can convert the rows of data into columns.
In SQL Server you can use the PIVOT function:
select batsman, [2012], [2011], [2010]
from
(
select batsman, runs, year
from yourtable
) d
pivot
(
sum(runs)
for year in ([2012], [2011], [2010])
) piv;
Or you can use an aggregate function with a CASE expression:
select batsman,
sum(case when year = 2012 then runs else 0 end) [2012],
sum(case when year = 2011 then runs else 0 end) [2011],
sum(case when year = 2010 then runs else 0 end) [2010]
from yourtable
group by batsman;
The other version will work great if you have a known number of columns. But if you are going to have an unknown number of year values, then you will need to use dynamic SQL:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT distinct ',' + QUOTENAME(year)
from yourtable
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT batsman,' + #cols + '
from
(
select batsman, runs, year
from yourtable
) x
pivot
(
sum(runs)
for year in (' + #cols + ')
) p '
execute(#query)
Please try PIVOT:
declare #tbl as table(batsman int, runs int, yearr int)
insert into #tbl values
(1, 800, 2012),
(1, 950, 2011),
(1, 1050, 2010),
(2, 550, 2012),
(2, 650, 2011),
(2, 400, 2010),
(3, 900, 2012)
select * From #tbl
select *
from
(
select *
from #tbl
) d
pivot
(
max(runs)
for yearr in ([2012], [2011], [2010])
) piv;
You would need to use Pivot Tables as detailed here:
http://blogs.msdn.com/b/spike/archive/2009/03/03/pivot-tables-in-sql-server-a-simple-sample.aspx
For example:
select * from batsman
pivot (runs for Year in ([2012], [2011], [2010])) as runsperyear