Reputation: 345
I am trying to make a table for reports which is made up from data from other tables. The Reports table needs to include info of how many holiday hours and sick hours each site is taken but the employees take hours individually. All these table are connected by foreign key relationship.
[dbo].[Employee]:
EmployeeID |Name |SiteID |
------------+----------------+---------------+
1 | Joe |1 |
2 | Tom |2 |
3 | Mary |3 |
4 | Bill |1 |
5 | 16 |2 |
Site:
[dbo].[Site]:
SiteID |Site Name |
------------+----------------+
1 | SiteA |
2 | SiteB |
3 | SiteC |
[dbo].[HolidayRequestForm]:
EmployeeID |HolidayHours |
------------+----------------+
1 | 8 |
2 | 16 |
3 | 16 |
[dbo].[SickLeaveRequestForm]:
EmployeeID |SickHours |
------------+----------------+
1 | 8 |
2 | 8 |
3 | 8 |
I have tried achieve this by using joins and then selecting the relevant data from the different tables, but I'm having no luck.
SELECT *
FROM [dbo].[Employee]
INNER JOIN [dbo].[Site]
ON [dbo].[Site].SiteID = [dbo].[Employee].SiteID
INNER JOIN [dbo].[HolidayRequestForm]
LEFT JOIN [dbo].[SickLeaveRequestForm]
ON [dbo].[SickLeaveRequestForm].EmployeeID = [dbo].
[HolidayRequestForm].EmployeeID
GROUP BY [dbo].[Site].SiteName
Would I be better off creating a view?
Here is the desired result!
SiteName |Holiday Hours |Sick Hours
---------+----------------+---------------+
Site A | 8 |8 |
Site B | 16 |8 |
Site C | 16 |8 |
Upvotes: 1
Views: 72
Reputation: 358
SELECT dbo.Site.SiteName,
SUM(dbo.HolidayRequestForm.HolidayHours) AS HolidayHours,
SUM(dbo.SickLeaveRequestForm.SickHours) AS SickHours
FROM dbo.Employee INNER JOIN
dbo.Site ON dbo.Employee.SiteID = dbo.Site.SiteID INNER JOIN
dbo.HolidayRequestForm ON dbo.Employee.EmployeeID =
dbo.HolidayRequestForm.EmployeeID INNER JOIN
dbo.SickLeaveRequestForm ON dbo.Employee.EmployeeID = dbo.SickLeaveRequestForm.EmployeeID
GROUP BY dbo.Site.SiteName
Would I be better off creating a view? It all depends if this is a one-time query or you are going to use that for reporting on a regular basis.
Also if you want to include all Sites where no employes have taken sick leave or holiday (e.g. for MSSQL):
SELECT dbo.Site.SiteName,
{ fn IFNULL(SUM(dbo.HolidayRequestForm.HolidayHours), 0) } AS HolidayHours,
{ fn IFNULL(SUM(dbo.SickLeaveRequestForm.SickHours), 0) } AS SickHours
FROM dbo.Employee
LEFT OUTER JOIN dbo.HolidayRequestForm ON dbo.Employee.EmployeeID = dbo.HolidayRequestForm.EmployeeID
LEFT OUTER JOIN dbo.SickLeaveRequestForm ON dbo.Employee.EmployeeID = dbo.SickLeaveRequestForm.EmployeeID
RIGHT OUTER JOIN dbo.Site ON dbo.Employee.SiteID = dbo.Site.SiteID
GROUP BY dbo.Site.SiteName
Upvotes: 2
Reputation: 5653
You can try the below query.
create table Employee(EmployeeID int, Name varchar(20), SiteID int)
insert into Employee values
(1, 'Joe', 1),
(2, 'Tom', 2),
(3, 'Mary', 3),
(4, 'Bill', 1),
(5, '16', 2)
create table [Site] (SiteID int, [Site Name] Varchar(20))
insert into [Site] Values
(1, 'SiteA'),
(2, 'SiteB'),
(3, 'SiteC')
Create table HolidayRequestForm(EmployeeID int, HolidayHours int)
insert into HolidayRequestForm Values
(1, 8),
(2, 16),
(3, 16)
Create table SickLeaveRequestForm(EmployeeID int, SickHours int)
insert into SickLeaveRequestForm Values
(1 ,8),
(2,8),
(3,8)
Select [Site Name], SUM(HolidayHours) as HolidayHours, SUM(SickHours) as
SickHours
from(
Select
[Site].[Site Name], HolidayRequestForm.HolidayHours, SickLeaveRequestForm.SickHours
from [Site]
inner join Employee on [Site].SiteId = Employee.SiteId
inner join HolidayRequestForm on HolidayRequestForm.EmployeeID = Employee.EmployeeID
inner join SickLeaveRequestForm on Employee.EmployeeID = SickLeaveRequestForm.EmployeeID
)a group by [Site Name]
Upvotes: 1
Reputation: 74710
Youre trying to select all the columns from a query that is grouping - you can only select columns that are grouped, or appear in an aggregate function:
SELECT s.SiteName, SUM(hrf.HolidayHours) as SumHolidayHours, SUM(slrf.SickHours) as SumSickHours
FROM
[dbo].[Employee] e
INNER JOIN [dbo].[Site] s
ON s.SiteID = e.SiteID
LEFT JOIN [dbo].[HolidayRequestForm] hrf
ON hrf.EmployeeId = e.EmployeeID
LEFT JOIN [dbo].[SickLeaveRequestForm] slrf
ON slrf.EmployeeID = e.EmployeeID
GROUP BY s.SiteName
You were missing an ON condition to one of your joins. I've also added table aliases to reduce the visual clutter and converted both the joins to the hours tables into being left joins just in case an employee doesn't have any records in those tables
The important thing to appreciate about grouping queries is that multiple records with the same combination of values in the grouped columns (your sitename) will be collapsed into a single record in the output. The aggregate function (SUM MAX MIN AVG etc) defines what to do with all the detailed data as it collapses. In this case we are trying to add up the hours by site. The association is
site -> employees -> hours (sick and holiday)
Because there are multiple employees and possibly also multiple hours, but we're grouping on site name, there will be only one row per site in the output, so we need to specify an aggregation to carry out on the other columns. You can't select * from a grouping query, because you can't include all the columns; you can only include columns that are part of th grouping combination, or an aggregate of the data that went into the group. If you ever want to add more columns to a group query but you don't want to have the group broken down into more fine detail on the grouping keys, you have to instead join the grouped data back to the main tables to get the additional data
Upvotes: 1
Reputation: 17953
You can use SUM
aggregate function with GROUP BY
like following query.
SELECT s.sitename,
Sum(hf.holidayhours) AS HolidayHours,
Sum(sl.sickhours) AS SickHours
FROM [dbo].[site] s
INNER JOIN [dbo].[employee] e
ON s.siteid = e.siteid
INNER JOIN [dbo].[holidayrequestform] hf
ON e.employeeid = hf.employeeid
INNER JOIN [dbo].[sickleaverequestform] sl
ON sl.employeeid = e.employeeid
GROUP BY s.sitename
Note:
*
doesn't go with GROUP BY
, any non aggregated column you want
in select should present in your GROUP BY
Upvotes: 1