For ALL queries in SQL , Table division - mysql

I have a schema with three tables:
Project (project_id,proj_name,chief_arch)
Employee (emp_id,emp_name)
Assigned-to (project_id,emp_id)
I have created all tables with data on http://sqlfiddle.com/#!9/3f21e
You can view the all data (select * ...) on http://sqlfiddle.com/#!9/3f21e/1
Please first view the tables and data on SQLFIDDLE.
I have an existing query to get employee names who work on at least one project where employee 107 also worked:
select EMP_NAME from employee natural join `assigned-to`
WHERE EMP_ID<>'107' AND
PROJECT_ID IN(
SELECT PROJECT_ID FROM `assigned-to`
WHERE EMP_ID='107'
)
GROUP BY EMP_NAME;
SQLFiddle
But now I need to solve a slightly different problem. I need the employee names who on work on ALL projects that employee 107 works on.
How can I write a query for this problem?

Try this:
SELECT EMP_NAME
FROM EMPLOYEE NATURAL JOIN `ASSIGNED-TO`
WHERE EMP_ID<>'107' AND
PROJECT_ID IN (
SELECT PROJECT_ID FROM `ASSIGNED-TO`
WHERE EMP_ID='107'
)
GROUP BY EMP_NAME
HAVING COUNT(*)=(
SELECT COUNT(*)
FROM `ASSIGNED-TO`
WHERE EMP_ID='107'
);
See it run on SQL Fiddle.

You can do this by counting the projects other employees in common with the employee and then selecting only those where the count exactly matches the original employees count.
SELECT EMP_ID FROM `ASSIGNED-TO` WHERE PROJECT_ID IN
(SELECT PROJECT_ID FROM `ASSIGNED-TO` WHERE EMP_ID = '107')
AND EMP_ID <> '107'
GROUP BY EMP_ID
HAVING COUNT(*) = (SELECT COUNT(*) FROM `ASSIGNED-TO` WHERE EMP_ID = '107')

This will work too. I want to validate if the project id in assigned-to is found in project table.
select e.emp_name
from employee e
natural join `assigned-to` a
where emp_id <> 107
and a.project_id in (
select project_id
from (
select emp_id, project_id
from employee natural join `assigned-to` natural join project
where emp_id = 107 ) t
)
group by e.emp_id
having count(project_id) = (select count(project_id) from `assigned-to` where emp_id = 107)

Related

Emulating row_number in MySQL 5.7 with date condition

I am working in MySQL 5.7.34 and I have the following:
create table employees (
employee_id int not null auto_increment,
first_name varchar(100) not null,
last_name varchar(100) not null,
primary key (employee_id)
);
create table documents (
document_id int not null auto_increment,
title varchar(100) not null,
last_modified datetime not null,
employee_id int not null,
primary key (document_id),
foreign key (employee_id) references employees(employee_id)
);
I have created a db-fiddle here. The columns shown in the fiddle are only a few, but in the real data set, the documents table will have 20 or more columns and all need to show.
SQL Query:
-- simply query
select e.employee_id,
e.first_name,
e.last_name,
d.title
from employees e
inner join documents d on e.employee_id = d.employee_id;
Current Output:
employee_id
first_name
last_name
title
1
John
Doe
JD_Doc_Updated
1
John
Doe
JD_Doc
2
Mike
Anderson
MA_Doc
Desired Output:
For each employee, I want to grab the most recent document only. In MS SQL Server (and recent versions of MySQL), I could use something like ROW_NUMBER() OVER(PARTITION BY d.employee_id ORDER BY d.last_modified DESC) AS num
employee_id
first_name
last_name
title
1
John
Doe
JD_Doc_Updated
2
Mike
Anderson
MA_Doc
I am not sure how to achieve the same here.
I had a read of this question, and I don't think the problem here is the same, that other question deals with data on the same table and isn't dealing with datetime as the data type. Unless I'm missing something?
A pretty simple method uses a correlated subquery:
select e.employee_id, e.first_name, e.last_name,
d.title
from employees e join
documents d
on e.employee_id = d.employee_id
where d.last_modified = (select max(d2.last_modified)
from documents d2
where d2.employee_id = d.employee_id
);
For performance, you want an index on documents(employee_id, last_modified).
Here is a db-fiddle.
You are using an old MySQL version which doesn't feature window functions. So, to get the latest document per employee you need two steps:
get the maximum document ID or date
get the row belonging to that ID or date
Let's say "latest document" refers to the one that was last modified:
select *
from employees e
join documents d
on d.employee_id = e.employee_id
and (d.employee_id, d.last_modified) in
(
select employee_id, max(last_modified)
from documents
group by employee_id
)
order by e.employee_id;
The IN clause ensures that the joined document is in the set of latest documents. There are several other ways to write this. You can replace the IN clause with a correlated clause to refer to the employee's ID. Or you could join the tables and have a criteria that NOT EXISTS a newer document. Or you could upgrade to MySQL 8 :-)
You can use the method. The first method is the same as row-number in MSSQL
select employee_id,first_name,last_name,title
from
(select *,
#row_number:=CASE
WHEN #empl_no = employee_id
THEN
#row_number + 1
ELSE
1
END AS num,
#empl_no := employee_id EmplNumber
from
(select e.employee_id,
e.first_name,
e.last_name,
d.title
from employees e
inner join documents d on e.employee_id = d.employee_id
order by d.last_modified desc) t1,(SELECT #empl_no :=0,#row_number:=0) as t2) T
where num = 1
order by employee_id
or you can use group by then select max last_modified. finally join the documents and fetch title column.
select t1.employee_id,t1.first_name,t1.last_name,title,t2.title
from
(select e.employee_id,
max(e.first_name) as first_name,
max(e.last_name) as last_name,
max(d.last_modified ) as last_modified
from employees e
inner join documents d on e.employee_id = d.employee_id
group by e.employee_id) t1
join documents t2 on t1.last_modified = t2.last_modified

How to handle SQL subqueries with sums

I am practicing queries on an example database in MySQL.
I have an employee table with a primary key of emp_id.
I have a works_with table with a composite key of emp_id and client_id. It also has a column of total_sales.
I am trying to write a query that returns the name of any employee who has sold over 100,000 total.
I was able to return the employee id and total for sums over 100,000 like so:
SELECT SUM(total_sales) AS total_sales, emp_id
FROM works_with
WHERE total_sales > 100000
GROUP BY emp_id;
But I am unsure how to use this to also get employee name. I have tried nested queries but with no luck. For example when I try this:
SELECT first_name, last_name
FROM employee
WHERE emp_id IN (
SELECT SUM(total_sales) AS total_sales, emp_id
FROM works_with WHERE total_sales > 100000
GROUP BY emp_id
)
I get Error 1241: Operand should contain 1 column(s). I believe this is because I am selecting two columns in the nested query? So how would I handle this problem?
Just join:
select sum(w.total_sales) as total_sales, e.first_name, e.lastnmae
from works_with w
inner join employee e on e.emp_id = w.emp_id
group by e.emp_id
having sum(w.total_sales) > 10000;
Note that I used a having clause rather than the where clause: presumably, you want to sum all sales of each employee, and filter on that result. Your original queried sums only individual values that are greater than 100000.
Adding to GMB's solution.
Take your existing Select and wrap it in a Derived Table/CTE:
SELECT e.first_name, e.last_name, big_sales.total_sales
FROM employee as e
join
(
SELECT SUM(total_sales) AS total_sales, emp_id
FROM works_with
GROUP BY emp_id
HAVING total_sales > 100000
) as big_sales
on e.emp_id = big_sales.emp_id
Now you can show the total_sales plus employee details. Additionally this should be more efficient, because you aggregate & filter before the join.
If you only need to show the employee you can use a SubQuery (like the one you tried), but it must return a single column, i.e. remove the SUM from the Select list:
SELECT first_name, last_name
FROM employee
WHERE emp_id IN (
SELECT emp_id -- , SUM(total_sales) AS total_sales
FROM works_with
GROUP BY emp_id
HAVING SUM(total_sales) > 100000
)

SQL : select record from table 1 which are not in table 2

I have two database tables: "employee" and "department".
employee's table has two columns Primary_key id and emp_name
and in department table emp_id, and dep_name.
There's supposed to be a relation b/w table as a foreign key,
but for some reason this relationship is virtual
Data in tables like
employee
id emp_name
1 'abc'
2 'efg'
4 'hij'
department
emp_id dept_name
1 'it'
2 'engineering'
3 'management'
5 'process'
want to select all records from the department table which are not in the employee table.
one solution is
select d.*
from department
where d.id not in(select id from employee);
is there any better optimized way?
You can use LEFT JOIN:
SELECT d.*
FROM department d
LEFT JOIN employee e
ON d.emp_id = e.id
WHERE e.id IS NULL;
You should compare execution plans to check which query has the best performance.
Using in, as you did, is fine. Using exists, however, may perform a tad faster:
SELECT *
FROM department d
WHERE NOT EXISTS (SELECT *
FROM employee e
WHERE d.emp_id = e.id)

How to write an SQL query going through foreign key

I am struggling to do the following:
I have two tables:
Table Employee; columns Employee_ID and Employee_Name. Employee_ID is primary key
Table Emp_Salary; columns Employee_Salary which is a foreign key linked to employee_ID.
I am looking to get the salary for an employee whose ID is 40
so for example
Select employee_salary from EMP_Salary where Employee_ID equals 40;
unfortunately this does not work; any reason why? with the foreign> primary key relation this should be fine?
Another query that I cant get to work is:
Select employee_Salary from EMP_Salary where Employee_name equals "Dan"
Any insight appreciated. Thanks!
Because there is nothing called equals. You may try this:
Select employee_salary from EMP_Salary where Employee_ID =40;
Similarly you may try this:
Select employee_Salary from EMP_Salary where Employee_name ="Dan"
You may also consider to look for JOINS in MYSQL if you want to combine the two queries and get the results.
Something like this:
select EMPLOYEE_SALARY
from EMPLOYEE E1 inner join EMP_SALARY E2 on E2.EMPLOYEE_ID = E1.EMPLOYEE_ID
where E1.EMPLOYEE_ID = 40;
or
select EMPLOYEE_SALARY
from EMPLOYEE E1 inner join EMP_SALARY E2 on E2.EMPLOYEE_ID= E1.EMPLOYEE_ID
where E1.Employee_Name= 'Dan';
Use = instead of equals:
Select employee_salary from EMP_Salary where Employee_ID = 40;
To get the salary by name you would JOIN both tables on Employee_ID and then put the Employee_name in the WHERE clause:
Select e.employee_salary
FROM EMP_Salary es
INNER JOIN employee e ON e.Employee_ID = es.Employee_ID
WHERE e.Employee_name = 'Dan';
have you tried using INNER JOIN?
For example:
select EMPLOYEE_ID, EMPLOYEE_NAME, EMPLOYEE_SALARY
from EMPLOYEE A
inner join EMP_SALARY B
on B.ID_EMPLOYEE = A.ID_EMPLOYEE
where ID_EMPLOYEE = --numberhere

SQL Query - How to find out how which employees worked at more than one store

I have a relationship table such that it has
employeeID | storeID
What would be the query to find out which employees worked at more than one store?
SELECT employeeID WHERE ???
And possibly also list each different stores just once per employee...
Use group by and having, as in:
select employeeID, count(*) from table group by employeeID having count(distinct storeID) > 1
This will give you the employees working at more than one store. Use this as a subquery to list the stores for each such employee.
you can try -
select distinct employeeID,StoreID from table1
where storeID in
(
select storeID from table1 group by storeID having count(distinct employeeID) >1
)
cor storing count and showing store ID also in one query you can use following query..
select a.employeeID,a.storeID,b.cnt
from table1 a,
(select employeeID,count(*) cnt
from table1
group by employeeID
having count(distinct storeID) >1) b
where a.employeID=b.employeeid