views:

55

answers:

2

A Project has multiple Tasks, which has multiple Assignments

Projects (1-n) -> Tasks (1-n) -> Assignments

A field on the Tasks table is Planned Hours.

A field on the Assignments table is Assigned Hours.

How do I get the Planned Hours and Assigned Hours for all Projects in a single query?

+5  A: 

EDIT: This answer gives incorrect results but I will leave it up for historical purposes.

I think this will help you out. I can't figure out how to get rid of the Distinct though.

Select Distinct  
        Project.ProjectId,
        Sum(Task.PlannedHours) 
            Over (Partition By Project.ProjectId) As PlannedHours,
        Sum(Assignment.AssignedHours) 
            Over (Partition By Project.ProjectId) As AssignedHours
From Project 
    Inner Join Task On Project.ProjectId = Task.ProjectId   
    Inner Join Assignment On Task.TaskId= Assignment.AssignmentId

Here is my test script.

Declare @Project Table (ProjectId Int)
Declare @Task Table (TaskId Int, ProjectId Int, PlannedHours Int)
Declare @Assignment Table (AssignmentId Int, TaskId Int, AssignedHours Int)

Insert Into @Project (ProjectId) values (1)
Insert Into @Project (ProjectId) values (2)
Insert Into @Project (ProjectId) values (3)

Insert Into @Task (TaskId, ProjectId, PlannedHours) values (1, 1, 10)
Insert Into @Task (TaskId, ProjectId, PlannedHours) values (2, 1, 10)
Insert Into @Task (TaskId, ProjectId, PlannedHours) values (3, 2, 11)
Insert Into @Task (TaskId, ProjectId, PlannedHours) values (4, 3, 12)

Insert Into @Assignment (AssignmentId, TaskId, AssignedHours) values (1, 1, 2)
Insert Into @Assignment (AssignmentId, TaskId, AssignedHours) values (2, 1, 2)
Insert Into @Assignment (AssignmentId, TaskId, AssignedHours) values (3, 2, 2)
Insert Into @Assignment (AssignmentId, TaskId, AssignedHours) values (4, 3, 3)

Select Distinct 
        Project.ProjectId,
        Sum(Task.PlannedHours) 
            Over (Partition By Project.ProjectId) As PlannedHours,
        Sum(Assignment.AssignedHours) 
            Over (Partition By Project.ProjectId) As AssignedHours   
From @Project Project 
    Inner Join @Task Task On Project.ProjectId = Task.ProjectId   
    Inner Join @Assignment Assignment On Task.TaskId= Assignment.AssignmentId
ChaosPandion
+1 for using `Sum Over Partition` instead of sub-queries and temporary tables.
macek
+3  A: 
select p.ProjectID, t.PlannedHoursSum, a.AssignedHoursSum
from Projects p
inner join (
    select ProjectID, sum(PlannedHours) as PlannedHoursSum
    from Tasks
    group by ProjectID
) t on p.ProjectID = t.ProjectID
inner join (
    select t.ProjectID, sum(AssignedHours) as AssignedHoursSum
    from Tasks t
    inner join Assignments a on t.TaskID = a.TaskID
    group by t.ProjectID
) a on p.ProjectID = a.ProjectID
RedFilter
This is very close to what I'm going with. Thanks!
Matthew Jones
@Matthew - I really don't care about the rep but if I was maintaining your app I would prefer to see the more succinct version in my answer. Also I compared the query plans of these two answers and it was estimated that this one would take twice as long as my answer. Obviously you won't know until you try it with a large data set but it is something to consider.
ChaosPandion
@Matthew Jones, I agree with @ChaosPandion on this one. The `Sum Over Partition` is way more performant than the sub-queries and temporary tables @OrbMan is using.
macek
Please note, the OP did not specify which version of SQL Server they are using. The above approach will work on all versions, and has the advantage of being standard ANSI SQL and portable across platforms. I don't doubt that Sum Over Partition would perform as well or faster. This may or may not be a concern.
RedFilter
@smotchkiss: I think you mean derived tables; there are no temporary tables in use here.
RedFilter
@Chaos, @smotchkiss: The problem with the partitioning query is the inner join against Assignments. That is causing the query to count the Planned Hours value for each Assignment (in other words, if a task has 100 planned hours and two Assignments, the query returns 200 planned hours for that task). I would prefer to use the partitioning solution too, but until I can solve that problem subqueries will have to do.
Matthew Jones
@Matthew - I wish you would have mentioned that. After tweaking my original query I could not get it to give the correct results without using a derived table.
ChaosPandion