MS ACCESS Query with junction table, for all items in one table, but not in another - sql

To create a many-to-many relationship, I have three tables:
tblEmployee, contains employees
tlkpPermission, contains 11 different possible permission groups an employee can be part of
tblEmployeeXPermission, combines the EmployeeID with one or more PermissionID
What I’m trying to create is a query that shows what permission groups an employee is NOT part of.
So, if EmployeeID 12345 is associated with PermissionID 1,2,3,4,5, but NOT 6,7,8,9,10,11 (in the EmployeeXPermission table) then I want the query to show EmployeeID 12345 is not part of PermissionID 6,7,8,9,10,11.
Of all the JOINs and query options, I can only get a query to show which PermissionIDs an employee is associated with, but not the PermissionIDs the employee is not associated with.
Any help would be appreciated.
Thanks

You need to start with all combinations of employees and permissions, and this type of join is CROSS JOIN, but MsAccess SQL does not have it in the new SQL syntax. You can use the old syntax of listing your tables in the FROM clause, comma separated, and provide the join condition, if any, in the WHERE clause:
SELECT
EmployeeId,
PermissionID
FROM
tblEmployee as E,
tlkpPermission as P
where not exists (
select 1
from tblEmployeeXPermission X
where X.EmployeeId=E.EmployeeId
and X.PermissionId=P.PermissionId
)
Here the part up to the WHERE clause would give you all employee - permission combinations, and the WHERE clause removes those occuring in the tblEmployeeXPermission, leaving you with the ones you want.

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

SQL Remove Duplicate Rows of Data from Query Result

I am still learning the ropes of SQL so I have run into my first obstacle. I am to create an SQL query that retrieves employee.firstname, employee.lastname, dependents.depname, and dependents.birthday from the two tables employees and dependents.
I am only supposed to show an employee if he or she has a dependent.
My primary table (employee; only the first 43 rows): employee table
My secondary table (dependents): dependents table
This is what I have so far:
SELECT
employee.firstname, employee.lastname,
dependents.depname, dependents.birthday
FROM
employee
INNER JOIN
dependents ON employee.id = dependents.empid
This works fine however I run into many duplicate rows of data:
Original Query
This is not the full query result but I think it provides sufficient evidence of my problem.
I used the DISTINCT keyword with my SELECT statement, but it only retrieved a small number of my dependents.
Adding DISTINCT
Have you already any duplicates in one of the tables employee or dependents? The second result looks correct. With select distinct the database removes all duplicates from the result set.

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!

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.

Stored procedure to return multiple records based on multiple conditions

I am new to SQL server, thus looking for some quick help on writing stored procedure:
brief about what I am doing:
employee says he is expert (type) in different domains (industries) and willing to work in countries of choice (mycountries) and my sal (minsal)and my native country (orgcountry)
Employer says he need so and so expert in the his choice of domain (industries) in the countries where openings are there and with sal range.
employee table has lots of records with columns like this:
name, email, myindustries, mycountries, mytype,minsal
employer table has lots of records with columns like:
expertneed, inindustries, incountries, sal-from, sal-to
now when employee logs in, he/she should get all the records of matching employers
when employer logs in, he/she also get all the records of matching employees.
can some one help in writing sp for this? appreciate any help
If you're storing comma-separated ids then you'll need a function to split a string into multiple rows. This is how you would use it:
SELECT DISTINCT employee.name [employee], employer.name [employer]
FROM employee
OUTER APPLY dbo.split(employee.myindustries) myi (industry)
OUTER APPLY dbo.split(employee.mycountries) myc (country)
JOIN employer
OUTER APPLY dbo.split(employer.inindustries) ini (industry)
OUTER APPLY dbo.split(employer.incountries) inc (country)
WHERE employer.expertneed = employee.type
AND ini.inindustries = myi.industry
AND inc.incountries = myc.country
AND employee.minsal BETWEEN employer.[sal-from] AND employer.[sal-to]