MySQL moving average calculation using CASE - mysql

How can i edit the ON operator part of my query below such that i would like the current code to work where id<4 (which is t2.id <= t1.id as shown below) so when t1 id=3, t2 is the cumulative id from id=1 to id=3 (as it is now).
but for id >3 I would like the ON operator to be (t2.id=t1.id>=t1.id-2 and <=t1.id) so when t1 id=4, t2.id should be between 2 and 4 inclusive. when t1 id =5, t2.id should be between 3 and 5 inclusive and so on.
I'm doing this because when i calculate col E for ids after id=3, i am only interested in getting the average of the previous 2 rows for C and D on a moving average.
Iam translating my excel formula into SQL so i know what is the correct values for col E.
My query has 2 sub queries and it updates column E. The table and correct data in EXCEL looks like this:
id A B C D E
1 NULL NULL NULL NULL NULL
2 4 6 1 1 1
3 6 9 1.2 1.2 1.2
4 8 7 1.33 0.954 1.143
5 10 5 1.25 0.714 0.982
6 12 2 1.2 0.428 0.814
http://www.sqlfiddle.com/#!2/17a0ad/1
EXCEL formulas (notice that the formulas change after id=3 to a moving average):
id C D E
2 =A2/AVERAGE(A1:A2) =B2/AVERAGE(B1:B2) =(C2+D2)/2
3 =A3/AVERAGE(A1:A3) =B3/AVERAGE(B1:B3) =(C3+D3)/2
4 =A4/AVERAGE(A2:A4) =B4/AVERAGE(B2:B4) =(C4+D4)/2
5 =A5/AVERAGE(A3:A5) =B5/AVERAGE(B3:B5) =(C5+D5)/2
6 =A6/AVERAGE(A4:A6) =B6/AVERAGE(B4:B6) =(C6+D6)/2
Here is my SQL query:
Update followers join
(
SELECT t1.id ,ifnull(t1.A/AVG(t2.A),null) C ,ifnull(t1.B/AVG(t2.B),null) D
FROM followers t1
JOIN followers t2
ON
case when t2.id < 4 then t2.id <= t1.id else t2.id<= t1.id and t2.id>=t1.id-2 end
group by t1.id
) AS tt on(followers.id = tt.id)
SET E = (tt.C + tt.D)/2;
Although this query works, the numbers that i want for col E are not exactly correct. They are correct only for id<=4 but not for id=5 or id=6 in col E.
I believe my syntax for CASE by the ON operator might be wrong.
I ran this query in sql fiddle and got this result:
ID A B E
1(null)(null)(null)
2 4 6 1
3 6 9 1.2
4 8 7 1.14
5 10 5 1.08
6 12 2 0.92
As we can see, the excel and sql output are different.
Thanks,

I think the query that you want is a very slight modification:
Update followers join
(SELECT t1.id, ifnull(t1.A/AVG(t2.A),null) as C, ifnull(t1.B/AVG(t2.B),null) as D
FROM followers t1 JOIN
followers t2
ON (case when t1.id < 4 then t2.id <= t1.id
----------------------------^
else t2.id<= t1.id and t2.id>=t1.id-2
end)
group by t1.id
) tt
on followers.id = tt.id
SET E = (tt.C + tt.D)/2;
You can express the on using basic boolean logic as:
on t2.id <= t1.id and (t1.id < 4 or t2.id >= t1.id - 2)

Related

How to do Nested Queries in SQL

I'm trying to learn SQL, and while I'm slowly learning how to query data, I'm stuck on querying the results of a query. Take this example
I want an SQL statement to do 2 things. Suppose I have 2 tables like the one below (table 1 borrowed from another example on stack overflow)
Table 1:
ID game point time
1 x 5 7:00
1 z 4 11:00
2 y 6 9:00
3 x 2 2:00
3 y 5 4:00
3 z 8 6:00
4 k 0 8:00
Table 2:
id tv chan
1 cab
2 trop
3 start
4 cab
The first thing I want to do is combine certain columns from these tables. I know I can select these columns and do an inner join on ID
However the second thing I want to do is drop all the rows with point value 0, and then have only rows with distinct game name with the lowest point value. So I want the final table to look like this
id game point tv chan
1 z 4 cab
2 y 5 trop
3 x 2 start
Thanks
You could try something like this:
SELECT t1.ID,
t1.game,
t1.point,
t2.tv_chan
FROM Table1 AS t1
INNER JOIN Table2 AS t2 ON t2.id = t1.id
INNER JOIN (SELECT t11.game, MIN(t11.point) AS min_point
FROM Table1 AS t11
WHERE t11.point != 0
GROUP BY t11.game
) AS t3 ON t3.game = t1.game
AND t3.min_point = t1.point
WHERE t1.point != 0
You could use a join with a subquery that group by id and game for obtain the min point
select t1.id, t1.game. t1.point, t2 `tv chan`
from (
select id, game, min(point) point
from table1
where point > 0
group by id, game
) t1
inner join table2 t2 on t1.id = t2.id

Join query with limit results

ID Name
1 A
2 B
3 C
4 D
5 E
6 F
7 G
8 H
9 I
10 J
And other table is
ID Image Date
1 aa.jpg 5/17/2016
1 bb.jpg 5/20/2016
1 aa1.jpg 5/15/2016
2 1.jpg 5/17/2016
3 2.jpg 5/17/2016
3 3.jpg 5/20/2016
4 x.jpg 5/17/2016
8 tt.jpg 5/17/2016
9 ww.jpg 5/21/2016
10 21.jpg 5/17/2016
5 67.jpg 5/17/2016
6 3d.jpg 5/17/2016
7 w3.jpg 4/17/2016
7 y78.jpg 5/17/2016
I have written below query to get this result.
select t1.id,t1.name,t2.image,t2.date
from emp t1
join images t2 ON t1.id = t2.id
result:
ID Name Images Date
2 B 1.jpg 5/17/2016
1 A bb.jpg 5/20/2016
1 A aa1.jpg 5/15/2016
1 A aa.jpg 5/17/2016
.
.
.
Here is my actual problem:
I have to add limit functionality like limit 0,100. in my limit 0,3
So my expectation is if any id coming with query so all id should be come.
I guess your requirement is to choose a set of ID values that yields approximately 100 rows in your result set.
LIMIT makes very little sense without ORDER BY; you're just choosing an unpredictable subset of the result set. So, I will guess you want to ORDER BY ID.
First, you need to figure out which ID values are in your set. That's relatively simple. (I'm using LIMIT 0,5).
SELECT DISTINCT id
FROM (
select t1.id
from emp t1
join images t2 ON t1.id = t2.id
order by t1.id
limit 0,5
) a
Then, use that as a subquery to limit your query. Here's a SqlFiddle demonstrating this (http://sqlfiddle.com/#!9/44430d/4/0).
select t1.id
from emp t1
join images t2 ON t1.id = t2.id
where t1.id IN (
SELECT DISTINCT id
FROM (
select t1.id
from emp t1
join images t2 ON t1.id = t2.id
order by t1.id
limit 0,5
) a
)
order by t1.id
This query is likely to yield more rows than your LIMIT, of course: it augments its result set with extra images as necessary. The SqlFiddle example yields six rows rather than five (http://sqlfiddle.com/#!9/44430d/4/0)

MySQL moving average calculation

How can i edit the ON operator part of my query below such that i would like the current code to work where id<=14 (which is t2.id <= t1.id as shown below) so when t1 id =14, t2 is the cumulative id from 1 to 14 (as it is now).
but for id >14 I would like the ON operator to be (t2.id=t1.id>=t1.id-2 and <=t1.id) so when t1 id=15, t2.id should be between 13 and 15. when t1 id =16, t2.id should be between 14 and 16 and so on.
I'm doing this because when i calculate col E for ids after id=14, i am only interested in getting the average of the previous 2 rows for C and D on a moving average.
My query has 2 sub queries and it updates column E. The table looks like this:
--------------------------
id | A | B | E |
--------------------------
1 | NULL | NULL |NULL|
--------------------------
2 | 4 | 6 |NULL|
--------------------------
3 | 6 | 9 |NULL|
--------------------------
This is my query where i got help from this link: Mysql Nested sub queries unknown column error
Update t join
(SELECT t1.id ,ifnull(t1.A/AVG(t2.A),0) C ,ifnull(t1.B/AVG(t2.B),0) D
FROM t t1
JOIN t t2
ON t2.id <= t1.id
group by t1.id ) AS tt
on(t.id = tt.id)
SET E = (tt.C + tt.D)/2;
Thanks,
where id<=14 (which is t2.id <= t1.id as shown below) so when t1 id =14, t2 is the cumulative id from 1 to 14 (as it is now).
Update t join
(
SELECT t1.id ,ifnull(t1.A/AVG(t2.A),0) C ,ifnull(t1.B/AVG(t2.B),0) D
FROM t t1
JOIN t t2
ON case when t2.id < 15 then t2.id <= t1.id else t2.id=t1.id>=t1.id-2 and <=t1.id end
group by t1.id
) tt on(t.id = tt.id)
SET E = (tt.C + tt.D)/2;

Group dates based on variable periods

i have two tables as follows------
table-1
CalenderType periodNumber periodstartdate
1 1 01-01-2013
1 2 11-01-2013
1 3 15-01-2013
1 4 25-01-2013
2 1 01-01-2013
2 2 15-01-2013
2 3 20-01-2013
2 4 25-01-2013
table2
Incidents Date
xyz 02-01-2013
xxyyzz 03-01-2013
ccvvb 12-01-2013
vvfg 16-01-2013
x3 17-01-2013
x5 24-01-2013
Now i want to find out the number of incidents took place in a given period(the Calendar type may change on runtime like)
the query should look something like this
select .......
from ......
where CalendarType=1
which should return
CalendarType PeriodNumber Incidents
1 1 2
1 2 1
1 3 3
1 4 0
can someone suggest me an approach or any method how this can be achieved.
Note:each period is variable in size.peroid1 may have 10 days period2 may have 5 days etc.
I think this does what you want, although I don't understand how you arrived at your sample output:
select t.CalenderType, t.periodNumber, count(*) as Incidents
from Table1 t
inner join (
select t2.Date, t2.Incidents, max(t1.periodstartdate) as PeriodStartDate
from Table2 t2
inner join Table1 t1 on t2.Date >= t1.periodstartdate
where CalenderType = 1
group by t2.Date, t2.Incidents
) a on t.periodstartdate = a.PeriodStartDate
where CalenderType=1
group by t.CalenderType, t.periodNumber
SQL Fiddle Example
Try this, a bit more general solution,SQLFiddle (Thanks RedFilter for schema):
SELECT t1.CalenderType, t1.periodNumber, count(Incidents)
FROM Table1 t1, Table1 t11, Table2
WHERE
(
(
t1.CalenderType = t11.CalenderType
AND t1.periodNumber = t11.periodNumber - 1
AND Date BETWEEN t1.periodstartdate AND t11.periodstartdate
)
OR
(
t1.periodNumber = (SELECT MAX(periodNumber) FROM Table1 WHERE t1.CalenderType = CalenderType)
AND Date > t1.periodstartdate
)
)
GROUP BY t1.CalenderType, t1.periodNumber
ORDER BY t1.CalenderType, t1.periodNumber

Difference tabels based on Id and unixtimestamp

My table is looking as folowing:
unix_timestap ID value
1351058019 1 500
1351058029 1 505
1351058039 9 105
1351058049 9 200
1351076620 1 520
I would like be able to generate a new column contain the differences between the values per ID between the current value and the first available "past" value. With past I mean that unixtimestamp is not placed in order in the original table.
The output would be:
unix_timestap ID value difference
1351058019 1 500 0
1351058029 1 505 5
1351058039 9 105 0
1351058049 9 200 95
1351076620 1 520 15
If no previous unix_timestamp exists, the value should be zero.
A Hint/tip would be much appreciated.
Thanks
if solution still needed
select
t3.unix_timestamp, t3.id, t3.value, ifnull(t3.value - t5.value, 0) as diff
from test1 t3
join (
SELECT t1.unix_timestamp, t1.id, max(t2.unix_timestamp) as old_stamp
FROM `test1` t1
left join test1 t2 on t1.id = t2.id and t1.unix_timestamp > t2.unix_timestamp
group by t1.unix_timestamp, t1.id) as t4
on t3.unix_timestamp = t4.unix_timestamp and t3.id = t4.id
left join test1 t5 on t4.old_stamp = t5.unix_timestamp and t4.id = t5.id