Return records where search data does not exist - sql

I have created a view containing data from tables employees and courses. Using this view i can easily find which courses every employee has been on, I however would like to know (depending on employees job title) what compulsory course they haven't been on. For example, all new employees with the job title 'cle' are required to go on a data protection course ('data'). How do I return all employees with job title 'cle' that haven't been on the 'data' course?
I think I need something similar to '!= 'data'' where it actually returns all employees who have a record of 'cle' but don't have a record of 'data' against them and disregards all the others.
I hope someone understands this?

Your title suggests the answer, use NOT EXISTS:
SELECT e.*
FROM dbo.Employee e
WHERE [job title] = 'cle'
AND NOT EXISTS
(
SELECT 1 FROM dbo.Courses c
WHERE c.EmployeeID = e.EmployeeID
AND c.ProtectionCourse = 'data'
)
Note that i've presumed all table and column. But you should get the idea.

Try something like this:
SELECT
*
FROM
employee e
LEFT JOIN
courses c
ON
e.employeeID = c.employeeID and
c.courseType = 'data'
WHERE
e.jobTitle = 'cle' and
c.employeeID is null
You will need to check column names etc, as you haven't given us the table structure, but you get the idea.

Related

How to join a table with itself?

I want to ask how can I join the table with itself, using the same column twice, and giving each a different WHERE to sort out. I have an Employees table that contain Employee_name, job_id and manager_id. I want it to get the name of all the managers through the job_id on one column, and then all the employees that they manage on the other column. This is what I came up with :
SELECT A.LAST_NAME AS MANAGER_NAME, B.LAST_NAME AS EMPLOYEES_NAME
FROM EMPLOYEES A
RIGHT JOIN EMPLOYEES B
ON A.EMPLOYEE_ID = B.EMPLOYEE_ID
WHERE A.JOB_ID LIKE '%MAN%'
AND A.MANAGER_ID = B.MANAGER_ID
This simply put the manager name on both columns, and I don't have another idea on how to do this. Any help would be greatly appreciated, thank you so much.
You join condition needs to change, the WHERE criteria should be moved to the ON clause, and also I would use a left join here:
SELECT emp1.LAST_NAME AS EMPLOYEES_NAME, COALESCE(emp2.LAST_NAME, 'NA') AS MANAGER_NAME
FROM EMPLOYEES emp1
LEFT JOIN EMPLOYEES emp2
ON emp2.EMPLOYEE_ID = emp1.MANAGER_ID AND emp2.JOB_ID LIKE '%MAN%';
It is unclear whether you even need the check on the JOB_ID, but in any case I use COALESCE above to display NA as the manager name for any employee not having a manager.

Is there a way to provide for an exception to a NOT EXISTS clause?

I have a query (that works) with a NOT EXISTS statement that all the names returned cannot be an internal employee. However, I have a record with an external person that has the same name as an internal employee. This record is being excluded but I need it.
I have no clue how to write an exception to my NOT EXISTS to pull in this person.
Sample code:
AND NOT EXISTS(SELECT 1 from dbo.EmployeeTable e
INNER JOIN dbo.ResourceTable r on e.empID = r.employeeID
WHERE r.TypeID = 2 --this is internal employees
AND e.EmployeeName = x.UserName --this user name comes from a join earlier on in my code.)
I need to somehow add code to say "except when x.UserName = [John Doe]"
Can someone out there please help me? Thanks in advance.
You need to do a check based on id, not EmployeeName. Something like:
AND NOT EXISTS(SELECT 1 from dbo.ResourceTable r
WHERE r.TypeID = 2 --this is internal employees
AND r.employeeID = x.UserId
)

Oracle SQL, Can't Figure Out Left Join

I have 2 tables:
Person table with column person_id
Employee table with columns emp_type = full or part
I need a query that returns everyone in Person, but exclude full time employees. What I'm struggling with is not all Persons are necessarily in Employee table.
Can someone help me out? Thanks!
You can try code below, but you didn't give us your tables structure so I can only guess what do you mean here.
select *
from person p
left join employee e on p.person_id = e.person_id
where p.emp_type <> 'full'
Since you didn't post your full query or table structure information, you'll need to make adjustments. But using a not exists clause is probably the most straight forward way of doing this.
select p.*
from person p
where not exists (select null
from employee e
where e.person_id = p.person_id
and e.emp_type = 'full')

How to select records with no matches in the foreign table (Left outer join)

I have one table that holds my ressources:
Ressource | Ressource-ID
And a table that holds the associations
Ressource-ID | Employee-ID
How to select the ressources of an Employee that are available, i.e. not in the association table?
I've tried this, but it's not working:
select r.ress, r.ress_id
FROM Ressource r
LEFT outer JOIN Ressource_Employee_Association a ON r.ress_id = a.ress_id
WHERE a.emp_id = 'ID00163efea66b' and a.ress_id IS NULL
Any ideas?
Thanks
Thomas
After writing my above comments, and looking at the proposed solutions: I think I've got some more understanding of what you are trying to do.
Assuming you have unlimited quantity of resources in your resources table, you want to select the un-assigned resources per employee (based on their non-existence for any specific employee in the resource association table).
In order to accomplish this (and get a comprehensive list of employees) you'll need a 3rd table, in order to reference the complete list of employees. You'll also need to CROSS JOIN all of the resources onto the list of employees (assuming every employee has access to every resource), then you LEFT JOIN (LEFT OUTER JOIN whatever) your association list onto the query where the resource_id and employee_id match the resource_id in the resources table, and the employee_id in the employees table (respectively). THEN you add your where clause that filters out all the records that assign an employee to a resource. This leaves you with the resources that are available to the employee, which they also do not have signed out. This is convoluted to say, so hopefully the query sheds more light:
SELECT e.employee_id, e.employee, r.res_id, r.res
FROM employees e
CROSS JOIN resources r
LEFT JOIN assigned_resources ar
ON ar.employee_id = e.employee_id AND r.res_id = ar.res_id
WHERE ar.res_id IS NULL
If you don't have an employees table, you can accomplish the same by using the assigned resources table, but you will be limited to selecting employees who already have some resources allocated. You'll need to add a GROUP BY query because of the possible existence of multiple employee definitions in the association table. Here's what that query would look like:
SELECT e.employee_id, r.res_id, r.res
FROM assigned_resources e
CROSS JOIN resources r
LEFT JOIN assigned_resources ar
ON ar.employee_id = e.employee_id AND r.res_id = ar.res_id
WHERE ar.res_id IS NULL
GROUP BY e.employee_id, r.res_id
Does this work?
select r.ress, r.ress_id
from resource r
where not exists
(
select 1 from ressource_emplyee_association a
where a.emp_id = '...' and a.ress_id = r.ress_id
)
EDIT
Before that I had the following, but changed it according to the comments below:
select r.ress, r.ress_id
from resource r
where not exists
(
select top 1 1 from ressource_emplyee_association a
where a.emp_id = '...' and a.ress_id = r.ress_id
)
The WHERE clause is applied after the LEFT JOIN. This means that you are currently trying to get results where there is NO matching record in Ressource_Employee_Association, but where the emp_id equals 'ID00163efea66b'.
But if there is no matching record, how can emp_id be anything other than NULL?
One option is to move part of the WHERE clause into the join...
SELECT
r.ress, r.ress_id
FROM
Ressource r
LEFT OUTER JOIN
Ressource_Employee_Association a
ON r.ress_id = a.ress_id
AND a.emp_id = 'ID00163efea66b'
WHERE
a.ress_id IS NULL
This will list all resources that are not associated to employee 'ID00163efea66b'.
EDIT
Your comment implies that what you want is...
- A view listing all employees
- For each employee list each resource that they DON'T have
This requires an extra table listing all of your employees.
SELECT
*
FROM
Employee
CROSS JOIN
Ressource
WHERE
NOT EXISTS (SELECT * FROM Ressource_Employee_Association
WHERE emp_id = Employee.id
AND ress_id = Ressource.id)
SELECT *
FROM Ressource
WHERE ress_id IN (
SELECT ress_id,
FROM Ressource
MINUS
SELECT ress_id
FROM Ressource_Employee_Association
WHERE emp_id = 'ID00163efea66b'
);

sql show current instance

here is the assignment that I had to face:
List the employees who have transferred between departments during their employment. You should show their current department, and the date when the transferred to the current department. This is a pretty tough query. My solution was to use a subquery to determine which employees have been in more than one department, and use that result in the base query.
The problem that I don't know how to display the employee who have been transfered and only once. The way to tell if the employee has been transfered is if in the EmployeeDepartmentHistory table, the employee id has been in more than one record (i.e. employeeID 1 is in both record 1 and record 2 because the person has been in two departments). How would I go about this? Here's what I have as of now:
SELECT EmployeeDepartmentHistory.EmployeeID,Person.Contact.FirstName, Person.Contact.LastName, Department.Name
From HumanResources.Department INNER JOIN
HumanResources.EmployeeDepartmentHistory ON
HumanResources.Department.DepartmentID =
HumanResources.EmployeeDepartmentHistory.DepartmentID INNER JOIN
HumanResources.Employee ON HumanResources.EmployeeDepartmentHistory.EmployeeID
= HumanResources.Employee.EmployeeID INNER JOIN
Person.Contact ON HumanResources.Employee.ContactID = Person.Contact.ContactID
WHERE EmployeeDepartmentHistory.EmployeeID=(SELECT COUNT(HumanResources.EmployeeDepartmentHistory.EmployeeID)
FROM HumanResources.EmployeeDepartmentHistory
WHERE EmployeeDepartmentHistory.EmployeeID = Employee.EmployeeID
Group by EmployeeDepartmentHistory.EmployeeID)
Not sure if I get the requirement correct. Would you want to try HAVING clause at the end?
That is:
HAVING COUNT(EmployeeDepartmentHistory.EmployeeID) = 2
(2 - assuming that the EmployeeDepartmentHistory will contain the current department as well)