Mysql timerange row neighbour - mysql

have a table (fruits) like this:
+--------------+-------------+------+-----+---------------------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------------+-------------+------+-----+---------------------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| time | timestamp | NO | | CURRENT_TIMESTAMP | |
| fruit | varchar(16) | NO | | NULL | |
+--------------+-------------+------+-----+---------------------+----------------+
How can I select rows that have a "row neighbour" within the timerange of +/- 10 seconds?
This is my sample data:
INSERT INTO `fruits` VALUES (1,'2016-01-19 02:14:07','apple'),(2,'2016-01-19 02:14:13','banana'),(3,'2017-06-08 06:59:12','melon'),(4,'2017-06-08 06:57:12','strawberry');
This is my expected result set:
1 2016-01-19 02:14:07
2 2016-01-19 02:14:13
The rows with the id 3 and 4 have no neighbours within 10 seconds (+/-).
I thought I could do something like this:
SELECT TIME
FROM fruits
WHERE (DATE_SUB(TIME , INTERVAL 10 SECOND) > ANY
(SELECT TIME
FROM fruits))
AND (DATE_ADD(TIME , INTERVAL 10 SECOND) < ANY
(SELECT TIME
FROM fruits));
Which didn't work out well and is very complicated.

Related

How to calculate a moving average in MYSQL

I have an application that stores stock quotes into my MySQL database.
I have a table called stock_history:
mysql> desc stock_history;
+-------------------+---------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------------+---------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| date | date | NO | MUL | NULL | |
| close | decimal(12,5) | NO | MUL | NULL | |
| dmal_3 | decimal(12,5) | YES | MUL | NULL | |
+-------------------+---------------+------+-----+---------+----------------+
5 rows in set (0.01 sec)
These are all the values in this table:
mysql> select date, close, dmal_3 from stock_history order by date asc;
+------------+----------+----------+
| date | close | dmal_3 |
+------------+----------+----------+-
| 2000-01-03 | 2.00000 | NULL |
| 2000-01-04 | 4.00000 | NULL |
| 2000-01-05 | 6.00000 | NULL |
| 2000-01-06 | 8.00000 | NULL |
| 2000-01-07 | 10.00000 | NULL |
| 2000-01-10 | 12.00000 | NULL |
| 2000-01-11 | 14.00000 | NULL |
| 2000-01-12 | 16.00000 | NULL |
| 2000-01-13 | 18.00000 | NULL |
| 2000-01-14 | 20.00000 | NULL |
+------------+----------+----------+-
10 rows in set (0.01 sec)
I am guaranteed that there will be 0 or 1 record for each date.
Can I write a single query that will insert the three-day moving average (ie: the average closing prices of that day and the two previous trading days before it) into the dmal_3 field? How?
When the query is done, I want the table to look like this:
mysql> select date, close, dmal_3 from stock_history order by date asc;
+------------+----------+----------+
| date | close | dmal_3 |
+------------+----------+----------+
| 2000-01-03 | 2.00000 | NULL |
| 2000-01-04 | 4.00000 | NULL |
| 2000-01-05 | 6.00000 | 4.00000 |
| 2000-01-06 | 8.00000 | 6.00000 |
| 2000-01-07 | 10.00000 | 8.00000 |
| 2000-01-10 | 12.00000 | 10.00000 |
| 2000-01-11 | 14.00000 | 12.00000 |
| 2000-01-12 | 16.00000 | 14.00000 |
| 2000-01-13 | 18.00000 | 16.00000 |
| 2000-01-14 | 20.00000 | 18.00000 |
+------------+----------+----------+
10 rows in set (0.01 sec)
That is what I call a good challenge. My solution first creates a counter for the values and uses it as a table. From it I select everything and join with the same query as a subquery checking the position of the counter on both. Once the query works it just need an inner join with the actual table to do the update. Here it is my solution:
update stock_history tb1
inner join
(
select a.id,
case when a.step < 3 then null
else
(select avg(b.close)
from (
select hh.*,
#stp:=#stp+1 stp
from stock_history hh,
(select #sum:=0, #stp:=0) x
order by hh.dt
limit 17823232
) b
where b.stp >= a.step-2 and b.stp <= a.step
)
end dmal_3
from (select h1.*,
#step:=#step+1 step
from stock_history h1,
(select #sum:=0, #step:=0) x
order by h1.dt
limit 17823232
) a
) x on tb1.id = x.id
set tb1.dmal_3 = x.dmal_3;
I changed some columns names for easiness of my test. Here it is the working SQLFiddle: http://sqlfiddle.com/#!9/e7dc00/1
If you have any doubt, let me know so I can clarify!
Edit
The limit 17823232 clause was added there in the subqueries because I don't know which version of MySql you are in. Depending on it (>= 5.7, not sure exactly) the database optimizer will ignore the internal order by clauses making it not work the way it should. I just chose a random big number usually you can use the maximum allowed.
The only column with different colunm name between your table and mine is the date one which I named dt because date is a reserved word and you should use backticks ( ` ) to use such columns, therefore I will left it as dt in above query.

SELECT Rows Older Than Date Only If Row Does Not Have Row Newer Than Date

I have the below table where I would like to 'prune' out nicks who have not gained points in 1 week. I'm new to MySQL and I'm not sure how to best SELECT these rows. Your help is greatly appreciated!
Here is what I have so far that is not yielding correct results. The results this yields are nicks who have earned points at any time it seems.
SELECT * FROM points_log p1
INNER JOIN points_log p2 ON p1.nick = p2.nick
AND p1.dt < NOW() - INTERVAL 1 WEEK
WHERE p2.dt > NOW() - INTERVAL 1 WEEK LIMIT 10;
Here is the table:
mysql> describe points_log;
+-------------------+-----------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------------+-----------------------+------+-----+---------+----------------+
| id | mediumint(8) unsigned | NO | PRI | NULL | auto_increment |
| nick | char(25) | NO | PRI | NULL | |
| amount | decimal(10,4) | YES | MUL | NULL | |
| stream_online | tinyint(1) | NO | MUL | NULL | |
| modification_type | tinyint(3) unsigned | NO | MUL | NULL | |
| dt | datetime | NO | PRI | NULL | |
+-------------------+-----------------------+------+-----+---------+----------------+
6 rows in set (0.00 sec)
You can get the nicks who have scored in the past week using an aggregation:
SELECT pl.nick
FROM points_log pl
GROUP BY pl.nick
HAVING MAX(pl.dt) < NOW() - INTERVAL 1 WEEK;
I'm not sure what you want as final output, but this will return the nicks that have scored in the past week.

mysql average latest 5 rows

I have table:
describe tests;
+-----------+-----------+------+-----+-------------------+-----------------------------+
| Field | Type | Null | Key | Default | Extra |
+-----------+-----------+------+-----+-------------------+-----------------------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| line_id | int(11) | NO | | NULL | |
| test_time | timestamp | NO | | CURRENT_TIMESTAMP | on update CURRENT_TIMESTAMP |
| alarm_id | int(11) | YES | | NULL | |
| result | int(11) | NO | | NULL | |
+-----------+-----------+------+-----+-------------------+-----------------------------+
And I execute query:
SELECT avg(result) FROM tests WHERE line_id = 4 ORDER BY test_time LIMIT 5;
which I want to generate average of 5 latest results.
Still something is not ok, because query generates average of all table data.
What can be wrong?
If you want the last five rows, then you need to order by the time column in descending order:
select avg(result)
from (select result
from tests
where line_id = 4
order by test_time desc
limit 5
) t
the guy before submitted something link that
for my it works
select avg( id ) from ( select id from rand limit 5) as id;
Only one result set will be returned because of the AVG function.

MySQL - Select last data inserted in last 5 days skip missing records for days

I want to select the data that was inserted in the last 5 days, and if the rows are missing for that day then move on to the previous day, but it always have to return rows from the last 5 days.
The column which i'm trying to match is a DATETIME column
I've tried using this query
select * from `thum_{ROH}` where date >= NOW() - INTERVAL 5 DAY;
Now this return data from 2013-12-24 to 2013-12-22 because data on 2013-12-25 and 2013-12-26
is not available.
How can i modify the query to make it return the last 5 days data irrespective of missing rows. So in this case it will return data inserted on
2013-12-24
2013-12-23
2013-12-22
2013-12-19
2013-12-12
The days which are missing in between the dates above simply have no rows associated with them so they won't be returned.
I have also tried using
select distinct(date(date)), power from `thum_{ROH}` limit 5;
But this only selects some values in a specific date while skips on the rest. What i mean is that there are around 30 or more rows in each day which are present so the above query only returns around 2 or 3 rows per day.
I hope my question makes sense. I've been trying to find a solution without any success. Please provide any sort of advice on how can i achieve this. I would appreciate any help.
Thanks in advance,
Maxx
EDIT
Here is the table structure in question.
+-----------------+------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-----------------+------------------+------+-----+---------+-------+
| thumType | int(11) | NO | PRI | 0 | |
| timestamp | int(10) unsigned | NO | PRI | 0 | |
| rune | char(15) | YES | | NULL | |
| date | datetime | YES | | NULL | |
| destruction | decimal(15,2) | YES | | NULL | |
| restoration | decimal(15,2) | YES | | NULL | |
| conjuration | decimal(15,2) | YES | | NULL | |
| alteration | decimal(15,2) | YES | | NULL | |
| illusion | int(10) unsigned | YES | | NULL | |
| power | decimal(15,2) | YES | | NULL | |
| magicka | decimal(15,2) | YES | | NULL | |
| health | decimal(15,2) | YES | | NULL | |
+-----------------+------------------+------+-----+---------+-------+
You can do this with a join:
select t.*
from `thum_{ROH}` t join
(select distinct date
from `thum_{ROH}`
order by date desc
limit 5
) as date5
on t.date = date5.date;
EIDT:
The above works if we assume that there is no time component.
We can fix that problem by doing:
select t.*
from `thum_{ROH}` t join
(select distinct date(date) as thedate
from `thum_{ROH}`
order by date desc
limit 5
) as date5
on date(t.date) = date5.thedate;
Assuming that the 'date' column is of type date, then we can solve this with a subquery to get the 5 most recent non-blank dates and then only select rows from those days.
SELECT *
FROM tablename
WHERE date IN (
SELECT distinct date FROM tablename ORDER BY date DESC LIMIT 5
)

Order by number of views in last hour [MySQL]

I have a table which holds all views for the last 24 hours. I want to pull all pages ordered by a rank. The rank should be calculated something like this:
rank = (0.3 * viewsInCurrentHour) * (0.7 * viewsInPreviousHour)
I want the prefferably in one single query. Is this possible, or do I need to make 2 queries (one for the current hour and one for the last hour and then just aggregate them)?
Here is the DESCRIBE of the table accesslog:
+-----------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------+------------------+------+-----+---------+----------------+
| aid | int(11) | NO | PRI | NULL | auto_increment |
| sid | varchar(128) | NO | | | |
| title | varchar(255) | YES | | NULL | |
| path | varchar(255) | YES | | NULL | |
| url | text | YES | | NULL | |
| hostname | varchar(128) | YES | | NULL | |
| uid | int(10) unsigned | YES | MUL | 0 | |
| timer | int(10) unsigned | NO | | 0 | |
| timestamp | int(10) unsigned | NO | MUL | 0 | |
+-----------+------------------+------+-----+---------+----------------+
select
url,
sum(timestamp between subdate(now(), interval 2 hour) and subdate(now(), interval 1 hour)) * .3 +
sum(timestamp between subdate(now(), interval 1 hour) and now()) * .7 as rank
from whatever_your_table_name_is_which_you_have_kept_secret
where timestamp > subdate(now(), interval 2 hour)
group by url
order by rank desc;
The sum(condition) works because in mysql trye is 1 and false is 0, so summing a condition is the same as what some noobs write as sum(case when condition then 1 else 0 end)
Edit:
Note the addition of where timestamp > subdate(now(), interval 2 hour) to improve performance, because only these records contribute to the result.