how to write an IN predicate as a NOT IN predicate - mysql

I have the following query --
select ssn, fname, lname
from employee, department
where (dname, dno) in (select dname, dno
from department, employee
where dno=dnumber
and dname <> 'Headquarters'
and dname <> 'Administration');
`
It returns the ssn, fname and lname of employees who do not work in the headquarters or administration department.
I am having trouble reversing the logic and figuring out how to get the same result using "not in". From what I have gathered the "in" predicate goes through the tables and checks to see if it can create tuples of fname, lname and ssn which is based on the department number from the employee table matching the department number in the department table.

You have to invert the test in the subquery's WHERE clause using de Morgan's Law
select ssn, fname, lname
from employee, department
where (dname, dno) not in (
select dname, dno
from department, employee
where dno != dnumber or dname IN ('Headquarters', 'Administration'));

I find that aliasing helps me keep track of things:
SELECT emp.ssn, emp.fname, emp.lname
FROM employee emp, department dep1
WHERE emp.dnumber = dep1.dno /* link the tables here */
AND (dep1.dno NOT IN ( /* subquery filters eligible results */
SELECT dep2.dno
FROM department dep2
WHERE dep2.dname <> 'Headquarters'
AND dep2.dname <> 'Administration')
);
Note: I didn't test this, but it should be close.

Related

OQL and SQL queries. Select all department numbers whose employees have the same salary

So, I have two tables:
EMP {EMP_NO, EMP_SALARY, EMP_DEPT_NO}
DEPT {DEPT_NO, DEPT_MNG}
EMP_NO, DEPT_NO - primary keys, EMP_DEPT_NO - external key to DEPT, DEPT_MNG - external key to EMP.
I need to find all departments where every employee has the same salary.
You can use the COUNT DISTINCT in the HAVING section to achieve that. the COUNT DISTINCT will return how many variations of salary there are in a certain dept.
SELECT DEPT_NO
FROM DEPT JOIN EMP ON DEPT.DEPT_NO=EMP.EMP_DEPT_NO
GROUP BY DEPT_NO
HAVING COUNT(DISTINCT SALARY) =1

Return information about oldest employee in department, if department has more than 20 employees

Don't judge me, I'm new to SQL querying. I got scheme, like the one shown on picture below. So, there are 2 tables, first one Employees contains EmployeeID, FirstName, LastName, DateOfBirth and DepartmentID. The second one is called Department and contains DepartmentID and DepartmentName .
I want to return FirstName, LastName and DepartmentName for the oldest employee from each department containing more than 20 employees.
My solution is the following query :
SELECT FirstName, LastName, DepartmentName
FROM employees
LEFT JOIN department
ON employees.DepartmentID = department.DepartmentID
WHERE (employees.DateOfBirth =
(SELECT MIN(employees.DateOfBirth ) FROM (
SELECT *FROM employees WHERE employees.DepartmentID IN (
SELECT employees.DepartmentID FROM employees GROUP BY DepartmentID HAVING COUNT(*) > 20)));
I think that logic is fine, because inner SELECT statement will return ID's of every department with more than 20 employees, and the outer should return to oldest employee.
The problem that I have is when I try to execute this query, it is returning SQL error every derived table must have it's own alias.
I've tried putting alias on each derived table, but outcome is still the same.
Please, help me with this one.
Also, if someone has different solution, please share it.
Thank You.
Addition which strawberry asked for, Create queries
CREATE TABLE Employees
(
EmployeeID int,
FirstName varchar(10),
LastName varchar(15),
DateOfBirth date,
DeparmentID int
)
CREATE TABLE Department
(
DepartmentID int,
DepartmentName varchar(15)
)
Your query is tricky to read due to inconsistent formatting. So I'll clean it up as follows:
SELECT FirstName, LastName, DepartmentName
FROM employees
LEFT JOIN department
ON employees.DepartmentID = department.DepartmentID
WHERE (employees.DateOfBirth =
(
SELECT MIN(employees.DateOfBirth)
FROM (
SELECT *
FROM employees
WHERE employees.DepartmentID IN (
--Departments with more than 20 employees
SELECT employees.DepartmentID
FROM employees
GROUP BY DepartmentID
HAVING COUNT(*) > 20)
) -- You need an alias here.
-- Also from this point you were missing closing brackets.
Problems with your query:
Obviously the missing alias and closing brackets meant you couldn't even test your query.
Also SELECT MIN(employees.DateOfBirth) returns only a single value. Not a value per department.
So your overall result includes only the oldest employee across all the 'big' departments. (Unless the oldest employee in each department happened to have the same birth date.)
It could also include results from a smaller department if any employee happened to have the same birth date the oldest from the big departments. And that employee needn't even be the oldest in their department!
You also have some inefficiencies by using more sub-queries than necessary.
CTEs (common table expressions) are great at simplifying complex queries. But I don't know if mysql supports them. So this solution still uses sub-queries.
SELECT e.FirstName, e.LastName, d.DepartmentName
FROM employees e -- I prefer short aliases
INNER JOIN (
-- This sub-query returns the earliest birth date within each
-- big department. This needs to be an aliased query so you
-- can join to other tables for your desired columns.
SELECT DepartmentID, MIN(DateOfBirth) AS MinDOB -- Must alias column
FROM employees
WHERE DepartmentID IN (
-- Big departments
SELECT DepartmentID
FROM employees
GROUP BY DepartmentID
HAVING COUNT(*) > 20
)
GROUP BY DepartmentID
) ddob -- Alias Department Date of Birth
-- As a result of inner joining to ddob your employees
-- will be filtered to only those that match the relevant
-- ones identified in the query.
ON e.DepartmentID = ddob.DepartmentID
AND e.DateOfBirth = ddob.MinDOB
INNER JOIN Department d
ON d.DepartmentID = e.DepartmentID
Something to note in the above solution, if 2 employees are tied for being oldest in a department, both will be returned.
This approach is structurally similar to yours, but you could also approach the problem from another direction.
Start out getting oldest employees in ALL departments.
And only at the end filter the result according to department size.
I'll leave that to you to try. I suspect the query would be a little simpler.
The following SQL script correspond to your Class Diagram:
CREATE TABLE Departments (
DepartmentID int AUTO_INCREMENT PRIMARY KEY,
DepartmentName varchar(15)
);
CREATE TABLE Employees (
EmployeeID int AUTO_INCREMENT PRIMARY KEY,
FirstName varchar(10),
LastName varchar(15),
DateOfBirth date,
DepartmentID int,
FOREIGN KEY (DepartmentID) REFERENCES Departments(DepartmentID)
);
Following your classes diagram, there is a department for each employee. So, is the reason of using INNER JOIN. I think the following query do what you want:
SELECT ee.FirstName, ee.LastName, ee.DateOfBirth, t.DepartmentName
FROM
(
SELECT e.DepartmentID, d.DepartmentName, MIN(e.DateOfBirth) AS DateOfBirth
FROM Employees AS e
INNER JOIN Departments AS d ON e.DepartmentID = d.DepartmentID
WHERE e.DepartmentID IN (
SELECT DepartmentID
FROM Employees
GROUP BY DepartmentID HAVING COUNT(DepartmentID) > 20
)
GROUP BY e.DepartmentID
) AS t
INNER JOIN Employees AS ee
WHERE ee.DepartmentID = t.DepartmentID AND ee.DateOfBirth = t.DateOfBirth
Example of output:
FirstName LastName DateOfBirth DepartmentName
fisrt14 last14 02/01/2000 SI
fisrt31 last31 12/01/2003 Finance
You improve its performance!

SQL- Using a value in one tuple to get a value from another one

I have a table called employee which contains the following columns -
Fname, Lname, ssn (primary key), salary, supervisor ssn, departmentname.
Now for each employee in the table belonging to the department 'Research', I want to output his name, salary and the NAME of his supervisor. What I currently have is this.
SELECT fname, salary, superssn from employee where departmentname='Research';
Now this gives me only the ssn of the employee's supervisor, and not the supervisor's name. I know that I have to compare the superssn of an employee with another employee who has the same ssn and get that other employee's name since he's he supervisor, but I'm not sure how to implement this in the same command.
You need a join back to the emplyee table (and you need an alias to reference that table), like this:
SELECT employee.fname, employee.salary, supervisorlist.fname as supervisor
from employee
left join employee as supervisorlist on supervisorlist.ssn = employee.supervisorssn
where employee.departmentname='Research';
You need a self join:
SELECT researcher.fname, researcher.salary, supervisor.fname
FROM employee researcher JOIN employee supervisor ON researcher.superssn = supervisor.ssn
WHERE researcher.departmentname='Research';
This gives you all related pairs from the cross product of employee x employee that match on the supervisor / researcher ssn.
When you want to include researchers without a supervisor, too, then you need a LEFT JOIN.

Is my Relation Algebra correct?

I am trying to understand Relation Algebra for my Database Class and I was wondering if someone here can look at what I've done and help me figure out what is correct and what isnt.
What I am trying to solve is: For each department, retrieve the name and the min and max salary range for every employee in that department.
Table Setup:
Employee(Fname, Minit, Lname, Ssn, Bdate, Address, Sex, Salary, Super_ssn, Dno)
Department(Dname, Dnumber, Mgr_ssn, Mgr_start_date)
SQL:
Select Dname, Min(Salary), Max(Salary)
from EMPLOYEE, DEPARTMENT
Where Dnumber = Dno
Group by Dname
Relational Algebra (Step, with one operand per line):
Salaries ← π(Dno, Salary)[EMPLOYEE]
Departments ← π(Dname, Dnumber)[DEPARTMENT]
SalariesD ← Salaries JOIN(Symbol wont work)(Dno=Dnumber) Departments
Selection ← π(Dname, Min(Salary), Max(Salary))[SalariesD]
Results ← Group-Symbol(Dname)[Selection]
Sorry about the formatting. Please let me know if im on the right track with this.
I think this could be also right......
select a.Fname,Max(a.Salary),Min(a.Salary)
from Employee a
join Department b on a.Dno = b.Dnumber
group by Dname

How to check the count and based on that query

I have a table employees and a table department. Each employee is linked to a department id. The fields of the employee table are
EMPLOYEE
ssn, fname, lname, dept_id
DEPARTMENT
dept_id, dept_name
So my problem is that I need to pick the employee name and last name if there are more than 3 employees in a specific department. I am not sure the best way to do this. Any help will be much appreciated.
The main problem is MySQL dislikes queries with subqueries that access same tables.
SELECT d.dept_name, GROUP_CONCAT( CONCAT(E.FNAME,' ',E.LNAME) ORDER BY E.LNAME
SEPARATOR ',' ),
FROM DEPARTMENT D JOIN EMPLOYEE E USING(dept_id)
GROUP BY dept_id
HAVING COUNT(*) > 3;
This solution would provide a list of employess per row per department.
For instance:
'IT', 'Micky Mouse, Tom Jones'
'HR', 'Emily Bronte'
If you prefer, you can change the separator character used in the employees list with SEPARATOR.
Try
SELECT lname, fname
FROM Employee
WHERE dept_id
IN (
SELECT dept_id
FROM Employee
GROUP BY dept_id
HAVING COUNT(*) > 3)