Reputation: 547
I have a work plan in DB as follows. For instance, the first row means that Team A has 10 hours' work to do in June (SP1006).
Team Sprint WorkHours A 1006 10 A 1007 20 A 1008 30 A 1009 40 B 1008 50 B 1009 60 B 1010 70
I want to derive it into the following form to show: at the end of each month, how many hours are left to be done, according to the work plan. As you see, the fields of the derived table depend on the original datatable too.
Team SP1005 SP1006 SP1007 SP1008 SP1009 SP1010 A 100 90 70 40 0 0 B 180 180 180 130 70 0
I'm trying to do this on SQL Server 2000. This is really hard for me. Can someone give some help? Thanks a lot!
Edit: Or you can ignore the row-to-column transformation part, (which I know how to do,) just concentrate on the accumulation part.
Upvotes: 2
Views: 2510
Reputation: 1462
Select Team,
isNull([1006], 0)+isNull([1007], 0)+isNull([1008], 0)+isNull([1009], 0)+isNull([1010], 0) as SP1005,
isNull([1007], 0)+isNull([1008], 0)+isNull([1009], 0)+isNull([1010], 0) as SP1006,
isNull([1008], 0)+isNull([1009], 0)+isNull([1010], 0) as SP1007,
isNull([1009], 0)+isNull([1010], 0) as SP1008,
isNull([1010], 0) as SP1009,
0 as SP1010
From YourTable
PIVOT (Sum(WorkHours) For Sprint in ([1005],[1006],[1007],[1008],[1009],[1010])) p
The problem is if you want this to be dynamic. In that case you should use dynamic sql query. Construction of dynamic query depends on several things:
Do you want to pass:
a) first month / last month or
b) list of months (eg. comma separated)
c) just start month and query should return columns up to the current month
d) just start month and query should return columns up to the last month you have in table
UPDATED
Version for SQL SERVER 2000
Select Team,
sum(SP1005) as SP1005,
sum(SP1006) as SP1006,
sum(SP1007) as SP1007,
sum(SP1008) as SP1008,
sum(SP1009) as SP1009,
0 as SP1010
From (
Select Team,
Case when Sprint in (1006, 1007, 1008, 1009, 1010) Then WorkHours Else 0 end as SP1005,
Case when Sprint in (1007, 1008, 1009, 1010) Then WorkHours Else 0 end as SP1006,
Case when Sprint in (1008, 1009, 1010) Then WorkHours Else 0 end as SP1007,
Case when Sprint in (1009, 1010) Then WorkHours Else 0 end as SP1008,
Case when Sprint in (1010) Then WorkHours Else 0 end as SP1009
From @t
) a
Group by Team
UPDATE 2: ADDED DYNAMIC STORED PROCEDURE (For SQL Server 2000+)
I assumed your Sprint column is integer.
Alter Procedure zzzGetTeamMonths( @FirstMonth int )
AS BEGIN
Declare @FirstMonthDate datetime
Declare @ActFirstMonth int
Declare @LastMonth int
Declare @LastMonthChar nvarchar(4)
Declare @nMonths int
Select @LastMonth=max(Sprint),
@ActFirstMonth=min(Sprint)
from zzzTest
Where Sprint >= @FirstMonth
If @LastMonth >= @FirstMonth Begin
-- Next line remove repeating columns from left side (for non-existant data)
-- You can comment it out if you want to see all columns
IF @ActFirstMonth>@FirstMonth Set @FirstMonth=@ActFirstMonth
Set @nMonths = (@LastMonth/100-@FirstMonth/100)*12 + (@LastMonth-(@LastMonth/100)*100-@FirstMonth+(@FirstMonth/100)*100)
Set @FirstMonthDate = convert(datetime, Right('0'+cast(@FirstMonth as varchar(4)),4)+'01',12)
Set @LastMonthChar = Right('0'+cast(@LastMonth as varchar(4)),4)
Create Table #Months (id int, mnt nvarchar(4), mntp nvarchar(4))
Insert Into #Months
Select Number,
Convert(nvarchar(4),Dateadd(month,number,@FirstMonthDate),12),
Convert(nvarchar(4),Dateadd(month,number-1,@FirstMonthDate),12)
From master.dbo.spt_values
Where type='P' and number<=@nMonths
Declare @cmd nvarchar(4000)
Declare @tmp nvarchar(4000)
Declare @col nvarchar(4000)
Select @tmp = '',
@col = ''
Select @col = @col + '
,sum(SP' + mntp + ') as SP' + mntp,
@tmp = @tmp + '
,Case when Sprint between ' + mnt + ' and ' + @LastMonthChar + ' Then WorkHours Else 0 end as SP' + mntp
from #Months
Drop Table #Months
Select @cmd = '
SELECT Team' + @col + '
,0 as SP' + @LastMonthChar + '
FROM
(
Select Team' + @tmp + '
From zzzTest
) a
Group by Team'
Print @cmd
Exec (@cmd)
End
RETURN 0
END
GO
Upvotes: 1
Reputation: 453897
For the accumulation part use a cursor. Unless the result set is very small (maybe <100 rows to process) when you can get away with a triangular join like.
SELECT t1.Team, t1.Sprint,g.wh-SUM(t2.WorkHours)
FROM tbl t1 JOIN tbl t2 ON t1.Team = t2.Team AND t2.Sprint <= t1.Sprint
JOIN
(SELECT SUM(WorkHours) AS wh, Team FROM tbl GROUP BY Team) g ON t1.Team=g.Team
GROUP BY t1.Team, t1.Sprint,g.wh
But as the number of rows grows the amount of work required by the triangular join grows exponentially.
Upvotes: 2