Is there a way to provide for an exception to a NOT EXISTS clause? - sql-server-2012

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
)

Related

history gives me too many subquery results

I appreciate your help, I hope I'm able to provide adequate information.
I need to roll back owners associated with quite a few entries in our asset table (one owner got applied to a lot of systems). I have a history table with the information I need, and an employee table that gets wedged in there because for some reason the history table stores names rather than employee_id's.
Source data:
asset_table has asset_id, employee_id, asset_tag
employee_table has employee_id, name
history_table has asset_id, old_name, new_name
update asset_table
set employee_id = (select employee_id
from employee_table
where name like (select old_name
from history_table
where asset_table.asset_id=history_table.asset_id
and new_name like 'tobe replaced'))
However, the subquery turns up more than one result per line.
What am I missing to limit that subquery results?
MSSQL server 2012
I guess you need someone who was the last in history:
update at set
employee_id = e.employee_id
from asset_table at
cross apply
(
select top (1)
h.old_name
from history_table h
where at.asset_id = h.asset_id
and h.new_name = 'tobe replaced'
order by h.<date or id or something> desc
) h
inner join employee_table e
on e.name = h.old_name
TOP 1 will fix "more than one result" problem.
Note, LIKE without % works exactly the same way as =.
You can write update statement is as below by using JOIN instead of sub query, Provide table structure and data for more correct query:
update at set at.employee_id = et.employee_id
from asset_table at
inner join history_table ht on at.asset_id = ht.asset_id
and ht.new_name like '%tobe replaced%'
inner join employee_table et on et.name = ht.old_name
and et.employee_id = at.employee_id

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')

SQL Solving with Subqueries?

I'm a beginner trying to learn and practice SQL with tables based on this schema:
EMPLOYEE - ID, Name
ASSIGNMENT - ID, Country, Start, End
The primary keys are Employee.ID and all four columns shown for ASSIGNMENT; and ASSIGNMENT.ID is a reference to EMPLOYEE.ID. The domain of start and end is in years.
Problem: I'm trying to write a query that will display all the employees (by name) where they were assigned to an assignment in the USA directly after they had completed an assignment in Canada.
This is my current attempt, which fails to compute. I believe I am heading in the correct direction but there are syntactical mistakes.
SELECT
E.Name
FROM
EMPLOYEE E
INNER JOIN
ASSIGNMENT A ON E.ID = A.ID
WHERE
(SELECT End FROM ASSIGNMENT
WHERE Country = 'Canada') = (SELECT Start FROM ASSIGNMENT
WHERE COUNTRY = 'USA')
GROUP BY
E.Name;
Any critique to benefit my understanding of my misconceptions are welcome. My errors are coming from the combination of the subqueries in the WHERE clause
At most one record can be returned by this subquery.
Perhaps someone can show me another way to compute this?
This query is being tested in MS Access since I found it easy to build a database and relationships quickly.
Instead of subqueries use another join and add constraints to the join conditions:
SELECT
E.Name
FROM
EMPLOYEE E
INNER JOIN
ASSIGNMENT A ON ( E.ID = A.ID
AND A.Country = 'Canada' )
INNER JOIN
ASSIGNMENT B ON ( E.ID = B.ID
AND B.Country = 'USA'
AND B.Start = A.End )
GROUP BY
E.Name;
Update
The OP reported an error from MS Access complaining about the composite join condition for the above version. However, you may safely move inner join conditions to the where clause. The interim resultsets will grow, though, since the product of the tables is produced first with less constraints and filtered thereafter (A good query optimizer might avoid the unnecessary generation of records, but I do not know about the capabilities of MS Access in this regard).
SELECT
E.Name
FROM
EMPLOYEE E
INNER JOIN
ASSIGNMENT A ON ( E.ID = A.ID )
INNER JOIN
ASSIGNMENT B ON ( E.ID = B.ID )
WHERE
A.Country = 'Canada'
AND B.Country = 'USA'
AND B.Start = A.End
GROUP BY
E.Name;

Return records where search data does not exist

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.

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'
);