Conor8630
Conor8630

Reputation: 345

SQL Server Selecting data from different tables

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

Answers (4)

Wojciech Szymanski
Wojciech Szymanski

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

Suraj Kumar
Suraj Kumar

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

Caius Jard
Caius Jard

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

PSK
PSK

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:

  1. * doesn't go with GROUP BY, any non aggregated column you want in select should present in your GROUP BY
  2. As a best practice, you should use alias names for your tables while joining.

Upvotes: 1

Related Questions