i have 3 tables, the employee table, educational background and job_title,
i need to join the 3 tables and select the latest year the employee is graduated. the result query i need is listed below.
EMPLOYEE TABLE
|ID | employee_id | Name |
| 1 | 123 | Jewel Brin |
| 2 | 554 | Donna Ferry |
| 3 | 853 | Ricky David |
educational background
|ID | employee_id | School/level | date graduated |
| 1 | 123 | highschool | 2007 |
| 2 | 123 | college | 2011 |
| 3 | 554 | college | 2010 |
| 4 | 554 | masteral | 2013 |
job title
|ID | employee_id | Job description |
| 1 | 123 | Free lancer |
| 2 | 554 | admin assistant |
| 3 | 853 | Support Admin |
i need to select the latest date info of the employee's educational background
the result would be:
result query
|ID | employee_id | Name | Job title | year_graduated | school_institute |
| 1 | 123 | Jewel Brin | Free Lancer | 2011 | college |
| 2 | 554 | Donna Ferry | Admin Assistant | 2013 | masteral |
| 3 | 853 | Ricky David | Support Admin | Null | Null
Forgot to mention, this is MySQL :)
SELECT e.employee_id, Name, `Job description` AS `Job title`, year_graduated, school_institute
FROM `employee` e
INNER JOIN job_title j ON j.employee_id = e.employee_id
LEFT JOIN (SELECT ID, `School/level` AS school_institute, employee_id, `date graduated` AS year_graduated FROM `educational background` b
INNER JOIN (
SELECT ID, MAX(`date graduated`) AS max_date FROM `educational background` GROUP BY employee_id
) b2
ON b2.max_date = b.year_graduated AND b2.ID = b.ID
) bb ON bb.employee_id = e.employee_id
In SQL Server :-
Select *
from
(
Select e.ID,e.employee_ID,e.Name,
b.Level,j.[Job description],
rn = row_number()
over (partition by e.Employee_ID order by [date graduated] desc)
From Employee e
left join Background b
on e.Employee_ID = b.Employee_ID
left join Jobtitle j
on j.Employee_ID = b.Employee_ID
)a
where a.rn=1
SQL FIDDLE
For Mysql you can try this
SELECT DISTINCT
e.employee_id,
e.Name,
j.Job_description,
eb.year_graduated,
eb.level
FROM
EMPLOYEE e
LEFT JOIN
(SELECT
*,
MAX(t.date_graduated) year_graduated
FROM
(SELECT * FROM Background ORDER BY date_graduated DESC) t
GROUP BY employee_id) eb
ON (e.employee_id = eb.employee_id)
LEFT JOIN JobTitle j
ON (e.employee_id = j.employee_id)
See Fiddle Demo
Related
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 3 years ago.
Improve this question
I have problems extracting data from these tables. I have a statistics system where numbers are entered for employees every some days in the month - for the sake of example here are only two months but it will be a full year. I tried a join but I received doubled results + some employee can work on more accounts while others could work on only one...
employees:
+----+-------------+--+
| ID | Name | |
+----+-------------+--+
| 1 | John Doe | |
| 2 | Helen Price | |
| 3 | John Price | |
+----+-------------+--+
Account
+----+---------+--+
| ID | Name | |
+----+---------+--+
| 1 | fisher | |
| 2 | fisher1 | |
+----+---------+--+
January
+----+-------------+---------+----------+----------+----------+
| ID | Employee | Account | firstday | seconday | thirdday |
+----+-------------+---------+----------+----------+----------+
| 1 | John Doe | fisher | 2 | 4 | 6 |
| 2 | Helen Price | fisher | 1 | 1 | 1 |
| 3 | John Price | fisher1 | 1 | 2 | 1 |
| 4 | John Price | fisher | 1 | 1 | 1 |
+----+-------------+---------+----------+----------+----------+
February
+----+-------------+---------+-----------+----------+-----------+
| ID | Employee | Account | fourthday | eightday | secondday |
+----+-------------+---------+-----------+----------+-----------+
| 1 | John Doe | fisher | 1 | 4 | 6 |
| 2 | Helen Price | fisher | 1 | 1 | 2 |
| 3 | John Price | fisher1 | 1 | 2 | 1 |
| 4 | John Price | fisher | 1 | 1 | 1 |
+----+-------------+---------+-----------+----------+-----------+
My question is how can I write a query with the following result:
+----+-------------+---------+-----------------+------------------+--------+--+
| ID | Employee | Account | January(totals) | February(totals) | Totals | |
+----+-------------+---------+-----------------+------------------+--------+--+
| 1 | John Doe | fisher | 12 | 11 | 23 | |
| 2 | Helen Price | fisher | 3 | 4 | 7 | |
| 3 | John Price | fisher | 3 | 3 | 6 | |
+----+-------------+---------+-----------------+------------------+--------+--+
What you're wanting is a pivot-table of aggregate results from the different JOIN tables.
There are a couple of approaches, but one approach is using LEFT JOIN with aggregate subqueries to total your monthly results by each employee and account.
In your sample data, there is no correlation of Employee to Account outside of your Month tables. So the association needs to be created by using another subquery to create the correlation to join the monthly tables on. This will cause an association for every employee and every account unless a many-to-many table is created to create the association.
Lastly since your sample data is only showing a single Account (fisher), you would need to filter it somehow, which was not specified clearly in your question, so I added it in to the WHERE criteria.
Example: DB-Fiddle
SELECT
e.ID,
e.Name AS Employee,
e.Account,
COALESCE(janurary_totals.total, 0) AS `January(totals)`,
COALESCE(february_totals.total, 0) AS `February(totals)`,
COALESCE(janurary_totals.total, 0) + COALESCE(february_totals.total, 0) AS Totals
FROM (
SELECT e1.ID, e1.Name, a1.Name AS Account
FROM employees AS e1
LEFT JOIN Account AS a1
ON a1.ID IS NOT NULL
) AS e
LEFT JOIN (
SELECT
Employee,
Account,
SUM(firstday) + SUM(seconday) + SUM(thirdday) AS total
FROM January
GROUP BY Employee, Account
) AS janurary_totals
ON janurary_totals.Employee = e.Name
AND janurary_totals.Account = e.Account
LEFT JOIN (
SELECT
Employee,
Account,
SUM(fourthday) + SUM(eightday) + SUM(secondday) AS total
FROM February
GROUP BY Employee, Account
) AS february_totals
ON february_totals.Employee = e.Name
AND february_totals.Account = e.Account
WHERE e.Account = 'fisher'
Result
| ID | Employee | Account | January(totals) | February(totals) | Totals |
| --- | ----------- | ------- | --------------- | ---------------- | ------ |
| 1 | John Doe | fisher | 12 | 11 | 23 |
| 2 | Helen Price | fisher | 3 | 4 | 7 |
| 3 | John Price | fisher | 3 | 3 | 6 |
You could greatly simplify things, by refactoring the individual month tables into a single employee_entry table with an employee_id, account_id, amount and DATE columns, that you could use to query against to create the pivot-table. As it stands currently, there is no means of determining which year the values are recorded, which will cause confusion on subsequent year totals.
The single entry table would allow you to create a pivot-table using conditional aggregates.
SUM(CASE MONTH(date_column) WHEN # THEN amount_column ELSE 0 END) AS MonthTotal
Allowing you to filter and group the results as desired.
Example: DB-Fiddle
SELECT
e.`Name` AS Employee,
a.`Name` AS Account,
m.`Year`,
m.`January(totals)`,
m.`February(totals)`,
m.`Totals`
FROM (
SELECT
ee.employee_id,
ee.account_id,
YEAR(ee.`datetime`) AS `Year`,
SUM(CASE MONTH(ee.datetime) WHEN 1 THEN ee.amount ELSE 0 END) AS `January(totals)`,
SUM(CASE MONTH(ee.datetime) WHEN 2 THEN ee.amount ELSE 0 END) AS `February(totals)`,
SUM(ee.amount) AS `Totals`
FROM employee_entry AS ee
GROUP BY ee.employee_id, ee.account_id, `Year`
) AS m
LEFT JOIN employees AS e
ON e.ID = m.employee_id
LEFT JOIN Account AS a
ON a.ID = m.account_id
Result
| Employee | Account | Year | January(totals) | February(totals) | Totals |
| ----------- | ------- | ---- | --------------- | ---------------- | ------ |
| Helen Price | fisher | 2020 | 3 | 4 | 7 |
| John Doe | fisher | 2020 | 12 | 11 | 23 |
| John Price | fisher | 2020 | 3 | 3 | 6 |
| John Price | fisher1 | 2020 | 4 | 4 | 8 |
To perform the conversion, you create the employee_entry table, and insert your individual monthly tables into it, by specifying a date for each of the day columns.
CREATE TABLE employee_entry (
`id` INTEGER NOT NULL AUTO_INCREMENT,
`employee_id` INTEGER,
`account_id` INTEGER,
`amount` INTEGER,
`datetime` DATE NULL DEFAULT NULL,
PRIMARY KEY (`id`)
);
INSERT INTO employee_entry
(`employee_id`, `account_id`, `amount`, `datetime`)
(
SELECT e.ID, a.ID, j.firstday, '2020-01-01'
FROM `January` AS j
LEFT JOIN employees AS e
ON j.Employee = e.Name
LEFT JOIN Account AS a
ON j.Account = a.Name
)
UNION
(
SELECT e.ID, a.ID, j.seconday, '2020-01-02'
FROM `January` AS j
LEFT JOIN employees AS e
ON j.Employee = e.Name
LEFT JOIN Account AS a
ON j.Account = a.Name
)
UNION
(
SELECT e.ID, a.ID, j.thirdday, '2020-01-03'
FROM `January` AS j
LEFT JOIN employees AS e
ON j.Employee = e.Name
LEFT JOIN Account AS a
ON j.Account = a.Name
)
UNION
(
SELECT e.ID, a.ID, f.fourthday, '2020-02-04'
FROM `February` AS f
LEFT JOIN employees AS e
ON f.Employee = e.Name
LEFT JOIN Account AS a
ON f.Account = a.Name
)
UNION
(
SELECT e.ID, a.ID, f.eightday, '2020-02-08'
FROM `February` AS f
LEFT JOIN employees AS e
ON f.Employee = e.Name
LEFT JOIN Account AS a
ON f.Account = a.Name
)
UNION
(
SELECT e.ID, a.ID, f.secondday, '2020-02-02'
FROM `February` AS f
LEFT JOIN employees AS e
ON f.Employee = e.Name
LEFT JOIN Account AS a
ON f.Account = a.Name
);
Some help will be greatly appreciated. I am trying to find the last date of most recent promotion of all employees from a dataset. A sample of the dataset is as below.
DATASET -
mysql> select * from emp;
+----+-------+-----------+------------+
| id | empid | title | startdate |
+----+-------+-----------+------------+
| 1 | 111 | Associate | 2017-11-01 |
| 2 | 222 | ED | 2017-11-01 |
| 3 | 111 | Associate | 2017-12-01 |
| 4 | 222 | MD | 2017-12-01 |
| 5 | 111 | VP | 2018-01-01 |
| 6 | 222 | MD | 2018-01-01 |
| 7 | 111 | VP | 2018-02-01 |
| 8 | 222 | MD | 2018-02-01 |
+----+-------+-----------+------------+
The output I want -
+-------+-----------+----------------+
| empid | title | min(startdate) |
+-------+-----------+----------------+
| 222 | MD | 2017-12-01 |
| 111 | VP | 2018-01-01 |
+-------+-----------+----------------+
I tried this SQL -
mysql> select empid, title, min(startdate)
from ( select * from emp ) x
group by title, empid
order by empid desc;
It outputs all groups, like this -
+-------+-----------+----------------+
| empid | title | min(startdate) |
+-------+-----------+----------------+
| 222 | MD | 2017-12-01 |
| 222 | ED | 2017-11-01 |
| 111 | Associate | 2017-11-01 |
| 111 | VP | 2018-01-01 |
+-------+-----------+----------------+
You can filter on the latest title per employee with a correlated subquery, and then aggregate:
select empid, title, min(startdate)
from emp e
where e.title = (
select e1.title
from emp e1
where e1.empid = e.empid
order by startdate desc
limit 1
)
group by empid, title
order by empid desc
Demo on DB Fiddle:
empid | title | min(startdate)
----: | :---- | :-------------
222 | MD | 2017-12-01
111 | VP | 2018-01-01
Take the empid and the max(startdate) ONLY, as a subquery and join it back to the main table:
SELECT *
FROM emp e
INNER JOIN
(SELECT empid, Max(startdate) recentpromo FROM emp GROUP BY empid) x
ON x.empid = e.empid AND x.recentpromo = e.startdate
If you're on mysql8 you could use ROW_NUMBER for this as an alternative. If an employee is promoted twice on the same day this will pick up both records, though I think that unlikely to occur
Edit, so we can extend this another level:
SELECT
em.empid,
em.title,
min(em.startdate) as
FROM
emp em
INNER JOIN
(
SELECT e.empid, e.title
FROM emp e
INNER JOIN
(SELECT empid, Max(startdate) recentpromo FROM emp GROUP BY empid) x
ON x.empid = e.empid AND x.recentpromo = e.startdate
) frpt --find recent promo title
ON
em.empid = frpt.empid AND
em.title = frpt.title
GROUP BY
em.empid, em.title
I have the following query which works fine:
SELECT c.cust_id, c.cust_type_cd, c.city, count(*) as `count`
FROM customer c
INNER JOIN account a
ON a.cust_id = c.cust_id
GROUP BY c.cust_id
HAVING `count` = 2;
Result:
+---------+--------------+---------+-------+
| cust_id | cust_type_cd | city | count |
+---------+--------------+---------+-------+
| 2 | I | Woburn | 2 |
| 3 | I | Quincy | 2 |
| 6 | I | Waltham | 2 |
| 8 | I | Salem | 2 |
| 10 | B | Salem | 2 |
+---------+--------------+---------+-------+
I would like to achieve the same result using a correlated subquery. I have not been able to make a "count" column as show above:
SELECT c.cust_id, c.cust_type_cd, c.city
FROM customer c
WHERE 2 = (
SELECT COUNT(*)
FROM account a
WHERE a.cust_id = c.cust_id
);
Result:
+---------+--------------+---------+
| cust_id | cust_type_cd | city |
+---------+--------------+---------+
| 2 | I | Woburn |
| 3 | I | Quincy |
| 6 | I | Waltham |
| 8 | I | Salem |
| 10 | B | Salem |
+---------+--------------+---------+
How can I achieve the same result as the one using the INNER JOIN and have a "count" column?
Not sure why you wanted this but you also need to specify the subquery in the SELECT part,
SELECT c.cust_id, c.cust_type_cd, c.city, (SELECT COUNT(*)
FROM account a
WHERE a.cust_id = c.cust_id) AS `count`
FROM customer c
WHERE 2 = (
SELECT COUNT(*)
FROM account a
WHERE a.cust_id = c.cust_id
);
EDIT VERSION
EMPLOYEE TABLE
|ID | employee_id | Name |
| 1 | 123 | John Richard |
| 2 | 554 | Daniel Domingo |
educational background
|ID | employee_id | School/institute | date graduated |
| 1 | 123 | highschool | 2007 |
| 2 | 123 | college | 2011 |
| 3 | 554 | college | 2010 |
| 4 | 554 | masteral | 2013 |
job title
|ID | employee_id | Job description |
| 1 | 123 | Free lancer |
| 2 | 554 | admin assistant |
i need to select the latest date info of the employee's educational background
the result would be
result query
|ID | employee_id | Name | Job title | year_graduated | school_institute |
| 1 | 123 | John Richard | Free Lancer | 2011 | college |
| 2 | 554 | Daniel Domingo | Admin Assistant | 2013 | masteral |
Try this:
SELECT *
FROM (SELECT * FROM tableA ORDER BY employee_id, date_graduated DESC) A
GROUP BY employee_id
OR
SELECT a.*
FROM tableA a
INNER JOIN (SELECT employee_id, MAX(date_graduated) maxDate
FROM tableA GROUP BY employee_id
) b ON a.employee_id = b.employee_id AND a.date_graduated = b.maxDate
SELECT employee_id ,MAX([date graduated]) FROM Table GROUP BY [employee_id]
The result is
employee_id | date graduated
----------------------------
123 | 2011
554 | 2013
Subquery will return the maximum date for every employee, you should then join this subquery with your table to return the full row:
SELECT yourtable.*
FROM
yourtable INNER JOIN (SELECT employee_id, MAX(`date graduated`) max_date
FROM yourtable
GROUP BY employee_id) m
ON yourtable.employee_id = m.employee_id
AND yourtable.`date graduated` = m.max_date
for employee 123
SELECT * FROM `your_table`
WHERE employee_id=123
ORDER BY date_graduated DESC LIMIT 1
for employee 554
SELECT * FROM `your_table`
WHERE employee_id=554
ORDER BY date_graduated DESC LIMIT 1
SELECT *
FROM t
GROUP BY employee_id
HAVING date_graduated=max(date graduated)
and you'll get the desired result
Find below the test tables:
SELECT * FROM tbl_emp; // There must be few employee with no dept id (did)
+------+-------+------+
| eid | ename | did |
+------+-------+------+
| 1 | SCOTT | 2 |
| 2 | JAMES | 4 |
| 3 | BOND | 1 |
| 4 | TIGER | 5 |
| 5 | CHIP | 0 |
| 6 | DALE | 0 |
| 7 | MARY | 0 |
+------+-------+------+
SELECT * FROM tbl_dept;// There must be few depts which have no employee.
+-------+-------------+
| dptid | dname |
+-------+-------------+
| 1 | HR |
| 2 | IT |
| 3 | ADMIN |
| 4 | TRAVEL |
| 5 | SALES |
| 6 | FINANCE |
| 7 | ENGINEERING |
+-------+-------------+
I want to list all the employee name from tbl_emp which does not have dept and all the dname from tbl_dept which have no employee in a SINGLE QUERY in following manner:
DESIRED RESULTSET:
-------------------
ename dname
CHIP
DALE
MARY
ADMIN
FINANCE
ENGINEERING
-------------------
All I could do is:
SELECT ename FROM tbl_emp WHERE did NOT IN (SELECT dptid FROM tbl_dept);
and
SELECT dname FROM tbl_dept WHERE dptid NOT IN (SELECT did FROM tbl_emp);
Please help in select both dname and ename and that too in single query.
you can use UNION
SELECT ename, NULL dname
FROM tbl_emp
WHERE did NOT IN (SELECT dptid FROM tbl_dept)
UNION
SELECT NULL ename, dname
FROM tbl_dept
WHERE dptid NOT IN (SELECT did FROM tbl_emp);
SQLFiddle Demo
UPDATE 1
you can also use LEFT JOIN instead of using NOT IN (I'd rather use this one)
SELECT a.ename, b.dname
FROM tbl_emp a
LEFT JOIN tbl_dept b
ON a.did = b.dptid
WHERE b.dptid IS NULL
UNION
SELECT c.ename, d.dname
FROM tbl_dept d
LEFT JOIN tbl_emp c
ON d.dptid = c.did
WHERE c.did IS NULL
SQLFiddle Demo