MySQL - How to limit one result per ID? - mysql

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;

Related

fetch id who have register all adv

There are 3 types of adventures for which I used distinct function in query.
There is only one 1 customer who have booked all types of adventures.
The query i used to fetch the data is:
select c.customerid,c.name
from customer c
inner join booking b
on c.customerid = b.customerid
inner join destination d
on b.destinationid=d.destinationid
inner join adventure a
on d.adventureid=a.adventureid
group by c.customerid
having count(distinct b.bid)=(select count(*) from bid)
or count(distinct a.adventuretype)=(
select count(distinct a.adventuretype)
from adventure
)
You can get the customer ids using aggregation and having:
select b.customerid
from booking b join
destination d
on b.destinationid = d.destinationid join
adventure a
on d.adventureid = a.adventureid
group by b.customerid
having count(distinct a.advtype) = 3;
Or, if you don't want to hardcode the "3", you can use:
having count(distinct a.advtype) = (select count(distinct advtype from adventure)
I'll leave it up to you to add in the customer name (using join, exists, or in).

How to limit one row per user id

I have this table called employeetimesheets:
empsheet_id|employee_id|timesheet_status|last_update
The table allows the manager to have access to all the employee time sheets. One employee can have several time sheets. I would like to display only the most recent entry per employee. I read in the manual I have to write a groupwise-maximum subquery and left join with inner join but I'm unsure how to go about it here.
So far this is the query I have:
$sqlempsheets="SELECT * FROM employeetimesheets JOIN employees ON employeetimesheets.employee_id=employees.employee_id WHERE employeetimesheets.timesheet_status='Pending Approval'";
$resultempsheets=mysqli_query($db,$sqlempsheets);
Try this:
select *
from employeetimesheets t
join (
select employee_id,
max(empsheet_id) as empsheet_id
from employeetimesheets
group by employee_id
) t2 on t.employee_id = t2.employee_id
and t.empsheet_id = t2.empsheet_id
join employees e on t.employee_id = e.employee_id
where t.timesheet_status = 'Pending Approval';
Or using left join:
select t.*, e.*
from employeetimesheets t
left join employeetimesheets t2 on t.employee_id = t2.employee_id
and t.empsheet_id < t2.empsheet_id
join employees e on t.employee_id = e.employee_id
where t.timesheet_status = 'Pending Approval'
and t2.employee_id is null;

Comparing two SQL queries when using IN

I have two tables, for example the Employee and Project tables:
Employee (id, dept, joining_date)
Project (emp_id, project)
With Project having foreign key from Employee table.
I have to query on project and dept and return Employee in the order of their joining_date. Which query will work faster on big data set on the queries below?
select * from Employee where id in (select p.emp_id from Project p join Employee e on p.emp_id = e.id where p.project = 'project1' and e.dept = 'dept1') order by joining_date
select * from Employee where id in (select p.emp_id from Project p join Employee e on p.emp_id = e.id where p.project = 'project1' and e.dept = 'dept1') and dept = 'dept1' order by joining_date
Or is there any better and simpler way to do so?
The outer query using the IN() expression serves no purpose is entirely unnecessary. This will produce the output you need:
select e.*
from Project p
inner join Employee e on p.emp_id = e.id
where p.project = 'project1' and e.dept = 'dept1'
order by joining_date

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

SQL aggregation group by

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