Query two tables linked to same primary key - sql

long time reader first time poster.
I have what looks like a straight forward Access question.
I've an Employee table with these fields;
|EmployeeID|StaffName|Phone|Email|
EmployeeID is a foreign key in two tables - one for logging the status of a generator ie
|LogID|Status|Logged By|
and one for any incidents with the generator
|IncidentID|Description|Raised By|LogID|
I can create a query to link incidents and their log details eg
|IncidentID|Description|Raised By|LogID|Logged By|
This works if I put the EmployeeID number into 'Logged By' and 'Raised By' - if I put the StaffName - as the company wants, it doesn't work because it's selecting two StaffNames from the same primary key.
Any way around this?
Here is the Access code, the part where LoggedBy and RaisedBy are joined to EmployeeID is in bold.
SELECT [2_Incidents].IncidentID, [2_Incidents].Description, [8_Employees].StaffName AS [Raised By], [1_Log].LogID, [8_Employees].StaffName AS [Logged By]
FROM 8_Employees INNER JOIN (2_Incidents INNER JOIN 2_Incidents ON [1_Log].LogID = [2_Incidents].LogID) ON [8_Employees].EmployeeID = [1_Log].LoggedBy AND [8_Employees].EmployeeID = [2_Incidents].RaisedBy);
I've gotten around this by creating a copy of the Employees table but this could get awkward if there are more fields linked to Employees in the future (eg Closed By, Verified By etc)

If I understand you correctly, the solution is to join to the employee table twice.
In your log details you are doing something like SELECT * FROM incident JOIN log USING (LogID)...
You mention "selecting two StaffNames from the same primary key" - I take it that you are attempting to join Employee somehow with Raised By and Logged By.
If you actually join to two copies of employee: SELECT ... FROM incident JOIN log USING (LogId) JOIN employee ei ON (ei.employeeid = incident.raised_by) JOIN employee el ON (el.employeeid = log.logged_by) you can reference then the ei.staffname (incident raise by employee) and el.staffname (logged by employee)
PS. Sorry about the non-access syntax, but hopefully you can convert!

Related

How to link tables correctly in SQL to add roles to staff?

Currently I have a staff table with columns:
Staff_Id, first_name, Surname.
My second table is:
Id, management_role.
When I link the tables each staff member gets added to every management role. So for example a person in first table called Jim is added three times as manager, supervisor, intern and this happens for every staff.
Some things to consider that are your ID columns are primary keys for their respective tables. If not are every value in the column is unique? Also are ids not
From your description you might be using a cross join here. The thing you need is inner join so it joins the matching id's together.
So you can do
SELECT *
FROM staff_table as st
INNER JOIN management_table as mt
ON st.Staff_Id = mt.ID

I am trying to write a SQL Server query using joins and having some difficulty

Tables are explained in detail as below:
I have 3 tables:
Table A:
It serves as the master table for information about the employees.
EmployeeId(Primary key)
Employee Designation
EmployeeName (More columns of employee data which is not relevant to this particular query)
Table B:
It serves as table where all employees who are accounted for are stored. For ex an employee who has reported sick or is on leave or has pregnancy leave, etc. Bottom line an employee which is not available
EmployeeID (primary key) (also referencing master table A as foreign key)
AccountedFor
AccountedFordurationFrom (datetime)
AccountedForDurationTo (datetime)
Table C:
It serves as a table where excused data of employees are present. For ex we have our organization's time table spread as events, 1st event is morning time conference, then 2nd is silence working time, 3rd is brainstorming sessions etc. Now if an employee is excused for a particular event, it is entered here.
EmployeeID
EventCode
Excuse_DurationFrom
Excuse Duration To
Any specific details
Here EmployeeID and ExcusedForEventCode are both composite primary keys as it is possible to have same employeeId for multiple excuses,but the combination is always unique.
We have built some custom attendance management system and would require the following details:
We need to find all those employees who are neither accounted for nor excused for a specific event(this will be provided through front end) for a time duration selected through the front end.
The result of the above query will subsequently be used to compare with a biometric attendance machine logs which gives
EmployeeId|LogDate(datetime)|EventCodes as a separate table input to our database (Master table A employeeId references this EmployeeId as foreign key)
It will be compared to find out true absentees for a particular event. ie All those employees who are neither accounted for, nor excuses for any particular event and who does not figure out in the biometric scan machine logs are absented for those time duration selected. We need the output of absentee like this EmployeeId|Employee Designation|Employee Name|EventName (have a separate table linking with EventCode)|Date&time (this would be per day per event report of employee who are absent from the selected time duration).
We have tried queries like:
select
employeemastertable.employeeid,
employeemastertable.Designation,
employeemastertable.Name,
EventCodes.EventCodeName as Eventexcusedfrom
from
employeemastertable
inner join
employeeexcusedforevents on employeemastertable.employeeid = employeeexcusedforevents.employeeid
inner join
EventCodes on employeeexcusedforEvents.ExcusedForEventCode = EventCodes.Eventcode
left join
employeeaccountedFor on employeemastertable.employeeid = employeeaccountedFor.employeeid
where
employeeexcusedforevents.ExcusedForEventCode != 1 (Morning conference)
and employeeaccountedFor.employeeid is null;
Names have been changed
I do understand this will give those employees who does not figure out in event Morning conference but even if I do left join instead of inner join between employeemastertable and employeeexcusedForevents and put employeeexcusedforevents.excusedforeventcode is null and employeeexcusedforevents.employeeid is null, I do get all those employees not present in the other two table, but the criteria of event is not satisfied. That means what if the employee is excused for the 2nd event as well in the organization. How would I cater for that in the above code? (PS this is only the 1st part of the equation I understand that, after this I need help for the other part also, where time duration and comparing with logs is concerned)?
I assume there will be just one row for the EventCode=1 in table EventCodes. Below I cross join the wanted event to the employee master table and then exclude any employees that are excused or accounted for.
-- employees neither accounted for nor excused for a specific event
SELECT
em.employeeid
, em.Designation
, em.Name
, ec.EventCodeName AS Eventexcusedfrom
FROM employeemastertable em
CROSS JOIN (
SELECT Eventcode, EventCodeName
FROM EventCodes
WHERE Eventcode = 1
) ec
WHERE NOT EXISTS (
SELECT NULL
FROM employeeexcusedforevents ee
WHERE em.employeeid = ee.employeeid
AND ec.Eventcode = ee.ExcusedForEventCode
)
AND NOT EXISTS (
SELECT NULL
FROM employeeaccountedFor eaf
WHERE em.employeeid = eaf.employeeid
)
;

Ms-Access: counting from 2 tables

I have two tables in a Database
and
I need to retrieve the number of staff per manager in the following format
I've been trying to adapt an answer to another question
SELECT bankNo AS "Bank Number",
COUNT (*) AS "Total Branches"
FROM BankBranch
GROUP BY bankNo
As
SELECT COUNT (*) AS StaffCount ,
Employee.Name AS Name
FROM Employee, Stafflink
GROUP BY Name
As I look at the Group BY I'm thinking I should be grouping by The ManID in the Stafflink Table.
My output with this query looks like this
So it is counting correctly but as you can see it's far off the output I need to get.
Any advice would be appreciated.
You need to join the Employee and Stafflink tables. It appears that your FROM clause should look like this:
FROM Employee INNER JOIN StaffLink ON Employee.ID = StaffLink.ManID
You have to join the Eployee table twice to get the summary of employees under manager
select count(*) as StaffCount,Manager.Name
from Employee join Stafflink on employee.Id = StaffLink.EmpId
join Employee as Manager on StaffLink.ManId = Manager.Id
Group by Manager.Name
The answers that advise you on how to join are correct, assuming that you want to learn how to use SQL in MS Access. But there is a way to accomplish the same thing using the ACCESS GUI for designing queries, and this involves a shorter learning curve than learning SQL.
The key to using the GUI when more than one table is involved is to realize that you have to define the relationships between tables in the relationship manager. Once you do that, designing the query you are after is a piece of cake, just point and click.
The tricky thing in your case is that there are two relationships between the two tables. One relationship links EmpId to ID and the other links ManId to ID.
If, however, you want to learn SQL, then this shortcut will be a digression.
If you don't specify a join between the tables, a so called Cartesian product will be built, i.e., each record from one table will be paired with every record from the other table. If you have 7 records in one table and 10 in the other you will get 70 pairs (i.e. rows) before grouping. This explains why you are getting a count of 7 per manager name.
Besides joining the tables, I would suggest you to group on the manager id instead of the manager name. The manager id is known to be unique per manager, but not the name. This then requires you to either group on the name in addition, because the name is in the select list or to apply an aggregate function on the name. Each additional grouping slows down the query; therefore I prefer the aggregate function.
SELECT
COUNT(*) AS StaffCount,
FIRST(Manager.Name) AS ManagerName
FROM
Stafflink
INNER JOIN Employee AS Manager
ON StaffLink.ManId = Manager.Id
GROUP BY
StaffLink.ManId
I don't know if it makes a performance difference, but I prefer to group on StaffLink.ManId than on Employee.Id, since StaffLink is the main table here and Employee is just used as lookup table in this query.

Needing 2 different ID's from the same ID Table

I am pulling reports for my company and am needing to pull a specific report that I am having trouble with. We are using SQL Server 2012 and I am pulling the SQL reports.
What I need is to pull a simple report:
Group Name, List of Members in the group; Supervisor of the group.
However, the problem is that the supervisor as well as the members and the group name all come from one table in order to get the relevant information. Currently here is my SQL code below:
Use DATABASE
go
-- This is the select portion deciding the columns needed.
select
C.group_name
,C2.first_name
,C2.last_name
-- These are the tables that the query is pulling from.
FROM db..groups AS G
LEFT OUTER JOIN db..contact AS C
ON G.group_id=C.contact_id
INNER JOIN db..contact AS C2
ON G.member=C2.contact_id
go
This pulls the first portion:
The group name, then the first name of a member in that group, and then the last name of a member in that group.
However, I am having trouble getting the supervisor portion. This portion uses the table db.contact under the column supervisor_id as a foreign key. The supervisor_id uses the same unique id as the normal contact_id, but in the same table. Some contact_ids have supervisor_id's that are other contact_id's from the same table, hence the foreign key.
How can I make it so I can get the contact_id that is equal to the supervisor_id of the contact_id that is equal to the group_id?
Taking a quick stab at this while we wait for details
You know you need groups and I'm assuming you don't care about Groups that have no members. Thus Groups INNER JOINed to Contact. This generates your direct group membership. To get the supervisor, you then need to factor in the Supervisor on the specific Contact row.
You might not have a boss, or your boss might be yourself. It's always interesting to see how various HR systems record this. In my example, I'm assuming the head reports to no one instead of themselves.
SELECT
G.group_name
, C.first_name
, C.last_name
-- this may produce nulls depending on outer vs inner join below
, CS.first_name AS supervisor_first_name
, CS.last_name AS supervisor_last_name
FROM
dbo.Groups AS G
INNER JOIN
dbo.Contact AS C
ON C.contact_id = G.member
LEFT OUTER JOIN
dbo.Contact AS CS
ON CS.contact_id = C.supervisor_id;
Depending on how exactly you wanted that data reported, there are various tricks we could use to report that data. In particular, GROUPING SETS might come in handy.
SQLFiddle

sql: how to query foreign key not based upon ID

My table IncomingLetter has a foreign key to a table Department, which has an ID and a column Short_Name.
I'm using this query to count the incoming letters assigned to a department.
SELECT COUNT(DocumentNumber) AS TotalNumberIncomingLetters
FROM IncomingLetter
WHERE Assigned_To_Department=1;
Whereas this works I want to make a query based upon the short name and not based upon the ID.
SELECT COUNT(DocumentNumber) AS TotalNumberIncomingLetters
FROM IncomingLetter
WHERE Assigned_To_Department.Short_Name="My Department Name";
This does not work, whereas I found examples that are using this syntax. However, it is probably important to notice, that I m using this query in MS access.
You should use
SELECT COUNT(il.DocumentNumber) AS TotalNumberIncomingLetters
FROM IncomingLetter il
INNER JOIN Department d on d.ID = il.Assigned_To_Department
WHERE d.Short_Name="My Department Name";
The "My Department Name" text is actually stored in the Departments table, and only the number (1) is stored in the IncomingLetter table, in the field Assigned_To_Department.
Asking for Assigned_To_Department.Short_Name basically asks the number 1 to get it's Short_Name field, that does not make sense.
You need to tell the database engine two things in these scenarios:
which tables are connected - IncomingLetter and Departments in this case (the inner join part)
how they are connected - by setting their Assigned_To_Department and ID fields respecively (the on ... part