SQL aggregation group by - mysql

I have an employee table and a leave_allocation table which has a one-to-many relationship, Each employee has a number of leave allocations over a period of time. I would like to get the LATEST allocation for each employee.
I tried the query but the date and the days values do not correlate to the same row
select e.employee_number, e.nme, MAX(l.date), l.days
from employee e, leave_allocation l
where l.employee_id = e.employee_id
group by e.employee_number, e.nme
How can I get the latest allocation per employee?

SELECT e.employee_number
,e.nme
,l.days
FROM employee e
, leave_allocation l
,(SELECT employee_id
,MAX(DATE) date
FROM leave_allocation
GROUP BY employee_id) m
WHERE l.employee_id = e.employee_id
AND l.employee_id = m.employee_id
AND l.date = m.date
If there can be multiple rows with same employee_number and date, then you need to sum.
SELECT e.employee_number
,e.nme
,sum(l.days)
FROM employee e
, leave_allocation l
,(SELECT employee_id
,MAX(DATE) date
FROM leave_allocation
GROUP BY employee_id) m
WHERE l.employee_id = e.employee_id
AND l.employee_id = m.employee_id
AND l.date = m.date
GROUP BY e.employee_number
,e.nme

Place the MAX() date in a subquery:
SELECT e.employee_number, e.nme, l.leavedate, la.days
FROM employee e
INNER JOIN
(
SELECT Max(date) leavedate, employee_id
FROM leave_allocation
GROUP BY employee_id
) l
ON e.employee_id = l.employee_id
INNER JOIN leave_allocation la
ON l.employee_id = la.employee_id
AND l.leavedate = la.date
I also switched the query to use ANSI join syntax instead of commas between the tables.

Try this one,
SELECT e.employee_number, e.nme, c.maxDate, l.days
FROM employee e
INNER JOIN leave_allocation l
ON l.employee_id = e.employee_id
INNER JOIN
(
select employee_id, MAX(date) maxDate
from leave_allocation
group by employee_id
) c ON c.employee_id = l.employee_ID AND
c.maxDate = l.date

Related

Partition over or Group By best for Mysql

SELECT P.FirstName, d.Name, EDH.StartDate
FROM HumanResources.EmployeeDepartmentHistory edh INNER JOIN HumanResources.Department d
ON EDH.DepartmentID=D.DepartmentID
INNER JOIN HumanResources.Employee e
ON EDH.BusinessEntityID=E.BusinessEntityID
INNER JOIN Person.Person P
ON E.BusinessEntityID=P.BusinessEntityID
WHERE EndDate IS NULL
ORDER BY D.NAME;
I am trying to list the person in each department that has worked there the longest. I know that using the top(1) for each department would most likely be the best option but I can figure if using partition or group by is going to be the better option in this case. Anybody with more skill than me have any ideas?
Working the longest means the earliest start date. That suggests:
SELECT ed.*
FROM (SELECT P.FirstName, d.Name, EDH.StartDate,
RANK() OVER (PARTITION BY D.DepartmentID ORDER BY EDH.StartDate) as seqnum
FROM HumanResources.EmployeeDepartmentHistory edh JOIN
HumanResources.Department d
ON EDH.DepartmentID = D.DepartmentID JOIN
HumanResources.Employee e
ON EDH.BusinessEntityID = E.BusinessEntityID JOIN
Person.Person P
ON E.BusinessEntityID = P.BusinessEntityID
WHERE EndDate IS NULL
) ed
WHERE seqnum = 1
ORDER BY D.NAME;

join query in sql

Here is my SQL query and result. My requirement is to display department_name with a maximum staff count but I don't know how to do that.
I want a result like department_name - SE and staff_ count - 4 only.
select d.department_name, count(staff_id) from department d, staff sf
where d.department_id = sf.staff_id
group by department_name
order by count(staff_id) desc
DEPARTMENT_NAME COUNT(SF.STAFF_ID)
------------------------------ ------------------
SE 4
EEE 2
IT 2
CSE 2
ECE 1
You need to first get the count of each department and then select department with the maximum count. Therefore, you can use sub-query as following:
SELECT d_name, MAX (staff_cnt)
FROM (SELECT d.department_name as d_name, count(staff_id) as staff_cnt
FROM department d, staff sf
WHERE d.department_id = sf.staff_id
GROUP BY department_name);
Try this
alias query
select d.department_name, count(staff_id)
from department d, staff sf
where d.department_id = sf.staff_id AND d.department_name = 'SE'
group by department_name
order by count(staff_id) desc
JOIN Query
select d.department_name, count(staff_id)
from department d
INNER JOIN staff sf ON d.department_id = sf.staff_id
WHERE d.department_name = 'SE'
group by department_name
order by count(staff_id) desc
LIMIT 1
OR Top
select TOP 1 d.department_name, count(staff_id)
from department d
INNER JOIN staff sf ON d.department_id = sf.staff_id
group by department_name
order by count(staff_id) desc
You should use LIMIT 1 for reduce the result but also use explicit join syntax for a better readability:
select d.department_name, count(staff_id)
from department d
INNER JOIN staff sf ON d.department_id = sf.staff_id
group by department_name
order by count(staff_id) desc
limit 1
Or without using limit you could try having on the max:
select d.department_name, count(staff_id) count_staff
from department d
INNER JOIN staff sf ON d.department_id = sf.staff_id
group by department_name
having count_staff = (
select max(count_staff)
from ( select d.department_name, count(staff_id) count_staff
from department d
INNER JOIN staff sf ON d.department_id = sf.staff_id
group by department_name ) t
)

MySQL - How to limit one result per ID?

I have the following query which creates a view table showing the highest salesperson in a store with few other details:
CREATE OR REPLACE VIEW sales_data AS
SELECT s.storename AS "Store",
e.employee_name AS "Employee",
e1.employee_name AS "Manager",
SUM(p.total_sale_value) AS "Sales Value"
FROM fss_Shop s
JOIN Employee e ON e.storeid = s.storeid
JOIN Payment p ON p.employee_number = e.employee_number
JOIN Employee e1 ON e1.employee_number = e.manager_number
WHERE s.storeid=1
GROUP BY e.employee_name
ORDER BY SUM(p.total_sale_value) DESC LIMIT 1;
The above query will only show the sales data for a single store and the reason being as I have stated WHERE s.storeid=1. I have 20 stores in my table. How can I change the above query so that it gives me sales data for 20 stores (so that's 20 rows).
CREATE OR REPLACE VIEW employee_sales_totals AS
SELECT
e.*,
SUM(p.total_sale_value) AS total_sale_value
FROM
Employee e
INNER JOIN
Payment p
ON p.employee_number = e.employee_number
GROUP BY
e.id -- This should be the Primary Key / Surrogate Key of the employee table
;
CREATE OR REPLACE VIEW shop_top_employee_by_sales_value AS
SELECT
s.storename AS "Store",
e.employee_name AS "Employee",
m.employee_name AS "Manager",
p.total_sale_value AS "Sales Value"
FROM
(
SELECT storeid, MAX(total_sale_value) AS total_sale_value
FROM employee_sales_totals
GROUP BY storeid
)
p
INNER JOIN
employee_sales_totals e
ON e.storeid = p.storeid
AND e.total_sale_value = p.total_sale_value
INNER JOIN
fss_Shop s
ON s.storeid = e.storeid
INNER JOIN
Employee m
ON m.employee_number = e.manager_number
;
As per the answer to your previous question, if multiple employees are tied for the same total sales amount in the same store, all such employees would be returned.
Consider using a ranking variable by SalesValue for each employee per store and then choose the RANK=1 in outer query:
SELECT main.Store, main.Employee, main.Manager, main.SalesValue
FROM
(SELECT agg.*,
#store:=agg.Store AS CURR_STORE,
#rank:=CASE WHEN #val > agg.SalesValue THEN #rank+1 ELSE 1 END AS RANK,
#val:=CASE WHEN #store <> agg.Store THEN #val ELSE agg.SalesValue END AS CURR_VAL
FROM
(SELECT s.storename AS "Store",
e.employee_name AS "Employee",
e1.employee_name AS "Manager",
SUM(p.total_sale_value) AS "SalesValue"
FROM fss_Shop s
INNER JOIN Employee e ON e.storeid = s.storeid
INNER JOIN Payment p ON p.employee_number = e.employee_number
INNER JOIN Employee e1 ON e1.employee_number = e.manager_number
GROUP BY s.storename,
e.employee_name,
e1.employee_name
) As agg
CROSS JOIN (SELECT #rank:= 0) AS r1
CROSS JOIN (SELECT #val:= 0) AS r2
CROSS JOIN (SELECT #store:= 0) AS r3
ORDER BY agg.Store, agg.SalesValue DESC
) As main
WHERE main.RANK = 1;
DEMO
Rextester (using random data with only one Sales table)
Alternatively, if variables cannot be used, consider creating two views where latter references the former: 1) initial aggregate query, 2) correlated subquery to retrieve top employee per store
CREATE OR REPLACE VIEW sales_data AS
SELECT s.storename AS "Store",
e.employee_name AS "Employee",
e1.employee_name AS "Manager",
SUM(p.total_sale_value) AS "SalesValue"
FROM fss_Shop s
INNER JOIN Employee e ON e.storeid = s.storeid
INNER JOIN Payment p ON p.employee_number = e.employee_number
INNER JOIN Employee e1 ON e1.employee_number = e.manager_number
GROUP BY s.storename,
e.employee_name,
e1.employee_name;
CREATE OR REPLACE VIEW top_sales_data AS
SELECT s.*
FROM sales_data s
WHERE (SELECT Count(*) FROM sales_data sub
WHERE sub.SalesValue > s.SalesValue
AND sub.Store = s.Store) = 0;

Selecting employees from database where the company contains between 1 and 5 employees

So far I have this query:
SELECT
c.id AS company_id,
c.company_name,
COUNT(*) AS employee_count
FROM
ct_companies c
INNER JOIN ct_employees e ON c.id = e.company_id
GROUP BY
c.domain,
c.postcode,
c.company_name
HAVING
(
employee_count >= 1
AND employee_count <= 5
)
ORDER BY
employee_count DESC
Now, this works great, it selects companies who have at least 1 employee but no more than 5 and shows me the employee count.
But what I want to be able to do, is select every employee within a company, but only where that company has between 1 and 5 employees like above.
So something like
SELECT e.id FROM ct_employees e WHERE (employee_count >= 1 AND employee_count <= 5)
You could try
SELECT e.id,
d.employee_count
FROM ct_employees e
INNER JOIN (SELECT c.id AS company_id,
COUNT(*) AS employee_count
FROM ct_companies c
INNER JOIN ct_employees e1
ON c.id = e1.company_id
GROUP BY c.domain,
c.postcode,
c.id
HAVING COUNT(*) >= 1
AND COUNT(*) <= 5) d
ON d.company_id = e.company_id
ORDER BY employee_count DESC

MySQL - Arithmetic operations with relations

How do I get the amount of income each employee automatically, based on how the number of employees who participated in the construction project .
Already tried this , but the error . Subquery returned more than 1 row .
SELECT e.name,
( SELECT( p.costs / count(r.employee_id))
FROM relation_employee r GROUP BY r.project_id ) AS revenue
FROM project p
INNER JOIN relation_employee r ON p.id = r.project_id
INNER JOIN employee e ON r.employee_id = e.id
table employee
id INT
name VARCHAR
table project
id INT
name VARCHAR
costs INT
table relation_employee
employee_id INT
project_id INT
Instead of using a correlated subquery in the select part you could get the employee count per project as a derived table to use in the from part, which at least to me looks a bit cleaner. The query could look like this:
-- revenue per employee
SELECT e.name, sum(p.costs * 1.0 / emp_count) AS revenue
FROM project p
INNER JOIN relation_employee r ON p.id = r.project_id
INNER JOIN (SELECT project_id, count(employee_id) emp_count FROM relation_employee GROUP BY project_id) c ON c.project_id = p.id
INNER JOIN employee e ON r.employee_id = e.id
GROUP BY e.name;
-- revenue per employee and project
SELECT
e.name as employee_name,
p.name as project_name,
sum(p.costs / emp_count) AS revenue
FROM project p
INNER JOIN relation_employee r ON p.id = r.project_id
INNER JOIN (SELECT project_id, count(employee_id) emp_count FROM relation_employee GROUP BY project_id) c ON c.project_id = p.id
INNER JOIN employee e ON r.employee_id = e.id
GROUP BY e.name, p.name;
Sample SQL Fiddle