Convert SumIfs Excel Function to MySQL - mysql

The formula in cell G2 "ReplenQty" is:
=SUMIFS(D:D,A:A,A2,B:B,B2,C:C,">=" & E2,C:C,"<=" &F2)
The formula in cell H2 "RpInVar" is:
=IF($A2<>$A1,ROUND(VAR(IF($A:$A=$A2,$G:$G)),2),0)
I attempted this in MySQL:
SELECT DISTINCT
Part,
Customer,
OrdDt,
OrdQty,
StartDate,
ReplenDate,
SUM(CASE WHEN Part = Part AND Customer = Customer AND OrdDt >= StartDate AND OrdDt <= ReplenDate THEN OrdQty ELSE 0 END) AS ReplenQty,
VARIANCE(CASE WHEN Part = Part AND Customer = Customer AND OrdDt >= StartDate AND OrdDt <= ReplenDate THEN OrdQty ELSE 0 END) AS RpInVar,
FROM
BeforeReplenQty
GROUP BY
Part,
Customer,
OrdDt,
OrdQty,
StartDate,
ReplenDate;
Problem is OrdQty and ReplenQty are the same and RpInVar are all 0.

This query is quite long and complicated but working on this demo: http://sqlfiddle.com/#!9/3b3334/70
One task is to do a sum where order date is between start date and replenish date.
Then get the row where part is new compared to previous row.
The first part of the query is to get the variance, the second subquery is to get the sum of Ordered qty and the sub-query at the bottom is to get the row where part column has changed.
select tab.Part,tab.Customer,tab.OrdDt,tab.OrdQty,tab.StartDate,tab.ReplenDate,tab.ReplenQty,
case when sumtab.Rnk=1 then
(select variance(ReplenQty)
from (select sum(t1.OrdQty) as ReplenQty
from BeforeReplenQty t2
inner join BeforeReplenQty t1
where t2.part=t1.part and t2.customer=t1.customer
and t2.OrdDt between t1.StartDate and t1.ReplenDate
group by t1.Part,t1.Customer,t1.OrdDt,t1.OrdQty,t1.StartDate,t1.ReplenDate) t3) else 0 end as ReplenVar
from (
select t1.*,sum(t1.OrdQty) as ReplenQty
from BeforeReplenQty t2
inner join BeforeReplenQty t1
where t2.part=t1.part and t2.customer=t1.customer
and t2.OrdDt between t1.StartDate and t1.ReplenDate
group by t1.Part,t1.Customer,t1.OrdDt,t1.OrdQty,t1.StartDate,t1.ReplenDate) tab
left join (select part,customer,orddt,rnk
from (
select t.part,t.customer,t.OrdDt,
#s:=CASE WHEN #c <> t.part THEN 1 ELSE #s+1 END AS rnk,
#c:=t.part AS partSet
from (SELECT #s:= 0) s
inner join (SELECT #c:= 'A') c
inner join (SELECT * from BeforeReplenQty
order by Part, Customer, OrdDt) t
) tab
where rnk = 1
) sumtab
on tab.part=sumtab.part and tab.customer=sumtab.customer and tab.orddt=sumtab.orddt;

Related

Mysql consecutive days returning wrong value

I am trying to do a consecutive days count in mysql from a timestamp column. I thought I had this problem solved once long ago - but for some strange reason, it is returning the wrong value.
It should return a single column containing a username, the lowest date with the highest time value in the consecutive date list, and the largest number of consecutive days.
It all works except the largest number of consecutive days is not always correct.
SELECT username, day1, count(distinct date) days from (
SELECT t1.username, t1.date, max(tmp.date) day1 from sites_surfed t1
LEFT JOIN (
SELECT tmp1.date from sites_surfed tmp1
LEFT JOIN sites_surfed tmp2
ON DATE_FORMAT(tmp1.date,'%d/%m/%Y') = DATE_FORMAT(tmp2.date,'%d/%m/%Y')+1
AND tmp1.username = tmp2.username where (tmp1.username = 'viraladmin')
AND (tmp2.date is null))tmp on (t1.date >= tmp.date) where t1.username = 'viraladmin'
GROUP BY t1.date
) fin
WEHERE username = 'viraladmin'
GROUP BY day1 having count(distinct date) > 1 order by days
The below jsfiddle shows an example of it returning 3 for the total amount of consecutive days when it should only be return 2 as only two unique dates are in the table list
http://sqlfiddle.com/#!9/85f6d6/4
What is going on here and how do I fix this?
I've changed
GROUP BY t1.date
to
GROUP BY DATE_FORMAT(t1.date,'%d/%m/%Y')
This seems to be working.
SELECT username, day1, count(distinct date) days from (
SELECT t1.username, t1.date, max(tmp.date) day1 from sites_surfed t1
LEFT JOIN (
SELECT tmp1.date from sites_surfed tmp1
LEFT JOIN sites_surfed tmp2
ON DATE_FORMAT(tmp1.date,'%d/%m/%Y') = DATE_FORMAT(tmp2.date,'%d/%m/%Y')+1
AND tmp1.username = tmp2.username where (tmp1.username = 'viraladmin')
AND (tmp2.date is null))tmp on (t1.date >= tmp.date) where t1.username = 'viraladmin'
GROUP BY DATE_FORMAT(t1.date,'%d/%m/%Y')
) fin
WHERE username = 'viraladmin'
GROUP BY day1 having count(distinct date) > 1 order by days

MySql GROUP BY Max Date

I have a table called votes with 4 columns: id, name, choice, date.
****id****name****vote******date***
****1*****sam*******A******01-01-17
****2*****sam*******B******01-05-30
****3*****jon*******A******01-01-19
My ultimate goal is to count up all the votes, but I only want to count 1 vote per person, and specifically each person's most recent vote.
In the example above, the result should be 1 vote for A, and 1 vote for B.
Here is what I currently have:
select name,
sum(case when uniques.choice = A then 1 else 0 end) votesA,
sum(case when uniques.choice = B then 1 else 0 end) votesB
FROM (
SELECT id, name, choice, max(date)
FROM votes
GROUP BY name
) uniques;
However, this doesn't work because the subquery is indeed selecting the max date, but it's not including the correct choice that is associated with that max date.
Don't think "group by" to get the most recent vote. Think of join or some other option. Here is one way:
SELECT v.name,
SUM(v.choice = 'A') as votesA,
SUM(v.choice = 'B') as votesB
FROM votes v
WHERE v.date = (SELECT MAX(v2.date) FROM votes v2 WHERE v2.name = v.name)
GROUP BY v.name;
Here is a SQL Fiddle.
Your answer are close but need to JOIN self
Subquery get Max date by name then JOIN self.
select
sum(case when T.vote = 'A' then 1 else 0 end) votesA,
sum(case when T.vote = 'B' then 1 else 0 end) votesB
FROM (
SELECT name,Max(date) as date
FROM T
GROUP BY name
) AS T1 INNER JOIN T ON T1.date = T.date
SQLFiddle
Try this
SELECT
choice,
COUNT(1)
FROM
votes v
INNER JOIN
(
SELECT
id,
max(date)
FROM
votes
GROUP BY
name
) tmp ON
v.id = tmp.id
GROUP BY
choice;
Something like this (if you really need count only last vote of person)
SELECT
sum(case when vote='A' then cnt else 0 end) voteA,
sum(case when vote='B' then cnt else 0 end) voteB
FROM
(SELECT vote,count(distinct name) cnt
FROM (
SELECT name,vote,date,max(date) over (partition by name) maxd
FROM votes
)
WHERE date=maxd
GROUP BY vote
)
PS. MySQL v 8
select
name,
sum( case when choice = 'A' then 1 else 0 end) voteA,
sum( case when choice = 'B' then 1 else 0 end) voteB
from
(
select id, name, choice
from votes
where date = (select max(date) from votes t2
where t2.name = votes.name )
) t
group by name
Or output just one row for the total counts of VoteA and VoteB:
select
sum( case when choice = 'A' then 1 else 0 end) voteA,
sum( case when choice = 'B' then 1 else 0 end) voteB
from
(
select id, name, choice
from votes
where date = (select max(date) from votes t2
where t2.name = votes.name )
) t
Based on #d-shish solution, and since introduction (in MySQL 5.7) of ONLY_FULL_GROUP_BY, the GROUP BY statement must be placed in subquery like this :
SELECT v.`name`,
SUM(v.`choice` = 'A') as `votesA`,
SUM(v.`choice` = 'B') as `votesB`
FROM `votes` v
WHERE (
SELECT MAX(v2.`date`)
FROM `votes` v2
WHERE v2.`name` = v.`name`
GROUP BY v.`name` # << after
) = v.`date`
# GROUP BY v.`name` << before
Otherwise, it won't work anymore !

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.

Advance Select Query - FIFO

I have a table with columns id, item, qty and expiration date. I have to select item with a given total qty based on expiration date. The item that expires first need to be the priority to select. The query works fine if the item has different expiration date but, my problem is if the item has the same expiration date it does not return any row. Please check out the query below.
SELECT t.ID,
t.itemcode,
t.qty,
t.expdate,
t.total,
t.prev_total,
CASE WHEN t.total > 500 THEN 500 - t.prev_total ELSE t.qty END AS total
FROM
(
SELECT t1.ID,
t1.itemcode,
t1.qty,
t1.expdate,
(SELECT SUM(t2.qty) FROM put_in t2
WHERE t2.expdate <= t1.expdate AND t2.itemcode = 'ITEM01') AS total,
COALESCE((SELECT SUM(t2.qty) FROM put_in t2
WHERE t2.expdate < t1.expdate AND t2.itemcode = 'ITEM01'), 0) AS prev_total
FROM put_in t1
WHERE t1.itemcode = 'ITEM01'
) t
WHERE t.total - t.qty < 500 AND
t.itemcode = 'ITEM01'
ORDER BY t.expdate;

mysql find date where no row exists for previous day

I need to select how many days since there is a break in my data. It's easier to show:
Table format:
id (autoincrement), user_id (int), start (datetime), end (datetime)
Example data (times left out as only need days):
1, 5, 2011-12-18, 2011-12-18
2, 5, 2011-12-17, 2011-12-17
3, 5, 2011-12-16, 2011-12-16
4, 5, 2011-12-13, 2011-12-13
As you can see there would be a break between 2011-12-13 and 2011-12-16. Now, I need to be able say:
Using the date 2011-12-18, how many days are there until a break:
2011-12-18: Lowest sequential date = 2011-12-16: Total consecutive days: 3
Probably: DATE_DIFF(2011-12-18, 2011-12-16)
So my problem is, how can I select that 2011-12-16 is the lowest sequential date? Remembering that data applies for particular user_id's.
It's kinda like the example here: http://www.artfulsoftware.com/infotree/queries.php#72 but in the reverse.
I'd like this done in SQL only, no php code
Thanks
SELECT qmin.start, qmax.end, DATE_DIFF( qmax.end, qmin.start ) FROM table AS qmin
LEFT JOIN (
SELECT end FROM table AS t1
LEFT JOIN table AS t2 ON
t2.start > t1.end AND
t2.start < DATE_ADD( t1.end, 1 DAY )
WHERE t1.end >= '2011-12-18' AND t2.start IS NULL
ORDER BY end ASC LIMIT 1
) AS qmax
LEFT JOIN table AS t2 ON
t2.end < qmin.start AND
t2.end > DATE_DIFF( qmin.start, 1 DAY )
WHERE qmin.start <= '2011-12-18' AND t2.start IS NULL
ORDER BY end DESC LIMIT 1
This should work - left joins selects one date which can be in sequence, so max can be fineded out if you take the nearest record without sequential record ( t2.anyfield is null ) , same thing we do with minimal date.
If you can calculate days between in script - do it using unions ( eg 1. row - minimal, 2. row maximal )
Check this,
SELECT DATEDIFF((SELECT MAX(`start`) FROM testtbl WHERE `user_id`=1),
(select a.`start` from testtbl as a
left outer join testtbl as b on a.user_id = b.user_id
AND a.`start` = b.`start` + INTERVAL 1 DAY
where a.user_id=1 AND b.`start` is null
ORDER BY a.`start` desc LIMIT 1))
DATEDIFF() show difference of the Two days, if you want to number of consecutive days add one for that result.
If it's not a beauty contents then you may try something like:
select t.start, t2.start, datediff(t2.start, t.start) + 1 as consecutive_days
from tab t
join tab t2 on t2.start = (select min(start) from (
select c1.*, case when c2.id is null then 1 else 0 end as gap
from tab c1
left join tab c2 on c1.start = adddate(c2.start, -1)
) t4 where t4.start <= t.start and t4.start >= (select max(start) from (
select c1.*, case when c2.id is null then 1 else 0 end as gap
from tab c1
left join tab c2 on c1.start = adddate(c2.start, -1)
) t3 where t3.start <= t.start and t3.gap = 1))
where t.start = '2011-12-18'
Result should be:
start start consecutive_days
2011-12-18 2011-12-16 3