Aggregate query on mysql 8 - mysql

I have two tables, allowances & salaries,
Allowances table looks like,
Id Title value
1 Transport 2000
2 Housing 1000
3 Housing 1000
The Salaries table,
id Salary
1 2000
2 1000
3 3000
For id's with no Housing allowance the value should be considered as 0 for housing and just the salary should be shown. If housing allowance is available, add up salary and housing.
My query is not exactly giving the right output,
SELECT
s.id,
CASE
WHEN t.`Housing` = 0 THEN s.salary
END AS Salary,
CASE
WHEN t.`Housing` <> 0 THEN t.Housing + s.salary
WHEN t.`Housing` = 0 THEN s.salary
END AS SalaryAndHousing
FROM
(SELECT
id,
CASE
WHEN Title = 'Housing' THEN value
WHEN Title <> 'Housing' THEN 0
END AS 'Housing'
FROM
Allowance) t
JOIN
Salaries s ON (t.id = s.id);
The output of my query is,
id Salary SalaryAndHousing
1 2000 2000
2 null 2000
3 null 4000
It should look like,
id Salary SalaryAndHousing
1 2000 2000 -- 2000 + 0
2 1000 2000 -- 1000 + 1000
3 3000 4000 -- 1000 + 3000
dbfiddle

This is can be pretty simple query:
select
s.id,
s.Salary,
s.Salary + coalesce(a.value, 0) SalaryAndHousing
from Salaries s
left join Allowance a on a.id = s.id and a.Title = 'Housing'
order by s.id;
Try it on SQLize.online

Related

How to substrate number from the sum of columns of a table based on the id

I have a table named deposit, below
dep_id
deposit_amount
comp_id
1
100
1
2
100
1
3
100
1
When I run the query below I get the next updated table which is not what I want :
query = em.createNativeQuery("UPDATE deposit SET deposit_amount = (SELECT SUM(deposit_amount) - 50) WHERE comp_id = :comp_id");
query.setParameter("comp_id", comp_id);
The updated table after the above query
dep_id
deposit_amount
comp_id
1
50
1
2
50
1
3
50
1
But I want when I substract 50 or any amount it should get the sum of the columns and minus the amount from the sum not from each column. Below
dep_id
deposit_amount
comp_id
1
83.3
1
2
83.3
1
3
83.3
1
Because the sum is 300, and 300-50 = 250
Please how should I do this?
Using a common table expression, you can use this query. Get the total deposit amount per comp_id. Then join this new table (called total) to deposit on comp_id. Subtract 50 from the sum.
WITH total as(
select comp_id,
sum(deposit_amount) as total
from
deposit
group by
comp_id
)
select dep.dep_id,
ttl.total - 50 as deposit_amount,
dep.comp_id
from
deposit dep
inner join
total ttl
on
dep.comp_id = ttl.comp_id
Sample:
dep_id
deposit_amount
comp_id
1
250
1
2
250
1
3
250
1
You should compute deposit amount in a separate query, then join back your two tables on matching "comp_id" value
WITH cte AS (
SELECT DISTINCT comp_id,
SUM(deposit_amount) OVER(PARTITION BY comp_id) AS amount
FROM deposit
)
UPDATE deposit
INNER JOIN cte
ON deposit.comp_id = cte.comp_id
SET deposit_amount = cte.amount - 50
WHERE deposit.comp_id = :comp_id;
In your final query it should look like:
query = em.createNativeQuery("WITH cte AS (SELECT DISTINCT comp_id, SUM(deposit_amount) OVER(PARTITION BY comp_id) AS amount FROM deposit) UPDATE deposit INNER JOIN cte ON deposit.comp_id = cte.comp_id SET deposit_amount = cte.amount - 50 WHERE deposit.comp_id = :comp_id");
query.setParameter("comp_id", comp_id);
Check the demo here.

MySQL sums from multiple tables

I have multiple tables and I need to aggregate the data from all of them, but it seems that I always get the wrong results for the sums. What am I doing wrong?
customers
ID Name
1 c1
2 c2
3 c3
budget
ID Cust_ID Value
1 1 100
2 1 300
3 2 600
4 3 450
forecast
ID Cust_ID Value
1 1 200
2 1 500
3 2 100
4 2 700
5 3 550
orders
ID Cust_ID Net_Sales
1 1 100
2 1 200
3 1 300
4 2 400
5 3 500
Here is the expected result:
ID Name sum(budget.Value) sum(forecast.Value) sum(orders.Net_Sales) count(orders.ID)
1 c1 400 700 600 3
2 c2 600 800 400 1
3 c3 450 550 500 1
And here's what I've tried so far:
SELECT customers.ID, customers.Name, sum(budget.Value), sum(forecast.Value), sum(orders.Net_Sales), count(orders.ID)
FROM customers
INNER JOIN budget ON budget.Cust_ID = customers.ID
INNER JOIN forecast ON forecast.Cust_ID = customers.ID
INNER JOIN orders ON orders.Cust_ID = customers.ID
GROUP BY customers.ID
ORDER BY customers.ID ASC
You are joining along multiple dimensions, which multiplies the results.
A simple solution is correlated subqueries:
SELECT c.ID, c.Name,
(SELECT SUM(b.Value)
FROM budget b
WHERE b.Cust_ID = c.ID
) as budget,
(SELECT SUM(f.Value)
FROM forecast f
WHERE f.Cust_ID = c.ID
) as forecast,
(SELECT SUM(o.Net_Sales)
FROM orders o
WHERE o.Cust_ID = c.ID
) as net_sales
FROM customers c
ORDER BY c.ID ASC;
With the right indexes in the second tables (budget(cust_id, value), and so on), this may actually be faster than a JOIN approach.

Getting sum of two rows in an inner joined table

I have these two tables;
trips
id
date
revenue
1
01/01/2020
5000
2
01/01/2020
3000
3
02/01/2020
4000
4
02/01/2020
2000
expenses
id
tripid
amount
1
1
500
2
1
300
3
2
400
4
2
200
5
2
700
I would like to get the sum of revenue collected in a day AND sum of expenses in a day. I have the following sql which gives me results but the sums are entirely wrong.
SELECT i.id, sum(i.revenue) as total, i.date trip , sum(c.amount) as exp, c.tripid expenses FROM trip i INNER JOIN expenses c ON i.id = c.tripid GROUP BY i.date ORDER BY trip DESC
You can preaggregate the expenses by trip, and then aggregate again in the outer query:
select t.date, sum(t.revenue) as revenue, coalesce(sum(e.expense), 0) as expense
from trips t
left join (
select tripid, sum(amount) as expense
from expenses
group by tripid
) e on e.tripid = t.id
group by t.date

SQL : How do I sum all null values into another value of specific row?

I am using Mysql.
I have 2 tables:
table Sales:
S_Id DATE Total_Price Employee_Id
1 1/02/2014 5636 10
2 1/04/2014 148 999
3 1/05/2014 101 30
4 1/06/2014 959 40
5 1/02/2014 3000 10
6 1/04/2014 4992 13
7 1/05/2014 472 11
8 1/06/2014 8483 40
table employees:
Employee_Id First_Name Last_Name Address City
10 brock li a1 berlin
20 dan santiago a2 london
30 alex brookmen a3 rome
40 michael gold a4 amsterdam
50 john bisping a5 barcelona
999 tony oneale a6 munich
the employees table holding only active employees
but still the table Sales holding all sales, including sales of not active employees
my question:
I want to make the top 4 employees who made the max of sum sales per only active employees (by sum Sales.Total_Price) at year 2015
Employee_Id = 999 will include his sum of sales and also the sum of all the Sales price that made by not active employees.
Employee_Id = 999 is an active employee
therefor result should be:
Employee_Id First_Name Last_Name Address City total sales
10 brock li a1 berlin 8636
20 dan santiago a2 london 8483
999 tony oneale a6 munich 5612
40 michael gold a4 amsterdam 959
therefor I tried to make a left join between table Sales to employees and sum all the prices when employees.Employee_Id = null
my code:
SELECT s.employee_id
, e.first_name
, e.last_name
, e.address
, e.city
, CASE WHEN ISNULL(e.employee_id) OR e.employee_id = 999
THEN SUM(s.total_price)
ELSE SUM(s.total_price) END 'total sales'
FROM sales s
LEFT
JOIN employees e
ON s.employee_id = e.employee_id
WHERE YEAR(s.date) = 2015
GROUP
BY s.employee_id
, e.first_name
, e.last_name
ORDER
BY SUM(s.total_price) DESC
LIMIT 4
but it didn't succeed:
it didn't sum all the sales of null values (not active employees) into the row of Employee_Id = 999
it still can be that not active employees will be presented in the top 4 sales when I only want active employees in the list
also, if you know a better way to sum max 4 sales per employee it will be great for me to learn
You have to first sum all the details from sales table and then use left join. Something like below might help you -
SELECT E2.*, Total_Sales
FROM (SELECT emp.employee_id
,SUM(total_price) Total_Sales
FROM (SELECT CASE WHEN e.employee_id IS NULL OR e.employee_id = 999 THEN 999 ELSE s.employee_id END employee_id
, e.first_name
, e.last_name
, e.address
, e.city
, s.total_price
FROM Sales s
LEFT JOIN employees e ON s.employee_id = e.employee_id) emp
GROUP BY emp.employee_id) s2
JOIN employees E2 on s2.employee_id = E2.employee_id
ORDER BY s.total_sales DESC
LIMIT 4
Here is the Db-Fiddle -
https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=748fd944384d80e717c08b3a2057f5c6
thank you Ankit Bajpai, this is the final answer
select employees.*, Total_Sales
from (select emp.Employee_Id,
sum(Total_Price) as 'Total_Sales'
from (select case when employees.employee_id is null or employees.Employee_Id = 999 then 999 else Sales.Employee_Id end Employee_Id,
Sales.Total_Price
from actiview_task.Sales
left join actiview_task.employees
on Sales.Employee_Id = employees.Employee_Id
where year(DATE)= 2015) emp
group by emp.Employee_Id) empGroupBy
join actiview_task.employees on empGroupBy.Employee_Id = employees.Employee_Id
order by Total_Sales desc
limit 4

Join table with multiple column result in single Column

I have two table as
Country table
id name
1 Nepal
2 India
Salary Table
id Countryid termid amount
1 1 1 100
1 1 2 500
3 2 1 200
4 2 2 400
i want result as
CountryName basicSalary allowance
Nepal 100 500
India 200 400
Here term 1 is basic salary and 2 is allowance
You can try the below query
SELECT c.name,
SUM(CASE WHEN s.termid = 1 THEN amount ELSE 0 END) basicSalary,
SUM(CASE WHEN s.termid = 2 THEN amount ELSE 0 END) allowance
FROM Country c
JOIN Salary s on c.id = s.Countryid
GROUP BY c.name;
Hope this would help you out.
Just join the table twice (use aliases)
select c.name as CountryName,
s1.amount as basicSalary
s2.amount as allowance
from Country c
left join Salary s1
on s1.countryid=c.id
and s1.termid = 1
left join Salary s2
on s2.countryid=c.id
and s2.termid = 2