generate multiple sums from the same query - mysql

I have a table like this:
id person_id total amount date_time
1 101 2000 2001-12-10
2 102 1000 2001-12-10
3 102 3000 2001-12-10
4 102 2000 2001-12-10
5 103 1000 2001-12-11
6 101 1000 2001-12-11
7 102 3000 2001-12-11
8 102 4000 2001-12-11
9 102 4000 2001-12-11
I want the output to be like the one below for the date 2001-12-11
person_101 person_102 person_103
1000 11000 1000
I've tried using the SUM() function but am stuck with the WHERE and JOIN clauses.
How do I solve this?

SELECT
SUM(CASE WHEN person_id = 101 THEN total_amount ELSE 0 END) as person_101,
SUM(CASE WHEN person_id = 102 THEN total_amount ELSE 0 END) as person_102,
SUM(CASE WHEN person_id = 103 THEN total_amount ELSE 0 END) as person_103
FROM
my_table
WHERE
date_time ='2001-12-11'

You need to pivot the data, unfortunately this is not dynamic in mysql, try this:
SELECT
SUM(IF(person_id = 101,total_amount,0)) as person_101,
SUM(IF(person_id = 102,total_amount,0)) as person_102,
SUM(IF(person_id = 103,total_amount,0)) as person_103
FROM
my_table
WHERE
date_time = '2001-12-11'
Also you can do this pivot split by date , you just need to have it in the field list and group by it :
SELECT
date_time,
SUM(IF(person_id = 101,total_amount,0)) as person_101,
SUM(IF(person_id = 102,total_amount,0)) as person_102,
SUM(IF(person_id = 103,total_amount,0)) as person_103
FROM
my_table
GROUP BY
date_time

Related

Optimising a JOIN to the latest data in a second table in MySQL

We are having a problem optimising a set of queries that all follow the same pattern.
The basic scenario is that we have a table (hours) that records the weekly hours worked by an individual, against project codes.
We have a second table (rates) which records the historical hourly rates for each individual.
We want to report the total salary (hours * rate) for each project in the hours table
The query to return all the hours that we are interested in is
SELECT hours_job, SUM(hour_value) AS total_hours
FROM hours_table
WHERE hours_job_status = "1"
GROUP BY hours_job
We need to join that to the rates table, on the latest rates_date, however an individual may, in edge cases, have >1 rate on a particualr date, and in these cases we want the MAX rate
This is out current attempt, which is extremenly slow
SELECT hours_job, SUM(hour_value * rate_value) AS salary
FROM hours_table
JOIN rates_table ON rate_person_id = hours_person_id
WHERE hours_job_active = "1"
AND rate_date = (
SELECT MAX(rate_date)
FROM rates_table
WHERE hours_person_id = rate_person_id
AND hours_week >= rate_date
AND rate_active = 1
)
AND rt_rate = (
SELECT MAX(rate_value)
FROM rates_table
WHERE hours_person_id = rate_person_id
AND hours_week >= rate_date
AND rate_active = 1
GROUP BY hours_job
ORDER BY hours_job;
As suggested here are is a snippet of the data and expected result
hours_table
hours_id hours_person_id hours_week hours_job hours_value hours_job_active
1 1 "2020-06-12" 100 20 1
2 1 "2020-06-12" 101 10 1
3 1 "2020-06-12" 102 10 1
4 2 "2020-06-12" 100 30 1
5 2 "2020-06-12" 102 10 1
rates_table
rate_id rate_person_id rate_date rate_value rate_type rate_active
1 1 "2020-04-01" 25.00 A 1
2 1 "2019-04-01" 20.00 A 1
3 1 "2018-04-01" 18.00 A 1
4 2 "2020-04-01" 20.00 A 1
5 2 "2020-04-01" 18.00 Y 1
Expected result would be
hours_job salary
100 1100 ((20*25) + (30*20))
101 250 (10*25)
102 450 ((10*25) + (20*10))
It's been long since I've had one of this problems to optimize, and without a test db I can't make sure this works, but have you tried something like this?
SELECT hours_job, SUM(hour_value * MAX(rate_value)) AS salary
FROM hours_table
JOIN rates_table ON rate_person_id = hours_person_id
WHERE hours_job_active = "1"
AND hours_week >= rate_date
AND rate_active = 1
GROUP BY hours_job, rates_date
ORDER BY hours_job;

MySQL Query to find users still inside room

Below is my database table, where I will have Check In and Check Out entry records for attending the conference room.
id registration_id roomno day type
1 101 1 2 In
2 103 1 2 In
3 101 1 2 Out
4 105 1 2 In
5 103 1 2 Out
6 101 1 2 In
7 103 1 2 In
8 101 1 2 Out
9 105 1 2 In
10 103 1 2 Out
Now, I want to select those records, which are still attending the conference. Condition is like their last record should be type = In. There can be multiple In/Out entries for each user during a day.
Please let me know the quickest possible MySQL query.
Thanks
Answer which I ended up using:
select * from `registrations_inouts` t
group by t.registration_id
having max(id) = max(case when type = 'In' then id end)
order by rand() limit 1;
Here is one method using not exists:
select *
from t
where t.type = 'In' and
not exists (select 1
from t t2
where t2.registration_id = t.registration_id and t2.type = 'Out'
);
Another method uses conditional aggregation:
select t.registration_id
from t
group by t.registration_id
having max(id) = max(case when type = 'In' then id end);
Note: both of these assume that the ids are assigned sequentially in time, so larger ids are later in time.

SQL Query - How to get difference of sum of multiple of cells of rows

I need to get the difference of the sums of two fields which are in single table (really sorry if this is confusing), please read on for an example
Id type account_id stock_id volume price value
==========================================================
1 BUY 1 1 5 500 2500
2 BUY 1 4 30 200 6000
6 BUY 1 1 10 500 5000
7 SELL 1 1 3 500 1500
8 SELL 1 1 2 500 1000
9 SELL 1 4 20 120 2400
Above is my sample data and I would my SQL query result to be something like,
account_id stock_id volume totalAmount
============================================
1 1 10 5000
1 4 10 3600
basically here I am trying to get the total buy value of unique account & stock combination and subtract with the total sell value
Any help here would be highly appreciated.
Thanks in advance
Fiddle Test:
http://sqlfiddle.com/#!2/53035/1/0
select account_id,
stock_id,
sum(case when type = 'BUY' then volume else -volume end) as volume,
sum(case when type = 'BUY' then value else -value end) as totalamount
from tbl
group by account_id,
stock_id
having sum(case when type = 'BUY' then volume else -volume end) <> 0
I added the HAVING clause based on your comment.
Just to reduce duplication I would change Brian's code to this:
SELECT
account_id,
stock_id,
SUM(volume * type_sign) as total_volume,
SUM(value * type_sign) as total_value
FROM
(select t.*, case when type = 'BUY' then 1 else -1 end as type_sign
from tbl) t
GROUP BY account_id,
stock_id
select buy.account_id,buy.stock_id,(buy.volume-sell.volume) volume,(buy.totalAmount-sell.totalAmount) totalAmount from
(select account_id,stock_id,sum(volume) volume,sum(value) totalAmount from stock
where type = 'BUY'
group by account_id,stock_id) buy
inner join
(select account_id,stock_id,sum(volume) volume,sum(value) totalAmount from stock
where type = 'SELL'
group by account_id,stock_id) sell
on buy.account_id = sell.account_id and buy.stock_id = sell.stock_id

How to build this query?

i have table
http://oi58.tinypic.com/2s7xreo.jpg
id action_date type item_id quantity
--- ----------- ---- ------- --------
87 4/25/2014 1 s-1 100
88 4/1/2014 1 s-1 150
89 4/4/2014 1 s-1 200
90 4/3/2014 1 s-2 222
91 4/7/2014 1 s-2 10
96 4/4/2014 1 s-2 8
97 4/22/2014 1 s-2 8
98 4/21/2014 2 s-1 255
99 4/5/2014 2 s-1 6
100 4/6/2014 2 s-2 190
101 4/6/2014 2 s-3 96
102 4/8/2014 2 s-1 120
103 4/15/2014 2 s-2 3
104 4/16/2014 2 s-2 3
type column which mean if 1 this is in item to my shop >>> if 2 this is out item from my shop >>
i need query to give me result like this
item in out net
s1 300 195 105
and so on >>
how to write query that give me this result >>
and if i must but them in tow table >> table for the in and table for the out if that help >>> how it build the query >
and thanx in advance :)
note :: i am work on access
One way to get the result is to use an aggregate function on an expression that conditionally returns the value of quantity, based on the value of type.
SELECT t.item
, SUM(CASE WHEN t.type = 1 THEN t.quantity ELSE 0 END) AS in_
, SUM(CASE WHEN t.type = 2 THEN t.quantity ELSE 0 END) AS out_
, SUM(CASE WHEN t.type = 1 THEN t.quantity ELSE 0 END)
- SUM(CASE WHEN t.type = 2 THEN t.quantity ELSE 0 END) AS net_
FROM mytable t
GROUP BY t.item
This approach works in Oracle, as well as MySQL and SQL Server.
(If you remove the SUM() aggregate function and the GROUP BY clause, you can see how that CASE expression is working. The query above gives the result you specified, this one is just a demonstration that helps "explain" how that query works.)
SELECT t.item
, CASE WHEN t.type = 1 THEN t.quantity ELSE 0 END AS in_
, CASE WHEN t.type = 2 THEN t.quantity ELSE 0 END AS out_
, t.*
FROM mytable t
UPDATE
Unfortunately, Microsoft Access doesn't support CASE expressions. But Access does have an iif function. The same approach should work, the syntax might be something like this:
SELECT t.item
, SUM(iif(t.type = 1, t.quantity, 0) AS in_
, SUM(iif(t.type = 2, t.quantity, 0) AS out_
, SUM(iif(t.type = 1, t.quantity, 0)
- SUM(iif(t.type = 2, t.quantity, 0) AS net_
FROM mytable t
GROUP BY t.item
SELECT item_id,
In_col,
Out_col,
(In_col - Out_col) AS Net
FROM
(SELECT item_id,
sum(CASE WHEN TYPE = 1 THEN quantity END) AS In_col,
sum(CASE WHEN TYPE = 2 THEN quantity END) AS Out_col
FROM TABLE
GROUP BY item_id) AS t1;
I hope this what you need.

SELECT without grouping

How get results without grouping it?
My table
id user_id amount currency_id type status
5 2 2.00 1 0 0
6 3 3.00 1 0 0
4 1 1.00 1 0 0
7 4 4.00 1 0 0
8 5 3.00 1 1 0
I do the following select
SELECT id, user_id, amount, currency_id, SUM( amount )
FROM market
WHERE amount <=3
AND type = 0
AND status = 0
Result:
id user_id amount currency_id SUM( amount )
5 2 2.00 1 6.00
How get this result:
id user_id amount currency_id SUM( amount )
5 2 2.00 1 0 6.00
6 3 3.00 1 0 6.00
4 1 1.00 1 0 6.00
If your intent is to return individual records that meet this criteria AND sum them up and you don't actually need the SUM value as a field on every row (not sure why you would), then I would suggest looking at the GROUP BY ... WITH ROLLUP modifier. It works like this:
SELECT id, user_id, SUM(amount) AS `amounts`, currency_id
FROM market
WHERE amount <=3
AND type = 0
AND status = 0
GROUP BY id WITH ROLLUP
Here I am grouping by id because this will keep the individual records intact as this value is unique
You output would look like this:
id user_id amounts currency_id
5 2 2.00 1
6 3 3.00 1
4 1 1.00 1
NULL 3 6.00 1
Note the last record provides the rollup to the SUM() function. Also note that values for user_id and currency_id in the rollup row are indeterminate as they were not part of the GROUP BY or aggregation. As such, they have no meaning.
You can do join
SELECT id,
user_id,
amount,
currency_id,
a.totalAmount
FROM market
CROSS JOIN
(
SELECT SUM(amount) totalAmount
FROM market
WHERE amount <=3
AND type = 0
AND status = 0
) a
WHERE amount <=3
AND type = 0
AND status = 0
or using inline subquery,
SELECT id,
user_id,
amount,
currency_id,
(
SELECT SUM(amount) totalAmount
FROM market
WHERE amount <=3
AND type = 0
AND status = 0
) totalAmount
FROM market
WHERE amount <=3
AND type = 0
AND status = 0
Here you go:
SELECT id, user_id, amount, currency_id, t2.total
FROM market, (
SELECT SUM(amount) AS total
FROM market
WHERE amount <=3
AND type = 0
AND status = 0
) AS t2
WHERE amount <=3
AND type = 0
AND status = 0