MySQL - Time difference between one row and the current row being inserted - php

So I have a MySQL table that looks something like this:
id timestamp action timePassed
1 2012-07-10 22:44:00 start 0
2 2012-07-10 22:44:50 pause 50
3 2012-07-10 22:45:30 play 50
4 2012-07-10 22:47:25 pause 205
5 2012-07-10 22:48:05 play 205
I don't know how obvious it is but what's happening here is basically on every 'pause' row I am calculating the difference in seconds between the current timestamp and the timestamp of the 'start' row.
The only way I can think of doing this is to INSERT a new 'pause' row into the database so the timestamp is generated...then query the database for the timestamp of that 'pause' row...calculate the difference in seconds between that 'pause' row and the 'start' row using PHP...then UPDATE the 'pause' row with the timePassed result.
My question is whether or not there is a better way to do this (i.e. using timediff or some other MySQL command). The issue is the 'pause' timestamp doesn't exist until I make the INSERT, so I feel like that needs to happen first before I can make any calculation?

Well if your table is like this and your requirement is like this then you are on track..
However better if you check the second difference between last row and last to last row. This will give you idea for how many second user has PAUSED or Played the TRACK.
INSERT INTO table (timestamp, action, timePassed)
SELECT now(), 'pause', SECOND(timediff(now(), timestamp))
FROM table
WHERE ID = (Select Max(ID) from Table)
Put action as you like so you can insert time difference between two.

The only way I can think of doing this is to INSERT a new 'pause' row
into the database so the timestamp is generated...then query the
database for the timestamp of that 'pause' row...calculate the
difference in seconds between that 'pause' row and the 'start' row
using PHP...then UPDATE the 'pause' row with the timePassed result.
You can INSERT the already calculated row.
INSERT INTO table (timestamp, action, timePassed)
SELECT now(), 'pause', SECOND(timediff(now(), timestamp))
FROM table
WHERE action = 'start'; -- and/or other WHERE clauses.
This will introduce a skew in time due to the time needed to retrieve the 'start' row (but with index on action (and maybe timestamp if the row is very large), that time should be negligible).

I would recommend not storing timePassed explicitly at all, since by having each event's timestamp, you already have every interval stored once. If you separately store timePassed, you'll be storing the same data in multiple places. To quote E. F. Codd, "If something is true, saying it twice doesn't make it any more true." Also, you're subject to update anomalies when you store the same thing multiple times.
If you want to calculate the interval with a query, this might work.
SELECT event.id,
event.timestamp,
event.action,
TIME_TO_SEC(TIMEDIFF(event.timestamp, start_event.timestamp)) time_passed
FROM event
JOIN (SELECT timestamp
FROM event
WHERE event.action = 'start') start_event ON 1 = 1

Related

Count active users per hour based on two datetime entries

I have a MySQL query question.
In my web app I record the active listeners on my Shoutcast server in a MySQL database table, which includes "created", a datetime field for when they tuned in, and an "updated", a datetime field for the latest time the server polled the Shoutcast server (each minute). Plus, I also retrieve the duration in seconds of there listening session, plus the uid (aka session id) which is unique to each session.
What I would like to do is count the amount of listeners per hour, for example 13:00 = 20 listeners, but I would like to include not only those who are tuned in, so "created" datetime field, but also any listeners who where still listening from the previous hour, so the "update" datetime field.
What query would I need to achieve this. I would only generate 1 days worth of results at a time.
I understand how it would use something similar to "COUNT(id) AS hits" and "GROUP BY", but I'm not sure how to factor in the datetime fields, as the "update" datetime field is constantly updated, as long as the user is still listening. And some users can remain listening for 3 hours+.
Edit
The the main parts of the database schema is: id (int 20), created (datetime), updated (datetime), uid (int 20), duration (int 10).
The desired result would look something similar to:
(Time / hits) 0900 => 10, 1000 => 15, 1100 => 5, 1200 => 8, 1300 => 25
and so on...
This is a query I've used to filter results by country, which uses group by and count():-
SELECT country, COUNT(id) AS hits FROM listeners_log WHERE YEAR(created) = YEAR(NOW()) AND MONTH(created) = MONTH(NOW()) AND duration >= 60
The query also has an added filter on the end, to filter out session that are less than 60 seconds long.
To elaborate a little more, the created field reflects when a user connects/starts a listening session. For example, they tune in at 2016-03-21 15:00:00 that is reflected in the created field. But if they're still listening in 1 hour's time, the update field will read 2016-03-21 16:00:00, but the created field will remain the same.
Update:
I've come up with the following SQL, but this only counts the inital connection, indicated by the created field, and ignores if a use remains connected from one hour to a next.
SELECT HOUR(created), COUNT(id) AS hits FROM listeners_log WHERE DATE(created) = CURDATE() group by HOUR(created)
So you will be only needing to query on 'update' datetime field, because even if a new user comes its entry gets created with 'created' datetime as well as in next update under 'update' datetime, so now your concern will be only to query on 'update' datetime for any particular hour.
SELECT COUNT(id) FROM TABLE_NAME
WHERE update_Field BETWEEN DATE(NOW() - INTERVAL 1 HOUR);
This is a sample query to help you out, I don't have schema now to test it.
Modify it in your way and
Let me know if it doesn't work.

avoid sql insert between times

Im trying to avoid multiple sql inserts in a database. The idea is to wait at least 5 minutes before inserting again.
Getting time from last insert using
$query ="SELECT fecha
FROM almacen
WHERE fecha > NOW() - INTERVAL 5 MINUTE
ORDER BY id DESC LIMIT 1"
$espera=mysqli_query($conexion, $query)
if (empty($espera)) { inserting code } else { close }
But the query returns nothing when it should be returning a value. I was thinking it might be a problem since date was inserted using php date ( "j/n/Y h:i");
Should i change the time format? what should i use?
If you wanted to restrict your rows to only 1 entry per 5 minutes I would:
Create a column called insert_interval DATE and create a unique index on that column. Then just attempt your insert, setting the value of insert_interval to
whatever 5 minute interval the insert time falls under.
E.G. Normalize date to 5 minute interval.
select now() - interval (mod(minute(now()),5)) minute - interval second(now()) second;
Insert
insert into table(columns ..., insert_interval) values(
..., now() - interval mod(minute(now()),5) minute - interval second(now())
);
Only 1 row is allowed per 5 minute interval starting on the hour. So 1 row from 00:00:00 - 00:04:59, and so on. Any attempt to insert another row in that time window, would result in a duplicate key error, that you can catch and take appropriate action.
SQL Fiddle
Uncomment the last insert to see the error on build schema.

How to datenow minus datetime MySQL with PHP in second

i want to minus purchase date with datenow. i have table named count_sec :
|user_id| purchasedate |second|
| 1 |2015-06-06 08:36:05| |
| 2 |2015-06-06 08:36:15| |
example time now is 2015-06-06 08:37:00
what is the code if i am want the code to update the second to:
|user_id| purchasedate |second|
| 1 |2015-06-06 08:36:05| 55 |
| 2 |2015-06-06 08:36:15| 45 |
thank you
EDIT
i have already create this php, but the code is not work, how to fix?
<?php
require 'database/db.php';
$selectprchsdate = $mysqli->query("SELECT purchasedate FROM count_sec");
$row = mysqli_fetch_assoc($selectprchsdate);
$date = date('Y-m-d H:i:s');
$result = $date - $row['purchasedate'];
$mysqli->query("UPDATE count_sec
SET second = '".$result."'");
?>
In PHP you can use
// get current date and time
$now = new DateTime();
// create DateTime object for purchase date
$purchaseDate = DateTime::createFromFormat('Y-m-d H:i:s', $row['purchasedate']);
// calculate seconds
$seconds = $now->getTimeStamp() - $purchaseDate->getTimeStamp();
But the SQL solution suits this question better.
Try with the SQL query:
SELECT UNIX_TIMESTAMP(NOW()) - UNIX_TIMESTAMP(purchasedate) as second from countsec
or this:
SELECT TO_SECONDS(NOW()) - TO_SECONDS(purchasedate) as second from countsec;
From MySQL Date and Time Functions
I am not understanding why you need to store this in a column in the table. As soon as it's stored, the value is old, and it will need to be updated again. (Don't do this.) But setting that issue aside for a moment...
As to why your code isn't "working"... your UPDATE statement is updating every row in the table. You've previously fetched one row from the table, and then calculated one value, and then the UPDATE statement doesn't have a WHERE clause to identify which row(s) you want to update, so every row gets updated with the same value. That's a big part of why your code isn't working.
And, there's no need to run a SELECT statement before you run an UPDATE. If you want to update all rows in the table, you set the column to an expression that returns the number of seconds between the current date and time and the date and time stored in purchasedate column.
One convenient way to do that is to use the UNIX_TIMESTAMP function to convert each of the DATETIME values into an integer value (number of seconds), and subtract them. For example:
UPDATE count_sec
SET `second` = UNIX_TIMESTAMP(NOW()) - UNIX_TIMESTAMP(purchasedate)
As an alternative, you could use the TIMESTAMPDIFF function for an equivalent result:
UPDATE count_sec
SET `second` = TIMESTAMPDIFF(SECOND,NOW(),purchasedate)
But back to the issue of why this is wrong. You do not want to store second column in the table.
When you run a SELECT statement to return a row from the table, the value in this column is going to old.
Instead, you could just return a calculated value, calculated as of the time the SELECT statement runs, by including one of those expressions in the SELECT list. For example:
SELECT user_id
, purchasedate
, UNIX_TIMESTAMP(NOW()) - UNIX_TIMESTAMP(purchasedate) AS `calc_second`
, `second` AS `old_stored_value`
FROM count_sec
ORDER BY user_id
The point I'm trying to emphasize here... do not store the second value in the table. You'll be chasing it, and continuously updating every row in table, whether that's every five minutes or every five seconds. And the stored value is always going to be "old".
Why do you need that? If you want to find out which rows in the table have second between 60 and 120, for example, if you intend to run this query:
SELECT user_id
FROM count_sec
WHERE second > 120
AND second <= 60
You could just as easily rewrite that based on purchasedate
SELECT user_id
FROM count_sec
WHERE purchasedate > NOW() - INTERVAL 120 SECOND
AND purchasedate <= NOW() - INTERVAL 60 SECOND
And, you won't be "chasing" continuous updates of the rows in the table, generating rollbackup, recording changes in the InnoDB log, writing the UPDATE statements in the binary logs. If you are running replication slaves, those statements have to be read from the log and re-executed on the slaves. All in all, storing second is just a poor design choice.
You can use TIMESTAMPDIFF() function like below. See Documentation
SELECT TIMESTAMPDIFF(SECOND, NOW(), purchasedate)
from count_sec;
(OR) if you want to UPDATE
UPDATE count_sec SET `second` = TIMESTAMPDIFF(SECOND, NOW(), purchasedate);
Per your comment, if you want to delay for 5 minutes then either you can use
SLEEP(seconds) function
(OR)
Wrap your updation code in a stored procedure and run that in every 5 minutes (probably using some scheduler job)

MYSQL Group By date + 3 hours

I have a query that counts the "Xp" difference per day from my database, this all works as it should however it groups from midnight-midnight, what I would like to do is group 3am to 3am.
However another issue I think I may have is that my query may not always have the rows being the exact second at 3am due to the fact that it has to run a huge query and retrieve data from another website per user profile, so it should get all data after 3am, but before maybe 4am or something, so it has enough time to get all of the rows.
my current mysql is:
SELECT FROM_UNIXTIME(date, '%Y%m%d') AS YYYYMMDD, MAX(xp)-MIN(xp) AS xp_gain
FROM skills
WHERE userID = '$checkID'
AND skill = '$skill'
AND date >= '$date'
GROUP BY YYYYMMDD
ORDER BY date ASC
The best way to handle this is to add (if you can) another column that is just a DATE (not a DATETIME) and have this field rollover from one day to the next at 3am, (you can to this by subtracting 3 hours from the current time when doing the INSERT).
This gives you a couple of benefits, especially with a large number of rows:
It is much faster to query or group by a DATE than a range of
DATETIME
It will always query the rows at the exact second of 3am,
regardless of how long the query takes.

Delete old MySQL records every hour

Suppose I include the built in MySQL timestamp into my fields on my database table.
Everytime I update the records it will update the timestamp.
I would like to delete records than are older than an hour.
Any ideas for how best to do this?
I could have a loop checking the timestamp for all records or perhaps a trigger in the database?
You can use a cronjob to schedule a query to be executed in a fixed time interval:
DELETE FROM table_name WHERE time_created < (UNIX_TIMESTAMP() - 3600);
Every time it's run, it will delete all records older than 1 hour (which is presumably what you want).

Categories