Getting the sum of column grouped by date - mysql

Was wondering if there is a way to get the sum of the stock_case column for items with the same date_of_export ?
Updated with fiddle here and some relevant data:
https://www.db-fiddle.com/f/szC1Ftj3ZGEC24gSYp6ad4/4
The expected output would be this:
This is the query used
SELECT
st.product_code,
st.date_of_export,
st.best_before_date,
st.stock_case,
(
SELECT
SUM(st2.stock_case)
FROM
stock_tracking AS st2
WHERE
st2.product_code IN ('MGN003')
AND MONTH(st2.date_of_export) IN (07)
AND YEAR(st2.date_of_export) IN (2018)
AND st2.stock_case != 0
) AS total
FROM
stock_tracking st
WHERE
product_code IN ('MGN003')
AND MONTH(st.date_of_export) IN (07)
AND YEAR(st.date_of_export) IN (2018)
AND stock_case != 0
and my results
Would like to have a total column like 16, 16, 16, ... , 19, etc
For another case I used a subquery like so
SELECT
d.products_name,
stock_case,
st.date_of_export,
st.best_before_date,
st.product_code,
(SELECT
SUM(st2.stock_case)
FROM
stock_tracking AS st2
WHERE
DATE(st2.date_of_export) = (SELECT
DATE(tmp.last_update)
FROM
(SELECT
date_of_export AS last_update
FROM
stock_tracking
ORDER BY date_of_export DESC
LIMIT 1) AS tmp
WHERE
product_code = 'MGN003')) AS total
FROM
stock_tracking st
LEFT JOIN
products AS p ON p.products_model = st.product_code
LEFT JOIN
products_description AS d ON d.products_id = p.products_id
WHERE
product_code = 'MGN003'
AND d.language_id = 2
AND DATE(st.date_of_export) = (SELECT
DATE(tmp.last_update)
FROM
(SELECT
date_of_export AS last_update
FROM
stock_tracking AS st
ORDER BY date_of_export DESC
LIMIT 1) AS tmp)
with this result:

You can write a subquery to sum(stock_case) by date_of_export, then self join on Date, then you can get your expect result.
SELECT
s.product_name,
s.date_of_export,
s.best_before_date,
s.product_code,
s.stock_case,
t.totle
FROM
stock_tracking s
INNER JOIN
(
SELECT SUM(stock_case) totle,date_of_export dt
FROM stock_tracking
where
product_code = 'MGN003'
AND MONTH(date_of_export) =07
AND YEAR(date_of_export) =2018
AND stock_case != 0
GROUP BY date_of_export
) t on DATE_FORMAT(s.date_of_export, "%d-%m-%Y") = DATE_FORMAT(t.dt, "%d-%m-%Y")
where
s.product_code = 'MGN003'
AND MONTH(s.date_of_export) =07
AND YEAR(s.date_of_export) =2018
AND s.stock_case != 0
sqlfiddle

Without giving you the exact answer: You should think in the direction of:
SELECT SUM(column) FROM table WHERE ... GROUP BY date
or
SELECT SUM(column), DISTINCT date FROM table WHERE ...
So lookup the way GROUP BY and DISTINCT work :-)

Related

Group by date and take the last one

This is my table :
What I'm trying to do, is to take the last disponibility of a user, by caserne. Example, I should have this result :
id id_user id_caserne id_dispo created_at
31 21 12 1 2019-10-24 01:21:46
33 21 13 1 2019-10-23 20:17:21
I've tried this sql, but it does not seems to work all the times :
SELECT * FROM
( SELECT id, id_dispo, id_user, id_caserne, MAX(created_at)
FROM disponibilites GROUP BY id_user, id_caserne, id_dispo
ORDER BY created_at desc ) AS sub
GROUP BY id_user, id_caserne
What am I doing wrong ?
I would simply use filtering in the where clause using a correlated subquery:
select d.*
from disponibilites d
where d.created_at = (select max(d2.created_at)
from disponibilites d2
where d2.id_user = d.id_user
);
EDIT:
Based on your comments:
select d.*
from disponibilites d
where d.created_at = (select max(d2.created_at)
from disponibilites d2
where d2.id_user = d.id_user and
d2.id_caserne = d.id_caserne
where date(d2.created_at) = date(d.created_at)
);
You can use a correlated subquery, as demonstrated by Gordon Linoff, or a window function if your RDBMS supports it:
select * from (
select
t.*,
rank() over(partition by id_caserne, id_user order by created_at desc) rn
from disponibilites t
) x
where rn = 1
Another option is to use a correlated subquery without aggregation, only with a sort and limit:
select *
from mytable t
where created_at = (
select created_at
from mytable t1
where t1.id_user = t.id_user and t1.id_caserne = t.id_caserne
order by created_at desc
limit 1
)
With an index on (id_user, id_caserne, created_at), this should be a very efficient option.
you can join your max(created_date) to your original table
select t1.* from disponibilites t1
inner join
(select max(created_at), id_caserne, id
from disponibilites
group by id_caserne, id) t2
on t2.id = t1.id

SQL join table lower date and lower id

I have got the following two tables
START AND REPEAT
START
INSPECID=======SCORE
1--------------3
2--------------1
3--------------4
REPEAT
ID========INSPECID========SCORE========DATE
1---------1---------------9------------12/01/2016
2---------1---------------1------------11/01/2016
3---------2---------------2------------29/01/2016
4---------2---------------4------------01/01/2016
5---------2---------------3------------22/01/2016
6---------2---------------5------------02/01/2016
7---------2---------------1------------11/01/2016
8---------2---------------1------------01/01/2016
9---------3---------------1------------02/01/2016
10--------3---------------2------------09/01/2016
I am expecting as below
INCREASED------1
DECREASED------2
EQUAL----------0
Rules
1) Join tables by INSPECID
2) When more than 1 INSPECID is found in REPEAT table consider the score from the lower date.
3) when both INSPECID is matched and date is matched than consider the lower ID in the REPEAT table, so ID 4 and ID 8 has same date and same INPECTID but consider the ID 4 score which is 4.
Do a self join with REPEAT table to pick the oldest row
select s.*,a.*
from `START` s
join `REPEAT` a on s.INSPECID = a.INSPECID
left join `REPEAT` b on a.INSPECID = b.INSPECID
and case when a.DATE = b.DATE
then a.ID > b.ID
else a.DATE > b.DATE
end
where b.INSPECID is null
For conflict when INSPECID and DATE is same use CASE to choose row with lowest ID
Demo
Updated for desired result set
select t.result,count(t1.result) cnt
from (
select 'Increased' result
union
select 'Decreased' result
union
select 'Equal' result
) t
left join (
select s.score,a.id,a.DATE,
case when s.SCORE > a.SCORE
then 'Increased'
when s.SCORE < a.SCORE
then 'Decreased'
else 'Equal'
end result
from `START` s
join `REPEAT` a on s.INSPECID = a.INSPECID
left join `REPEAT` b on a.INSPECID = b.INSPECID
and case when a.DATE = b.DATE
then a.ID > b.ID
else a.DATE > b.DATE
end
where b.INSPECID is null
) t1 using(result)
group by t.result
Demo
This is a bit tricky. The following uses the group_concat() trick for calculating the first and last scores. It then puts these into the categories that you want:
select w.which, count(r.INSPECID)
from (select 'DECREASING' as which union all
select 'INCREASING' as which union all
select 'EQUAL' as which
) w left join
(select r.INSPECID,
(substring_index(group_concat(score order by date), ',', 1) + 0) as first_score,
(substring_index(group_concat(score order by date desc), ',', 1) + 1) as last_score
from repeat r
group by INSPECID
) r
ON (last_score > first_score and w.which = 'INCREASING') or
(last_score < first_score and w.which = 'DECREASING') or
(last_score = first_score and w.which = 'INCREASING')
group by w.which;
Note that the first table is not necessary.

MySQL SELECT query returns wrong result

I have a table orders(c_id(INT),o_date(DATE),o_price(INT)). I have to find out the id and MAX value of total price for each id in the current year. The table is as follows:
c_id o_date o_price
1 2017-08-27 30
2 2017-05-25 100
2 2017-05-02 20
1 2017-02-23 80
3 2017-01-26 60
4 2016-04-22 10
2 2016-03-15 5
1 2015-09-01 1
I code as follow:
SELECT c_id, MAX(m_price)
FROM ( SELECT c_id, SUM(o_price) AS
m_price FROM orders WHERE
( YEAR(o_date) = YEAR(curdate()) )
GROUP
> BY c_id )AS T
the MAX value return right but the id is not fit with that MAX value. Has anyone a suggestion for my situation? Thank you in advance!!
If there is guaranteed to be only one c_id which has the largest sum, or you don't mind returning only one result in the event of a tie, then you can just LIMIT 1 along with an ORDER BY to get the max result.
SELECT
c_id,
SUM(o_price) AS m_price
FROM orders
WHERE
YEAR(o_date) = YEAR(CURDATE())
GROUP BY c_id
ORDER BY m_price DESC
LIMIT 1
If there could be a tie for maximum sum, then there are workarounds which are slightly uglier, e.g.
SELECT
c_id,
SUM(o_price) AS m_price
FROM orders
WHERE
YEAR(o_date) = YEAR(CURDATE()) AND
GROUP BY c_id
HAVING SUM(o_price) =
(
SELECT MAX(t.m_price)
FROM
(SELECT SUM(o_price) AS m_price FROM orders
WHERE YEAR(o_date) = YEAR(CURDATE()) GROUP BY c_id) t
)
The above query is a bit verbose, and in other databases we would use analytic functions to handle this. But since MySQL does not support these out of the box we need to use another way.
If you have multiple prices, the following is slightly simpler than Tim's code:
SELECT o.c_id, SUM(o.o_price) AS m_price
FROM orders o
WHERE YEAR(o.o_date) = YEAR(CURDATE())
GROUP BY o.c_id
HAVING m_price = (SELECT SUM(o2.o_price)
FROM orders o2
WHERE YEAR(o2.o_date) = YEAR(CURDATE()
GROUP BY c_id
ORDER BY SUM(o2.o_price) DESC
LIMIT 1
);
In practice, using variables might be faster.
SELECT o.*
FROM (SELECT o.*, #maxp := GREATEST(#maxp, m_price)
FROM (SELECT o.c_id, SUM(o.o_price) AS m_price
FROM orders o
WHERE YEAR(o.o_date) = YEAR(CURDATE())
GROUP BY o.c_id
) o CROSS JOIN
(SELECT #maxp := 0) params
) o
WHERE m_price = #maxp;
Adjust your query as
SELECT c_id, MAX(m_price)
FROM ( SELECT c_id, SUM(o_price) AS
m_price FROM orders WHERE
YEAR(o_date) = YEAR(curdate())
)
GROUP BY c_id
My solution for this is:
set #p1 = (SELECT MAX(m_price) FROM
(
SELECT c_id, SUM(o_price) AS m_price FROM orders WHERE
(
YEAR(o_date) = YEAR(curdate())
) GROUP BY c_id
) as T
);
SELECT c_id from (
SELECT c_id, sum(o_price) as m_price from orders WHERE (
YEAR(o_date) = YEAR(curdate())
)group by c_id
) as a WHERE m_price = #p1

SQL SCORE RANKING

I'm working with score ranking on my app for all user score. My problem is I don't know how to return one row for each stud_num.
My query:
SELECT * FROM score WHERE assess_type = 'professional' ORDER BY total_score DESC.
Result:
As you can see I have 3 stud_num and I only want one row per stud_num and the highest score of it.
You can use correlated query like this:
SELECT * FROM score t
WHERE t.assess_type = 'professional'
AND t.total_score = (select max(s.total_score)
from score s
where t.stud_num = s.stud_num)
group by stud_num
The option given by #sagi is good:
SELECT * FROM score t
WHERE t.assess_type = 'professional'
AND t.total_score = (select max(s.total_score)
from score s
where t.stud_num = s.stud_num)
group by stud_num
Another option would be to use an inner join and group by together.
The resulting query would become:
select * from score a
inner join (
SELECT stud_num, max(total_score) tscore FROM `score` group by stud_num) b
on a.stud_num = b.stud_num and total_score= tscore
group by a.stud_num
try it out at sqlfiddle
Use the MAX and GROUP BY functions like this:
SELECT score_id, stud_num, assess_type, total_item, MAX(total_score), average, date_taken
FROM score
WHERE assess_type = 'professional'
GROUP BY stud_num
ORDER BY 5 DESC
Here's my the ans:
SELECT score_id, stud_num, assess_type, total_item, MAX( total_score )
FROM score
WHERE assess_type = 'professional'
GROUP BY stud_num, total_item
ORDER BY MAX( total_score ) DESC

Better optimized SELECT SQL query for 50,000+ records

I have a query which works great for 1000 records or less but now I need to optimize it for 50,000+ records and when I run it on that it just stalls...
Here is my code:
SELECT
b1.account_num,b1.effective_date as ed1,b1.amount as am1,
b2.effective_date as ed2,b2.amount as am2
FROM bill b1
left join bill b2 on (b1.account_num=b2.account_num)
where b1.effective_date = (select max(effective_date) from bill where account_num = b1.account_num)
and (b2.effective_date = (select max(effective_date) from bill where account_num = b1.account_num and effective_date < (select max(effective_date) from bill where account_num = b1.account_num)) or b2.effective_date is null)
ORDER BY b1.effective_date DESC
My objective is to get the latest two effective dates and amounts from one table with many records.
Here is a working answer from your SQL-Fiddle baseline
First, the inner preQuery gets the max date per account. That is then joined to the bill table per account AND the effective date is less than the max already detected.
That is then joined to each respective bill for their amounts.
select
FB1.account_num,
FB1.effective_date as ed1,
FB1.amount as am1,
FB2.effective_date as ed2,
FB2.amount as am2
from
( select
pq1.account_num,
pq1.latestBill,
max( b2.effective_date ) as secondLastBill
from
( SELECT
b1.account_num,
max( b1.effective_date ) latestBill
from
bill b1
group by
b1.account_num ) pq1
LEFT JOIN bill b2
on pq1.account_num = b2.account_num
AND b2.effective_date < pq1.latestBill
group by
pq1.account_num ) Final
JOIN Bill FB1
on Final.Account_Num = FB1.Account_Num
AND Final.LatestBill = FB1.Effective_Date
LEFT JOIN Bill FB2
on Final.Account_Num = FB2.Account_Num
AND Final.secondLastBill = FB2.Effective_Date
ORDER BY
Final.latestBill DESC
In mysql , window analytic function like row_number is not there, so we can simulate the same using variables.
The good thing is, the table is scanned only once with this approach.
A row_number is assigned to each partition which is divided based on ( account number, effective date ) and only 2 rows are selected from each partition.
select account_num,
max(case when row_number =1 then effective_date end) as ed1,
max(case when row_number =1 then amount end) as am1,
max(case when row_number =2 then effective_date end) as ed2,
max(case when row_number =2 then amount end )as am2
from (
select account_num, effective_date, amount,
#num := if(#prevacct= account_num , #num + 1, 1) as row_number,
#prevacct := account_num as dummy
from bill, (select #num:=0, #prevacct := '' ) as var
order by account_num , effective_date desc
)T
where row_number <=2
group by account_num