Ms-Access: counting from 2 tables - sql

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.

Related

How To Select data from multiple tables with grouping for duplicates

I have Two Tables, one with Employees Details and another with vacations taken by them in different years.Please check this image for the tables
Here as you'll find out in the vacation table, for the same employee with same employeeId and in sam year different vacation days are mentioned. Like John Smith in 2011 have two entries one with 10 vacation and one with 3 vacation. I want my query to return a single row with vacations mentioned as 13.
I tried the following query but no luck
SELECT Employee_Details.EmployeeId, Employee_Details.EmployeeName, Employees_Vacation.Year, Employees_Vacation.Vacation, Employee_Details.Department
FROM Employees_Vacation INNER JOIN Employee_Details ON Employees_Vacation.EmployeeId=Employee_Details.EmployeeId group by Employee_Details.EmployeeId ORDER BY Employee_Details.EmployeeName, Employees_Vacation.Year ;
if i understood you right, i think this may help you
select sum(vacation) as sum, ev.year, ed.EmployeeName from employee_Details as ed inner join employee_Vacation as ev
on ed.employeeID = ev.employeeID
group by ev.year, ed.EmployeeName
A lot here will depend on the sql engine you are using, however there are some things that will apply regardless of the engine to consider:
Your current GROUP BY clause is grouping only by employeeId - from the question text it seems like you are instead looking for results grouped by employee AND vacation year
Your projection (SELECT statement) currently isn't actually aggregating anything - it's just projecting a bunch of fields. On some db engines, this actually isn't even allowed (SQL Server for example will only allow grouped or aggregated columns in the projection). Again, from the question text it seems like you are looking for the SUM of vacation days per employee and year.
Taking these into account and assuming the assumptions made are accurate, something like the following should work in most/all modern RDBMS's:
SELECT Employee_Details.EmployeeId,
Employee_Details.EmployeeName,
Employees_Vacation.Year,
SUM(Employees_Vacation.Vacation) AS TotalVacationDays,
Employee_Details.Department
FROM Employees_Vacation
INNER JOIN Employee_Details
ON Employees_Vacation.EmployeeId = Employee_Details.EmployeeId
GROUP BY
Employee_Details.EmployeeId, Employee_Details.EmployeeName,
Employees_Vacation.Year, Employee_Details.Department
ORDER BY
Employee_Details.EmployeeName,
Employee_Details.EmployeeId,
Employees_Vacation.Year;
You may be able to get away with fewer grouping clauses in some engines (MySql for example). Additionally I added an EmployeeId to the order by clause to ensure records for the same employee remain together in the results (for employees with the same names for example).

SQL Query Involving Finding Most Frequent Tuple Value in Column

I have the following relations:
teaches(ID,course_id,sec_id,semester,year)
instructor(ID,name,dept_name,salary)
I am trying to express the following as an SQL query:
Find the ID and name of the instructor who has taught the most courses(i.e has the most tuples in teaches).
My Query
select ID, name
from teaches
natural join instructor
group by ID
order by count(*) desc
I know this isn't correct, but I feel like I'm on the right track. In order to answer the question, you need to work with both relations, hence the natural join operation is required. Since the question asks for the instructor that has taught the most courses, that tells me that we are trying to count the number of times each instructor ID appears in the teaches relation. From what I understand, we are looking to count distinct instructor IDs, hence the group by command is needed.
Don't use natural joins: all they do is rely on column names to decide which columns relate across tables (they don't check for foreign keys constraints or the-like, as you would thought). This is unreliable by nature.
You can use a regular inner join:
select i.id, i.name
from teaches t
inner join instructor i on i.id = t.sec_id
group by i.id, i.name
order by count(*) desc
limit 1
Notes:
this assumes that column teaches.sec_id relates to instructor.id (I cannot see which other column could be used)
I added a limit clause to the query since you stated that you want the top instructor - the syntax may vary across databases
always prefix the column names with the table they belong to, to make the query unambiguous and easier to understand
it is a good practice (and a requirement in many databases) that in an aggregate query all non-aggregared columns listed in the select clause should appear in the group by clause; I added the instructur name to your group by clause

Display all results queried on PostgreSQL where the JOINING value is missing

I have two tables on PostgreSQL, namely: employees and employers.
There is data in each of these tables.
Columns in employees: employee_id, employee_name, employer_id.
Columns in employers: employer_id, employer_name.
I want to display all employee_name's that don't have an associating employer_name.
I used the below query:
SELECT DISTINCT a.employee_name, b.employer_name
FROM employees a
NATURAL JOIN employers b
WHERE a.employee_name LIKE 'Jack';
NB!
I have also tried adding in the below to my query:
COALESCE(b.employer_name, '') = ''
Problem:
If there is no record in the employer table containing the associating employee_id value, the query returns nothing all. I am assuming this is because there is nothing for the two tables to join on?... But I would like to at least find all employees that don't have an employer. I would ideally like the employer_name value in my result to either return: blank/''/NULL.
Your assistance is greatly appreciated.
select employees.employee_name ,employers.employer_name
from employees
left join employers
on employees.employee_id = employees.employee_id
where employers.employer_name is NULL
Use a LEFT JOIN:
SELECT a.employee_name,
COALESCE(b.employer_name, 'NA') AS employer_name
FROM employees a
LEFT JOIN employers b
ON a.employer_id = b.employer_id
WHERE b.employer_id IS NULL
Your current query has several problems, first of which is that you are using a NATURAL JOIN, which should behave link an INNER JOIN, discarding employee records which do not match to any employer. Instead, by using LEFT JOIN, we can retain all employee records, regardless of whether or not they have an employer. The WHERE clause checks for NULL in the joined table, with NULL indicating that the employee did not match to any employer.
Your table name employees table is a little odd, IMO. There is a design 'rule of thumb' that says a table models either an entity or the relationship between entities but not both. To demonstrate this oddness, consider the relational operation is
employees MINUS employers
...which suggests something is off.
Another symptom of this design problem is that the employer_id in the employees table must have some kind of placeholder to represent the predicate employee has no employer, possibly a null (and nulls are to be avoided, IMO).
I suggest you fix this design by introducing a third table to model the relationship between an employee and their employer. Herein lies another design problem: shouldn't this new table be named employees? In other words, isn't the very definition of an employee a person who has an employer?
Consider this design instead:
People: person_id, person_name
Employers: employer_id, employer_name
Employees: employer_id, person_id
To find the names of people who are not employees in pseudo-relational notation:
People MINUS Employees { person_name }
SQL is quite a bit more verbose:
SELECT DISTINCT person_name
FROM People
NATURAL JOIN
( SELECT person_id FROM People
EXCEPT
SELECT person_id FROM Employees ) t;
Note your original query needlessly uses range variables a and b. One of the benefits of NATURAL JOIN is the elminiation of duplicate column names. Range variables with NATURAL JOIN always looks odd to me!

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

Need help in understanding JOINS in SQL

I was asked the below SQL question in an interview. Kindly explain how it works and what join it is.
Q: There are two tables: table emp contains 10 rows and table department contains 12 rows.
Select * from emp,department;
What is the result and what join it is?
It would return the Cartesian Product of the two tables, meaning that every combination of emp and department would be included in the result.
I believe that the next question would be:
Blockquote
How do you show the correct department for each employee?
That is, show only the combination of emp and department where the employee belongs to the department.
This can be done by:
SELECT * FROM emp LEFT JOIN department ON emp.department_id=department.id;
Assuming that emp has a field called department_id, and department has a matching id field (This is quite standard in these type of questions).
The LEFT JOIN means that all items from the left side (emp) will be included, and each employee will be matched with the corresponding department. If no matching department is found, the resulting fields from departments will remain empty. Note that exactly 10 rows will be returned.
To show only the employees with valid department IDs, use JOIN instead of LEFT JOIN. This query will return 0 to 10 rows, depending on the number of matching department ids.
The join you specified is a cross join. It will produce one row for each combination of records in the tables being joined.
I'll let you do the math from there.
This will do a cross join I believe, returning 120 rows. One row for each pair-wise combination of rows from each of the two tables.
All-in-all a fairly useless join most of the time.
You will get all rows from both tables with each row joined together.
This is known as a Cartesian join and is very bad.
You will get a total of 120 rows.
This is also the old implied syntax (18 yeasr out of date) and accidental cross joins are a common problem with this syntax. One should never use it. Explict joins are a better choice. I would have also mentioned this in an interview and explained why. I also would not have taken the job if they actually used crappy syntax like this because it's very use shows me the database is very likely to be poorly designed.