How to isolate rows in MySQL using MAX? - mysql

I have the following MySQL tables:
project:
pname, pnumber, plocation, dnum
works_on:
essn, pno, hours
My goal of this query is to return the project name and hours of the project(s) with the maximum amount of hours allocated to any project.
So far, I have
SELECT *, SUM(hours) as total_hours
FROM project p
JOIN works_on w ON p.pnumber = w.pno
GROUP BY pname
which when executing gives the following:
I would like to isolate the entries with 55 hours allocated to it but I've tried
SELECT *, SUM(hours) as total_hours
FROM project p
JOIN works_on w ON p.pnumber = w.pno
WHERE total_hours = MAX(total_hours)
GROUP BY pname
which gives me Error Code: 1054: Unknown column "total_hours" in "where clause"
which I know is clearly wrong but I'm not sure what other approaches I would take.

WITH cte AS (
SELECT *, SUM(w.hours) as total_hours,
DENSE_RANK() OVER (ORDER BY SUM(w.hours) DESC) drnk
FROM project p
JOIN works_on w ON p.pnumber = w.pno
GROUP BY p.pname
)
SELECT * FROM cte WHERE drnk = 1;

Related

Orders that have not yet been paid mysql

I am stuck with the following problem: I would like to return from my query the orders which have yet to be paid with the amount of the order and my customer information.
Here is what the tables look like:
Taking the case of customer number 124. I can see that this one has placed 13 orders. But I only have 7 checks registered
And I can see that if I add the sum of the orders minus the sum of the payments I find a difference that corresponds to the last 3 orders that this customer has placed.
So the question is how do I bring out the commands 10382, 10371, 10368 with all that? Knowing that the solution must be able to be applied to all orders.
So I thought to myself maybe using the order and payment dates. What orders does my customer place between their last payment and today?
So I tried something like this
#last order date
WITH last_cmd AS
(
SELECT
customerNumber, od.orderNumber, orderDate last_orderDate,
shippedDate, SUM(quantityOrdered * priceEach) totcmd
FROM
orders o
LEFT JOIN
orderdetails od ON o.orderNumber = od.orderNumber
WHERE
status NOT LIKE 'Can%'
GROUP BY
orderDate
ORDER BY
customerNumber, orderDate DESC
),
#last payment date last_payment AS
(
SELECT
customerNumber, amount, paymentDate, checkNumber
FROM
payments
GROUP BY
paymentDate
ORDER BY
customerNumber, paymentDate DESC
)
SELECT *
FROM last_cmd lc
LEFT JOIN last_payment lp ON lc.customerNumber = lp.customerNumber
GROUP BY lc.orderNumber
HAVING MAX(paymentDate) NOT BETWEEN MAX(last_orderDate) AND NOW();
But as you can see the result is not really what is expected. Still taking customer 124 as an example, command 10382 does not appear. And when I do the comparison of other orders manually for other customers the results do not match.
Does anyone have an idea please?
Here is the query I tried before posting
WITH ALL_PAYMENTS AS (
WITH paymentsValues AS (
select customerNumber, checkNumber, YEAR(paymentDate) paymentYear, sum(amount) paymentValue
from payments
LEFT JOIN customers USING (customerNumber)
GROUP BY customerNumber, paymentYear)
SELECT
customerNumber,
checkNumber, #Cumul des commandes clients
paymentYear,
SUM(paymentValue) totalPaymentsValue
FROM
paymentsValues
GROUP BY
customerNumber,
checkNumber
WITH ROLLUP
HAVING checkNumber is null),
ALL_ORDERS AS(
WITH ordersValues AS (
select customerNumber, orderNumber, YEAR(orderDate) orderYear, sum(quantityOrdered*priceEach) AS orderValue
from orderdetails
LEFT JOIN orders USING (orderNumber)
GROUP BY orderNumber, orderYear)
SELECT
customerNumber,
orderNumber, #Cumul des commandes clients
orderYear,
SUM(orderValue) totalOrderValue
FROM
ordersValues
#where orderNumber = '10179'
GROUP BY
customerNumber,
orderNumber
WITH ROLLUP
HAVING orderNumber is null)
SELECT AO.customerNumber, totalOrderValue, totalPaymentsValue, totalOrderValue-totalPaymentsValue Diff
FROM ALL_ORDERS AO
LEFT JOIN ALL_PAYMENTS AP ON AP.customerNumber=AO.customerNumber
WHERE totalOrderValue-totalPaymentsValue > 0;
This should give a list op amount that have to be paid, per date:
WITH d as (
select distinct orderDate as `date` from orders
union
select distinct paymentDate as `date` from payments)
select
`date`,
`customerNumber`,
`ordersValue`,
`amountPayed`,
`ordersValue` - `amountPayed` NotPayed
from (
select
d.`date` as "date",
o.customerNumber as "customerNumber",
sum(quantityOrders*priceEach) ordersValue,
(select sum(amount)
from payments p
where o.customerNumber = p.customerNumber AND p.paymentDate <= d.`date`) as amountPayed
from orderdetails od
cross join d
inner join Orders o on od.orderNumber = o.orderNumber AND o.orderDate <= d.`date`
group by d.`date`, o.customerNumber
) orderListPerCustomer
WHERE `customerNumber` = 124
ORDER BY `date` DESC
;
It is impossible to add order info, because there is no order info in the payments
This query does not take into account the status of the order (which might be Cancelled)
NOTE: This code is not tested, so there could be typing errors...

Issue with select statements in MySQL past exam paper

Im going over a past paper and have found some select statements I cannot do. Can anyone help with these? I'm using MySQL. Thanks
Schema:
employee = (employee id, name, address, date of birth, salary)
project = (project id, name, budget, start date, end date)
manages = (employee id, project id)
works on = (employee id, project id)
Questions :
Names and addresses of all employees who work on projects which are
current (have not ended yet)
The names of managers of projects, ordered by the total number of employees they manage
The names of employees who work on the project with the largest budget
amount.
My attempts:
1.
select employee.name, address
from employee natural join works_on natural join project
where end_date is null
2.
select employee.name, count(works_on.employee_id) as manages_count
from (employee natural join manages) joins works_on using (employee_id)
I'm completely lost I cannot even attempt this one ^
3.
select employee.name, address, budget
from employee natural join works_on natural join project
order by budget
limit n
^ I know this is wrong as technically I should be able to show them without a limit
Here you go:
1.
select
e.name,
e.address
from employee e
join works_on w on w.employee_id = r.employee_id
join project p on p.project_id = e.project_id
where p.end_date is null
2.
select
n.name
from employee n
join manages m on m.employee_id = n.employee_id
join works_on w on e.project_id = m.project_id
group by n.employee_id
order by count(*) desc
3.
select
e.name
from employee e
join works_on w on w.employee_id = e.employee_id
join project p on p.project_id = w.employee_id
where p.project_id in (
select project_id from project where budget = (
select max(budget) from project
)
)

Mximum hours spent by an employee on each project

I have this query
SELECT t.pname,MAX(t.name) ,MAX(t.total)
FROM
(
SELECT p.`id`,e.`name`,p.`pname`,(m.`hour`) AS total
FROM employee e INNER JOIN epmap m ON m.`employeeID`=e.`id` INNER JOIN project p ON p.`id`=m.`projectID`
)t
GROUP BY t.id
it gives the right answer, but its not the good approach beacuse Max(t.name) not appropriate
This will give you the result you need:
SELECT t.pname, t.name, t.hour as total
FROM (
SELECT p.id, e.name, p.pname, m.total,
ROW_NUMBER() OVER(partition by p.id order by m.hour desc) rn
FROM employee e
INNER JOIN epmap m ON m.employeeID=e.id
INNER JOIN project p ON p.id=m.projectID
) t
where t.rn = 1
SELECT DISTINCT would probably let you get rid of the MAX on name.

select department(s) with maximum number of employees

I have two tables EMP(id,name,DEPT_id) and DEPT(id ,name). I need to find the department(s) in which the maximum number of employees work. Please help.
This will give the department name of the department which is having maximum number of employees.
Select DEPT_NAME from department where DEPT_ID = (select DEPT_ID from (Select DEPT_ID, count(DEPT_ID) from Employee group by DEPT_ID order by count(DEPT_ID) desc) where rownum = 1);
Just a little more verbose than the other two solutions, but it will get the job done...feel free to tweak to your convenience.
select countbydept.*
from
(
-- from EMP table, let's count number of records per dept
-- and then sort it by count (highest to lowest)
-- and take just the first value. We just care about the highest
-- count
select dept_id, count(*) as counter
from emp
group by dept_id
order by counter desc
limit 1
) as maxcount
inner join
(
-- let's repeat the exercise, but this time let's join
-- EMP and DEPT tables to get a full list of dept and
-- employe count
select
dept.id,
dept.`name`,
count(*) as numberofemployees
from dept
inner join emp on emp.dept_id = dept.id
group by dept.id, dept.`name`
) countbydept
-- combine the two queries's results by matching the employee count
on countbydept.numberofemployees = maxcount.counter
Example: http://sqlfiddle.com/#!9/7d6a2d/1
Try this query.
SELECT a.name,Max(a.NumEmp) AS maxEmpCount FROM ( SELECT d.name,COUNT(*) AS NumEmp FROM EMP e INNER JOIN DEPT d ON e.DEPT_id = d.id GROUP BY e.DEPT_id ) AS a GROUP BY a.name
you can solve this using with statement like this:
with deps as
(select dep.department_name as dep_name, count(emp.employee_id) as cnt
from departments dep
inner join employees emp
on emp.department_id = dep.department_id
group by dep.department_name)
select deps.dep_name,cnt from deps
where cnt=(select max(cnt) from deps)
OR
select dep.department_name as dep_name, count(emp.employee_id) as cnt
from departments dep
inner join employees emp
on emp.department_id = dep.department_id
group by dep.department_name
having count(emp.employee_id) >= all (select count(emp.employee_id) as cnt
from departments dep
inner join employees emp
on emp.department_id =
dep.department_id
group by dep.department_name)
OR
with s1 as
(select dep.department_name as dep_name,
count(emp.employee_id) over(partition by dep.department_name) as cnt
from departments dep
inner join employees emp
on emp.department_id = dep.department_id
order by cnt desc),
s2 as
(select s1.dep_name,
s1.cnt,
row_number() over(order by cnt desc) as row_num
from s1)
select dep_name from s2 where row_num = 1
these solutions are proper for databases like Oracle that we do not have top(1) or limit 1
select Top 1 d.DNAME,count(e.ename) as counts from emp e,dept d where d.DEPTNO=e.DEPTNO
group by d.DNAME
order by counts desc
Or
select d.DNAME,count(e.ename) as counts from emp e,dept d where d.DEPTNO=e.DEPTNO
group by d.DNAME
having count(e.ename) = (select max(micount) from (select count(deptno) micount from emp group by DEPTNO) a)
You can try this query.
Select Id, Name from Dept
Where Id = (Select Top(1) DeptId from Emp
Group By DeptId
order by Count(DeptId) desc)
you can create view to find it.
CREATE VIEW TEMP AS SELECT COUNT(EMP.id) AS A, DEPT.name AS B
FROM EMP JOIN DEPT ON EMP.DEPT_id=DEPT.id GROUP BY DEPT.id;
SELECT MAX(A) FROM TEMP;
Now, EMP(id,name,DEPT_id) and DEPT(id ,name) these two tables are given. Now, I insert some entries in the table in such a manner that:
SELECT COUNT(*) AS NO_OF_EMPLOYEES,
DEPARTMENT.DEPT_NAME
FROM EMP, DEPARTMENT
WHERE EMP.DEPT_ID=DEPARTMENT.DEPT_ID
GROUP BY EMP.DEPT_ID
ORDER BY NO_OF_EMPLOYEES;
This query generates the following:
NO_OF_EMPLOYEES DEPT_NAME
3 Research
3 Finance
4 Sales
4 Product
Now, the query which gives the correct result:
SELECT COUNT(*) AS MAX_NO_OF_EMPLOYEES,
DEPARTMENT.DEPT_NAME
FROM EMP, DEPARTMENT
WHERE EMP.DEPT_ID=DEPARTMENT.DEPT_ID
GROUP BY EMP.DEPT_ID
HAVING MAX_NO_OF_EMPLOYEES=(
SELECT COUNT(*) AS NO_OF_EMPLOYEES
FROM EMP
GROUP BY DEPT_ID
ORDER BY NO_OF_EMPLOYEES DESC
LIMIT 1
);
It will generate:
MAX_NO_OF_EMPLOYEES DEPT_NAME
4 Sales
4 Product
This question can be solved in multiple ways
Using sub query
SELECT name FROM dept WHERE id IN (SELECT dept_id FROM emp HAVING COUNT(dept_id) IN (SELECT MAX(COUNT(dept_id)) FROM emp) GROUP BY dept_id)
Using Join
SELECT name FROM emp e INNER JOIN dept d ON e. dept_id = d. id HAVING COUNT(e.dept_id) IN (SELECT MAX(COUNT(dept_id)) from emp) group by dept_id)
If you have only emp table then below query will hep you get a results -
select a.* from (select deptno, dense_rank() over(order by count(*) desc ) as rank from dbo.emp group by deptno) a where a.rank =1
select deptno,count(*)from emp group by
deptno having count(*)=(select max(count(*))from emp group by deptno);
SELECT department_id, count(employee_id) as 'No_of_Emp'
FROM employees
GROUP BY department_id
ORDER BY No_of_Emp DESC

a better way to get customers who placed maximum and minimum number of orders

My question is to find all those customers who have made the minimum and maximum no of orders.
what i could come up with is this :
select customer_id , count(order_id) as num
from bab_customer right outer join bab_order_details using(customer_id)
group by customer_id
having count(order_id) >=
all(select count(order_id)
from bab_customer right outer join bab_order_details using(customer_id)
group by customer_id)
or count(order_id) <=
all(select count(order_id)
from bab_customer right outer join bab_order_details using(customer_id)
group by customer_id);
it gives me the correct output , but i have to do the same join 3 times. is there some better way to do this ?
Is this better? I don't know...
SELECT x.*
FROM
( SELECT customer_id
, COUNT(*) cnt
FROM orders
GROUP
BY customer_id
) x
JOIN
( SELECT MIN(cnt) min_cnt
, MAX(cnt) max_cnt
FROM
( SELECT customer_id
, COUNT(*) cnt
FROM orders
GROUP
BY customer_id
) n
) y
ON y.min_cnt = x.cnt
OR y.max_cnt = x.cnt;
I realised, that you cannot run it in a single query without calling same subqueries several times. As you refer min(numOrders) and max(numOrders) you have to store numOrders as a temporal table or to calculate it twice. Most likely the DBMS will cache your requests if they are identical, so you should not worry about wasting resources.
So your query must be a bit rewrited and is good enough. My variant is
select customer_id , count(order_id) as num
from customers right outer join orders using(customer_id)
group by customer_id
having
num = (SELECT min(num) FROM (select count(order_id) as num
from customers right outer join orders using(customer_id)
group by customer_id) numOrders)
or
num = (SELECT max(num) FROM (select count(order_id) as num
from customers right outer join orders using(customer_id)
group by customer_id) numOrders)
You can check it working with this sqlfiddle
If I absolutely felt this must be a single query, I'd probably just use a union all.
(
select
customer_id,
count(*) as num
from bab_customer right outer join bab_order_details using(customer_id)
group by customer_id
order by num desc limit 1
)
union all
(
select
customer_id,
count(*) as num
from bab_customer right outer join bab_order_details using(customer_id)
group by customer_id
order by num asc limit 1
)
Originally, I'd been thinking this would perform better due to avoiding joins on intermediate values. However, I'm not seeing any performance gain here. (If it was faster, then limit could be raised to some arbitrary number and the application could trim out a few extra values without losing much of the increased performance)
But, no dice.