How to calculated multiple moving average in MySQL - mysql

Using table below, How would get a column for 5 period moving average, 10 period moving average, 5 period exponential moving average.
+--------+------------+
| price | data_date |
+--------+------------+
| 122.29 | 2009-10-08 |
| 122.78 | 2009-10-07 |
| 121.35 | 2009-10-06 |
| 119.75 | 2009-10-05 |
| 119.02 | 2009-10-02 |
| 117.90 | 2009-10-01 |
| 119.61 | 2009-09-30 |
| 118.81 | 2009-09-29 |
| 119.33 | 2009-09-28 |
| 121.08 | 2009-09-25 |
+--------+------------+

The 5-row moving average in your example won't work. The LIMIT operator applies to the return set, not the rows being considered for the aggregates, so changing it makes no difference to the aggregate values.

SELECT AVG(a.price) FROM (SELECT price FROM t1 WHERE data_date <= ? ORDER BY data_date DESC LIMIT 5) AS a;
Replace ? with the date whose MA you need.

SELECT t1.data_date,
( SELECT SUM(t2.price) / COUNT(t2.price) as MA5 FROM mytable AS t2 WHERE DATEDIFF(t1.data_date, t2.data_date) BETWEEN 0 AND 6 )
FROM mytable AS t1 ORDER BY t1.data_date;
Change 6 to 13 for 10-day MA

Related

Getting top 5 results based on difference from last day data

I am running a mysql - 10.1.39-MariaDB - mariadb.org binary- database.
I am having the following table:
| id | date | api_endpoint | ticker | open | high | low | close | volume |
|------|---------------------|--------------|--------|-----------|-----------|-----------|-----------|-----------|
| 18 | 2019-08-07 00:00:00 | daily | AAPL | 195.41000 | 199.56000 | 193.82000 | 199.04000 | 33364400 |
| 19 | 2019-08-06 00:00:00 | daily | AAPL | 196.31000 | 198.07000 | 194.04000 | 197.00000 | 35824800 |
| 20 | 2019-08-05 00:00:00 | daily | AAPL | 197.99000 | 198.65000 | 192.58000 | 193.34000 | 52393000 |
| 21 | 2019-08-02 00:00:00 | daily | AAPL | 205.53000 | 206.43000 | 201.62470 | 204.02000 | 40862100 |
| 44 | 2019-08-01 00:00:00 | monthly | AAPL | 213.90000 | 218.03000 | 206.74000 | 208.43000 | 54017900 |
| 5273 | 1999-09-07 00:00:00 | monthly | AAPL | 73.75000 | 77.93800 | 73.50000 | 76.37500 | 246198400 |
I am calculating returns using mysql:
SELECT *
,(CLOSE - (SELECT (t2.close)
FROM prices t2
WHERE t2.date < t1.date
ORDER BY t2.date DESC
LIMIT 1 ) ) / (SELECT (t2.close)
FROM prices t2
WHERE t2.date < t1.date
ORDER BY t2.date DESC
LIMIT 1 ) AS daily_returns
FROM prices
The above query adds a column daily_returns to my table.
I would like to get the top 5 highest daily_returns. I tried to use ORDER BY, however, this does not work with a calculated column.
Any suggestions how to get the top 5 highest daily_returns?
Update: MySQL 8
SELECT
prices.*,
prices.close - LAG(prices.close) OVER w AS daily_return
FROM prices
WHERE api_endpoint = 'daily'
WINDOW w AS (ORDER BY prices.`date` ASC)
ORDER BY daily_return DESC
LIMIT 5;
MySQL 5.7 & Lower
Use MySQL variable to store close value of last day. Compare it with close value to the current row to do the calculation.
SELECT
*
FROM (
SELECT
prices.*,
(`close` - #old_close) / #old_close AS daily_return, -- Use #old_case, currently it has value of old row, next column will set it to current close value.
#old_close:= `close` -- Set #old_close to close value of this row, so it can be used in next row
FROM prices,
(SELECT #old_close:= 0 as o_c) AS t -- Initialize old_close as 0
WHERE api_endpoint = 'daily'
ORDER BY `date` ASC -- return is calculated based on last day close, so keep it sorted based on ascending order of date
) AS tt
ORDER BY daily_return DESC
LIMIT 5;
Reference: How to get diff between two consecutive rows

How to sum the value and retrieve the result using if condition- using MySQL?

I want to sum every time and check with if condition. If condition matches I want the get the created date of the final matched row.
+------------+----------------------------------+------------+--------+
| id | EMAIL | created | Amt |
+------------+----------------------------------+------------+--------+
| 61 | abc#gmail.com | 1514909390 | 57.00 |
| 25 | xyz#gmail.com | 1515534837 | 360.00 |
| 36 | zccc#abv.com | 1515645391 | 240.00 |
| 22 | vv#aa.com | 1516419622 | 320.40 |
| 48 | aa#xyz.com | 1516706121 | 240.00 |
+------------+----------------------------------+------------+--------+
I try this query but I'm not getting the solution...
select
sum(a.amount) as amt,
if(sum(a.amount)>8000,slp.sal_time,0) as Amt_exceed_date
from employee a
join emp_user u
on a.cmp_id=u.user_id
left join emp_sal as slp
on slp.user_id=a.cmp_id
where
order by slp.sal_time;
Somewhat like row wise sum
select e.*,(
select sum(Amt)
from employee
where created <= e.created
) row_wise_sum
from employee e
having row_wise_sum < 800
order by e.created
desc limit 1
Demo

MYSQL Total required for Query

Hi I am looking for a solution to my inability to understand how I can get an overall total for a column in my query.
This query gets engineers names and the number of jobs they have that are out of SLA i.e. the data the job should have been completed has past and the job has still to be completed.
SELECT Engineer,Job_Status,COUNT(*) as 'Out Of SLA'
FROM import
WHERE (Job_Status = 'P' or Job_Status='P2' or Job_Status='P8')
and (isnull(Job_Completed_Date)
or Job_Completed_Date='0000-00-00')
and (Job_SLA_Due_Date < CURDATE()
)
GROUP BY import.Engineer,Job_Status
The code above produces the following results from the import table.
+----------------+------------+------------+
| Engineer | Job_Status | Out of SLA |
+----------------+------------+------------+
| Andy Beeres | P | 15 |
| Andy Broad | P | 4 |
| Darren Goodwin | P | 6 |
+----------------+------------+------------+
I want to be able to show the total number of the Out of SLA column as well as the rest of the table data if that makes sense something like the table below.
| Engineer | Job_Status | Out of SLA |
|------------- |------------ |------------ |
| Andy Beeres | P | 14 |
| | P2 | 3 |
| | P8 | 1 |
| Total | | 18 |
| Andy Broad | P | 12 |
| | P2 | 2 |
| Total | | 14 |
| Grand Total | | 32 |
Regards
Alan
Use with rollup with group by to get total_sla
According to MySql Docs:
The GROUP BY clause permits a WITH ROLLUP modifier that causes summary output to include extra rows that represent higher-level (that is, super-aggregate) summary operations. ROLLUP thus enables you to answer questions at multiple levels of analysis with a single query.
SELECT Engineer,Job_Status,COUNT(*) as 'Out Of SLA'
FROM import
WHERE (Job_Status = 'P' or Job_Status='P2' or Job_Status='P8')
and (isnull(Job_Completed_Date)
or Job_Completed_Date='0000-00-00')
and (Job_SLA_Due_Date < CURDATE()
)
GROUP BY import.Engineer,Job_Status WITH ROLLUP
One option is to use a subquery which finds the SLA total:
SELECT Engineer,
Job_Status,
COUNT(*) AS `Out Of SLA`,
(SELECT COUNT(*) FROM import) AS total_sla
FROM import
WHERE (Job_Status = 'P' OR Job_Status='P2' OR Job_Status='P8') AND
(ISNULL(Job_Completed_Date) OR Job_Completed_Date = '0000-00-00') AND
Job_SLA_Due_Date < CURDATE()
GROUP BY Engineer,
Job_Status

MySql finding median of a column grouped by another, rather than the median for the whole column

Context:
I'm attempting to take a series of market transactions, and determine the amount of money actually moving per item type. This is pretty much my first attempt at MySql, so the query is ugly, but the following nearly works:
SELECT types.typename,
averages.type,
averages.price,
movement.sold,
( averages.price * movement.sold ) AS value
FROM (SELECT type,
Round(Avg(price)) AS price
FROM orders
GROUP BY type) AS averages
INNER JOIN (SELECT type,
( startingvolume - currentvolume ) AS sold
FROM (SELECT type,
Sum(volume) AS currentVolume,
Sum(volumeentered) startingVolume
FROM orders
GROUP BY type) AS movement
WHERE ( startingvolume - currentvolume ) > 10000
ORDER BY sold) AS movement
ON averages.type = movement.type
INNER JOIN invtypes AS types
ON types.typeid = averages.type
ORDER BY value DESC
LIMIT 10 ;
-
+------------------------------------+-------+---------+------------+------------------+
| typeName | type | price | sold | value |
+------------------------------------+-------+---------+------------+------------------+
| Dirt | 34 | 1904767 | 2670581874 | 5086836224393358 |
| Light Wood | 2629 | 42999 | 2756595 | 118530828405 |
| Dark Wood | 24509 | 47344 | 1107771 | 52446310224 |
| Stone | 21922 | 18386 | 1505884 | 27687183224 |
| Grass | 238 | 5643 | 4554470 | 25700874210 |
| Paper | 3814 | 25635 | 861006 | 22071888810 |
| Iron | 3699 | 320270 | 58833 | 18842444910 |
| Ink | 16275 | 8552 | 2200545 | 18819060840 |
| Loam | 2679 | 5759 | 2608771 | 15023912189 |
| Copper | 672 | 904612 | 14989 | 13559229268 |
+------------------------------------+-------+---------+------------+------------------+
The problem with the data above is that the raw market data is unavoidably corrupted by outliers, as you can see below:
select type, price from orders where type = 34 order by price desc limit 10;
-
+------+-----------+
| type | price |
+------+-----------+
| 34 | 200000000 |
| 34 | 15.99 |
| 34 | 12.06 |
| 34 | 10 |
| 34 | 7.67 |
| 34 | 7.5 |
| 34 | 7.3 |
| 34 | 7.17 |
| 34 | 7.1 |
| 34 | 7.06 |
+------+-----------+
Core problem:
99% of the market data is clean, but the outliers destroy the average, and MySql doesn't seem to have a median function. I've found several examples of how to find the median of an entire column, but I need the median per-item.
How would I determine a per-item median in stead of a per-item mean, or efficiently clean the data of these outliers prior to running the primary query?
Note:
I've tried omitting results via std, but prices of items range from $17 to $10B, while deviation remains relatively low, regardless of price range.
I won't touch your original query because it very complex, but one option you could do would be to use a subquery to remove any statistical outliers. For example, if you wanted to remove any outlier from the orders table whose value is more than say two standard deviations away from the mean you could use:
SELECT t1.type,
t1.price
FROM orders t1
INNER JOIN
(
SELECT type,
AVG(price) AS AVG,
STD(price) AS STD
FROM orders
GROUP BY type
) t2
ON t1.type = t2.type
WHERE t1.price < ABS(2*t2.STD - t2.AVG) -- any value more than 2 standard devations
-- away from the mean is discarded
Demo here:
SQLFiddle

Mysql query data transformation

I am trying to do transformation on a table in Mysql. I can't figure out how to do it. Could anyone tell me how to do it? The input and output is given. I would like to know how it is done?
Input table
+-------------+------------+------------------+-------------------+
| Employee_ID | Start_Date | Termination_Date | Performance_Level |
+-------------+------------+------------------+-------------------+
| 1 | 1/1/2007 | 3/1/2007 | Low |
| 2 | 6/5/2004 | Null | Medium |
| 3 | 4/3/2003 | Null | High |
| 4 | 9/1/2002 | 4/15/2007 | Medium |
| 5 | 4/6/2007 | 11/1/2007 | Low |
| 6 | 7/1/2007 | Null | High |
| 7 | 3/2/2005 | 8/1/2007 | Low |
+-------------+------------+------------------+-------------------+
Ouput Table
+---------+-----------------------------------+-----------------+-------------------+----------------+
| Period | Total_Employees_at_end_of_quarter | High_Performers | Medium_Performers | Low_Performers |
+---------+-----------------------------------+-----------------+-------------------+----------------+
| Q1-2007 | 4 | 1 | 2 | 1 |
| Q2-2007 | 4 | 1 | 1 | 2 |
| Q3-2007 | 4 | 2 | 1 | 1 |
| Q4-2007 | 3 | 2 | 1 | 0 |
+---------+-----------------------------------+-----------------+-------------------+----------------+
This is what I tried
select * from emp
where date(sdate)< date'2007-04-01' and (date(tdate)> date'2007-03-31' or tdate is null);
select * from emp
where date(sdate)< date'2007-07-01' and (date(tdate)> date'2007-06-30' or tdate is null);
select * from emp
where date(sdate)< date'2007-010-01' and (date(tdate)> date'2007-09-30' or tdate is null);
select * from emp
where date(sdate)< date'2008-01-01' and (date(tdate)> date'2007-12-31' or tdate is null);
I have the individual queries but I want a single query which will give the outputs.
The approach taken below is to create a driver table for each quarter, with information about the year and quarter. This is then joined to the employee table, using a non-equijoin. Employees who start in or before the quarter and end after the quarter are active at the end of quarter.
It uses one trick for the date comparisons, which is to convert the year-quarter combination into a quarter count, by multiplying the year by 4 and adding the quarter. This is a convenience for simplifying the date comparisons.
select driver.qtryr, count(*) as TotalPerformers,
sum(Performance_level = 'High') as HighPerformers,
sum(Performance_level = 'Medium') as MediumPerformers,
sum(Performance_level = 'Low') as LowPerformers
from (select 2007 as yr, 1 as qtr, 'Q1-2007' as qtryr union all
select 2007 as yr, 2 as qtr, 'Q2-2007' as qtryr union all
select 2007 as yr, 3 as qtr, 'Q3-2007' as qtryr union all
select 2007 as yr, 4 as qtr, 'Q4-2007' as qtryr
) driver left outer join
Table1 emp
on year(emp.start_date)*4+quarter(emp.start_date) <= driver.yr*4+qtr and
(emp.termination_date is null or
year(emp.termination_date)*4+quarter(emp.termination_date) > driver.yr*4+qtr
)
group by driver.qtryr
sqlfiddle
try this
SELECT QUARTER('2008-04-01');
http://dev.mysql.com/doc/refman/5.6/en/date-and-time-functions.html#function_quarter
and CONCAT()