Calculate difference between two datetimes in MySQL - mysql

I am storing the last login time in MySQL in, datetime-type filed. When users logs in, I want to get the difference between the last login time and the current time (which I get using NOW()).
How can I calculate it?

USE TIMESTAMPDIFF MySQL function. For example, you can use:
SELECT TIMESTAMPDIFF(SECOND, '2012-06-06 13:13:55', '2012-06-06 15:20:18')
In your case, the third parameter of TIMSTAMPDIFF function would be the current login time (NOW()). Second parameter would be the last login time, which is already in the database.

my two cents about logic:
syntax is "old date" - :"new date", so:
SELECT TIMESTAMPDIFF(SECOND, '2018-11-15 15:00:00', '2018-11-15 15:00:30')
gives 30,
SELECT TIMESTAMPDIFF(SECOND, '2018-11-15 15:00:55', '2018-11-15 15:00:15')
gives:
-40

If your start and end datetimes are on different days use TIMEDIFF.
SELECT TIMEDIFF(datetime1,datetime2)
if datetime1 > datetime2 then
SELECT TIMEDIFF("2019-02-20 23:46:00","2019-02-19 23:45:00")
gives: 24:01:00
and datetime1 < datetime2
SELECT TIMEDIFF("2019-02-19 23:45:00","2019-02-20 23:46:00")
gives: -24:01:00

I don't think the accepted answer is appropriate. For example, if the difference between last login time and current time is 8 hours then getting the difference in seconds is illogical. The correct format will be in hours, minutes and seconds. I have illustrated this as follows -
Here, I create a table login_info table to store login information of users.
CREATE TABLE login_info (
-> user_id INT UNSIGNED NOT NULL AUTO_INCREMENT,
-> last_login DATETIME NOT NULL,
-> PRIMARY KEY (user_id)
-> );
Then I populate the table using some random values -
INSERT INTO login_info (last_login) VALUES
-> ("2021-09-22 09:32:44"),
-> ("2021-09-22 13:02:57"),
-> ("2021-09-21 23:43:21"),
-> ("2021-09-22 04:43:39"),
-> ("2021-09-22 17:23:21");
Now I calculate the difference between last_login and current_time as follows:
CREATE TABLE login_dur_in_sec AS
-> SELECT user_id,
-> TIMESTAMPDIFF(SECOND, last_login, NOW()) AS diff
-> FROM login_info;
SELECT * FROM login_dur_in_sec;
+---------+-------+
| user_id | diff |
+---------+-------+
| 1 | 28580 |
| 2 | 15967 |
| 3 | 63943 |
| 4 | 45925 |
| 5 | 343 |
+---------+-------+
CREATE TABLE hour_section AS
-> SELECT user_id,
-> FLOOR (diff / 3600) AS hour_part
-> FROM login_dur_in_sec;
CREATE TABLE minute_section AS
-> SELECT user_id,
-> FLOOR (MOD (diff, 3600)/ 60) AS minute_part
-> FROM login_dur_in_sec;
CREATE TABLE second_section AS
-> SELECT user_id,
-> MOD (MOD (diff, 3600), 60) AS second_part
-> FROM login_dur_in_sec;
CREATE TABLE login_dur AS
-> SELECT h.user_id, h.hour_part, m.minute_part, s.second_part
-> FROM hour_section AS h INNER JOIN minute_section AS m
-> ON h.user_id = m.user_id
-> INNER JOIN second_section AS s
-> ON m.user_id = s.user_id;
CREATE TABLE login_dur_trunc AS
-> SELECT user_id,
-> CONCAT (hour_part, ":", minute_part, ":", second_part) AS login_duration
-> FROM login_dur;
SELECT * FROM login_dur_trunc;
+---------+----------------+
| user_id | login_duration |
+---------+----------------+
| 1 | 8:14:46 |
| 2 | 4:44:33 |
| 3 | 18:4:9 |
| 4 | 13:3:51 |
| 5 | 0:24:9 |
+---------+----------------+
Here, the answer given by #Adi won't work always as pointed out by #CaiusJard.

Related

How to get data between start and expiration date if date is not empty or null?

I am trying to select offers between two dates, one of start and one of expiration and in case the expiration date is empty or null it will always show the offers.
Table
+----------------+---------------------+---------------------+
| deal_title | deal_start | deal_expire |
+----------------+---------------------+---------------------+
| Example Deal | 10-24-2021 16:10:00 | 10-25-2021 16:10:00 |
| Example Deal 2 | 10-24-2021 16:10:00 | NULL |
+----------------+---------------------+---------------------+
Php Function to get the current date by timezone.
function getDateByTimeZone(){
$date = new DateTime("now", new DateTimeZone("Europe/London") );
return $date->format('m-d-Y H:i:s');
}
Mysql query:
SELECT deals.*, categories.category_title AS category_title
FROM deals
LEFT JOIN categories ON deal_category = categories.category_id
WHERE deals.deal_status = 1
AND deals.deal_featured = 1
AND deals.deal_start >= '".getDateByTimeZone()."'
AND '".getDateByTimeZone()."' < deals.deal_expire
OR deals.deal_expire IS NULL
OR deals.deal_expire = ''
GROUP BY deals.deal_id ORDER BY deals.deal_created DESC
You didn't really explain what problem you're having. Having written queries like this many times in the past, you likely need parentheses around the expiration side of your date qualifications.
WHERE deals.deal_status = 1
AND deals.deal_featured = 1
AND deals.deal_start >= '".getDateByTimeZone()."'
AND (
'".getDateByTimeZone()."' < deals.deal_expire
OR deals.deal_expire IS NULL
)
If you don't put parentheses around your OR clause, then operator precedence will cause the whole WHERE clause to be true whenever the expire date is NULL and that's not what you want. You want a compounded OR clause here.
I don't think you need to compare against empty string either, just assuming you put that in there trying to figure things out so I left it out in my sample code.
Also I'm not familiar with PHP string interpolation enough to know if there's an issue with the way you're interpolating the result of the 'getDateByTimeZone' function into that query. It looks funky to me based on past experience with PHP, but I'm ignoring that part of it under the assumption that there's something wrapping this code which resolves it correctly.
The best would be to have MySQL datetimes from the start in your database
But you can do all in MySQL.
STR_TO_DATE will cost time every time it runs
When you put around all expire dates a () it will give back a true if youe of them is true
CREATE TABLE deals (
deal_id int,
deal_status int,
deal_featured int,
deal_category int,
`deal_title` VARCHAR(14),
`deal_start` VARCHAR(19),
`deal_expire` VARCHAR(19)
,deal_created DATEtime
);
INSERT INTO deals
(deal_id,deal_status,deal_featured,deal_category,`deal_title`, `deal_start`, `deal_expire`,deal_created)
VALUES
(1,1,1,1,'Example Deal', '10-24-2021 16:10:00', '10-25-2021 16:10:00',NOW()),
(2,1,1,1,'Example Deal 2', '10-24-2021 16:10:00', NULL,NOW());
CREATE TABLE categories (category_id int,category_title varchar(20) )
INSERT INTO categories VALUES(1,'test')
SELECT
deals.deal_id, MIN(`deal_title`), MIN(`deal_start`), MIN(`deal_expire`),MIN(deals.deal_created) as deal_created , MIN(categories.category_title)
FROM
deals
LEFT JOIN
categories ON deal_category = categories.category_id
WHERE
deals.deal_status = 1
AND deals.deal_featured = 1
AND STR_TO_DATE(deals.deal_start, "%m-%d-%Y %H:%i:%s") >= NOW() - INTERVAL 1 DAY
AND (NOW() < STR_TO_DATE(deals.deal_expire, "%m-%d-%Y %H:%i:%s")
OR deals.deal_expire IS NULL
OR deals.deal_expire = '')
GROUP BY deals.deal_id
ORDER BY deal_created DESC
deal_id | MIN(`deal_title`) | MIN(`deal_start`) | MIN(`deal_expire`) | deal_created | MIN(categories.category_title)
------: | :---------------- | :------------------ | :------------------ | :------------------ | :-----------------------------
1 | Example Deal | 10-24-2021 16:10:00 | 10-25-2021 16:10:00 | 2021-10-24 22:42:34 | test
2 | Example Deal 2 | 10-24-2021 16:10:00 | null | 2021-10-24 22:42:34 | test
db<>fiddle here

MySQL: Populating A Table with Random Dates or Null Vallues

I would like to populate a column in my table with either random dates from the past or Null Values. I would like to set the random dates between two dates, January 1 1920 and December 1 2018, or NULL VALUES.
I've come accross some confusing code that could be a solution for generating a random date during a specific period, but it doesn't cater for the null values.
INSERT INTO `FootballPlayers` VALUES (SELECT timestamp('2010-04-30') - INTERVAL FLOOR( RAND( ) * 366) DAY);
I would like for the column of the table to have something like.
+----------------+
| Date of Death |
+----------------+
| 20/10/1990 |
| 01/11/1988 |
| 04/02/2006 |
| NULL |
| 17/05/2011 |
| 22/04/1972 |
| NULL |
| NULL |
| 13/04/1989 |
| 10/03/1999 |
+----------------+
Actually you don't get any null value by this query but if you use some case statement here then you can get this null here.
I used when-case statement and get exact result.
Here you go :
SELECT
CASE
WHEN (SELECT FLOOR( RAND( ) * 366)) BETWEEN 50 AND 255
THEN TIMESTAMP('2010-04-30') -INTERVAL FLOOR( RAND( ) * 366) DAY
ELSE NULL
END time_of_death
You can use this to your insert statement as well.
You can use ELT() function.
SELECT ELT(N,MYRANDOMDATE) AS RANDOMDATE FROM
(SELECT TIMESTAMP('2010-04-30') - INTERVAL FLOOR( RAND( ) * 366) DAY AS MYRANDOMDATE,
FLOOR(RAND()*(2-1+1))+1 AS N) AS FINAL
Here i'm generating random number between 1 and 2 along with the random date. And I'm providing only one argument in ELT() function.When the random number is 2 , it will give you NULL. You can adjust this arguments based on your frequency of needed NULL values
Check demo here
As a convenient single IF expression:
SELECT IF(RAND() > 0.2, TIMESTAMP(NOW()) - INTERVAL FLOOR(RAND() * 43200) MINUTE, NULL)

Identify bitcoin transaction MySQL Query problems

I have a user table holding user datas
++ id | username | btc_recive_address++
----------------------------------------
++ 1 | myuser | 123kahpoiq31328 ++
order table
++ order_id | user_id | amount | order_timestamp
------------------------------------------------------
++ h6765-a1s | 1 | 0.1 BTC | 2014-04-09 13:21:34
------------------------------------------------------
++ kzg765-a1 | 1 | 0.1 BTC | 2014-04-09 17:11:23
and collector table which retrieves data from bitcoin API( here I identify sender with btc_recive_address)
++ block_chain | user | amount | timestamp
--------------------------------------------------------------------------------
++ 2d37e5351196... | 1 | 0.1 | 2014-04-09 16:21:34
--------------------------------------------------------------------------------
++ 123kjhg7231k.. | 1 | 0.1 | 2014-04-08 19:33:56
--------------------------------------------------------------------------------
and I try to assign transaction to order_id like generating a joined view from order and collector table but I have problems when the amount and user is the same
THE PROBLEM
User places multiple order with same value
0,1 X 3
I get back transactions data from API than I identify user with the reciver address
someaddress - (here the transaction has 3 incoming confirms)
than I try to build a MySQL View as comparing
order table with collector table like joining on user and amount. When the amount and the user is same I do not get the unique transaction_id from order_table in my view
Here is the view query
ALTER ALGORITHM=UNDEFINED DEFINER=`my_view`#`%` SQL SECURITY DEFINER VIEW `ci_orders_in` AS (
SELECT
`c`.`block_chain` AS `block_chain`,
`c`.`assigned_user` AS `assigned_user`,
`c`.`incoming_amount` AS `incoming_amount`,
`c`.`timestamp` AS `timestamp`,
`c`.`type` AS `type`,
`c`.`category` AS `category`,
`c`.`import_timestamp` AS `import_timestamp`,
`o`.`transaction_id` AS `transaction_id`,
`o`.`datum` AS `datum`,
`o`.`status` AS `status`,
`o`.`convert_coin` AS `convert_coin`,
`o`.`convert_coin_to` AS `convert_coin_to`,
`o`.`amount` AS `amount`,
`o`.`converted_amount` AS `converted_amount`,
`o`.`conversion_rate` AS `conversion_rate`,
`o`.`user` AS `user`,
`o`.`units_to_transfer` AS `units_to_transfer`,
`o`.`provision` AS `provision`
FROM (`ci_orders` `o`
JOIN `ci_collector` `c`
ON ((`o`.`user` = `c`.`assigned_user`)))
WHERE (`o`.`convert_coin` = `c`.`type`)
GROUP BY `o`.`converted_amount`)$$
DELIMITER ;
here I should use another join which should give me the nearest timestamp but I do not get forward with it
Well just looking at your view, its obvious you aren't showning us all the columns in these tables, and you've fudged the table names because the query has the column "user" in the ci_order table, but in the sample data is has the column "user_id". But since I read your question on the bitcoin SE site, and am vaguely familiar with what you're trying to do, I'm guessing you're going to want a query similar to this
[incorrect query]
Edit:
Sorry for not looking at the timestamps more closely. I actually bothered to load your dataset in to SQL (MS SQL 2014) this time, although I may have renamed the columns slightly. How about this? Also if you could provide details about the delay it would be helpful, such as does the order timestamp always come after the collector timestamp?
select *
from ci_orders
join ci_collector
on ci_orders.user_id = ci_collector.user_id
and ci_orders.amount = ci_collector.amount
and ci_collector.timestamp = (
select top 1 timestamp
from ci_collector
where ci_orders.user_id = ci_collector.user_id
and ci_orders.amount = ci_collector.amount
and ci_orders.timestamp > ci_collector.timestamp
order by timestamp desc
)

How can I get the difference between the individual maximum values of different days?

I am new in MySQL, I am trying to find:
The difference between a given day's maximum value occurred and the previous day's maximum value.
I was able to get the maximum values for dates via:
select max(`bundle_count`), `Production_date`
from `table`
group by `Production_date`
But I don't know how to use SQL to calculate the differences between maximums for two given dates.
am expecting output like this
Please help me.
Update 1: Here is a fiddle, http://sqlfiddle.com/#!2/818ad/2, that I used for testing.
Update 2: Here is a fiddle, http://sqlfiddle.com/#!2/3f78d/10 that I used for further refining/fixing, based on Sandy's comments.
Update 3: For some reason the case where there is no previous day was not being dealt with correctly. I thought it was. However, I've updated to make sure that works (a bit cumbersome--but it appears to be right. Last fiddle: http://sqlfiddle.com/#!2/3f78d/45
I think #Grijesh conceptually got you the main thing you needed via the self-join of the input data (so make sure you vote up his answer!). I've cleaned up his query a bit on syntax (building off of his query!):
SELECT
DATE(t1.`Production_date`) as theDate,
MAX( t1.`bundle_count` ) AS 'max(bundle_count)',
MAX( t1.`bundle_count` ) -
IF(
EXISTS
(
SELECT date(t2.production_date)
FROM input_example t2
WHERE t2.machine_no = 1 AND
date_sub(date(t1.production_date), interval 1 day) = date(t2.production_date)
),
(
SELECT MAX(t3.bundle_count)
FROM input_example t3
WHERE t3.machine_no = 1 AND
date_sub(date(t1.production_date), interval 1 day) = date(t3.production_date)
GROUP BY DATE(t3.production_date)
), 0
)
AS Total_Bundles_Used
FROM `input_example` t1
WHERE t1.machine_no = 1
GROUP BY DATE( t1.`production_date` )
Note 1: I think #Grijesh and I were cleaning up the query syntax issues at the same time. It's encouraging that we ended up with very similar versions after we were both doing cleanup. My version differs in using IFNULL() for when there is no preceding data. I also ended up with a DATE_SUB, and I made sure to reduce various dates to mere dates without time component, via DATE()
Note 2: I originally had not fully understood your source tables, so I thought I needed to implement a running count in the query. But upon better inspection, it's clear that your source data already has a running count, so I took that stuff back out.
I am not sure but you need something like this, Hope it will be helpful to you upto some extend:
Try this:
SELECT t1.`Production_date` ,
MAX(t1.`bundle_count`) - MAX(t2.`bundle_count`) ,
COUNT(t1.`bundle_count`)
FROM `table_name` AS t1
INNER JOIN `table_name` AS t2
ON ABS(DATEDIFF(t1.`Production_date` , t2.`Production_date`)) = 1
GROUP BY t1.`Production_date`
EDIT
I create a table name = 'table_name', as below,
mysql> SELECT * FROM `table_name`;
+---------------------+--------------+
| Production_date | bundle_count |
+---------------------+--------------+
| 2004-12-01 20:37:22 | 1 |
| 2004-12-01 20:37:22 | 2 |
| 2004-12-01 20:37:22 | 3 |
| 2004-12-02 20:37:22 | 2 |
| 2004-12-02 20:37:22 | 5 |
| 2004-12-02 20:37:22 | 7 |
| 2004-12-03 20:37:22 | 6 |
| 2004-12-03 20:37:22 | 7 |
| 2004-12-03 20:37:22 | 2 |
| 2004-12-04 20:37:22 | 1 |
| 2004-12-04 20:37:22 | 9 |
+---------------------+--------------+
11 rows in set (0.00 sec)
My query: to find difference in bundle_count between two consecutive dates:
SELECT t1.`Production_date` ,
MAX(t2.`bundle_count`) - MAX(t1.`bundle_count`) ,
COUNT(t1.`bundle_count`)
FROM `table_name` AS t1
INNER JOIN `table_name` AS t2
ON ABS(DATEDIFF(t1.`Production_date` , t2.`Production_date`)) = 1
GROUP BY t1.Production_date;
its output:
+---------------------+-------------------------------------------------+--------------------------+
| Production_date | MAX(t2.`bundle_count`) - MAX(t1.`bundle_count`) | COUNT(t1.`bundle_count`) |
+---------------------+-------------------------------------------------+--------------------------+
| 2004-12-01 20:37:22 | 4 | 9 |
| 2004-12-02 20:37:22 | 0 | 18 |
| 2004-12-03 20:37:22 | 2 | 15 |
| 2004-12-04 20:37:22 | -2 | 6 |
+---------------------+-------------------------------------------------+--------------------------+
4 rows in set (0.00 sec)
This is PostgreSQL syntax (sorry; it's what I'm familiar with) but should fundamentally work in either database. Note this doesn't exactly run in PostgreSQL either because group is not a valid table name (it's a reserved keyword). The approach is a self-join as others have mentioned but I've used a view to handle the max-by-day and the difference as separate steps.
create view max_by_day as
select
date_trunc('day', production_date) as production_date,
max(bundle_count) as bundle_count
from
group
group by
date_trunc('day', production_date);
select
today.production_date as production_date,
today.bundle_count,
today.bundle_count - coalesce(yesterday.bundle_count, 0)
from
max_by_day as today
left join max_by_day yesterday on (yesterday.production_date = today.production_date - '1 day'::interval)
order by
production_date;
PostgreSQL also has a construct called window functions which is useful for this and a bit easier to understand. Just had to stick in a bit of advocacy for a superior database. :-P
select
date_trunc('day', production_date),
max(bundle_count),
max(bundle_count) - lag(max(bundle_count), 1, 0)
over
(order by date_trunc('day', production_date))
from
group
group by
date_trunc('day', production_date);
These two approaches differ in how they handle missing days in the data - the first will treat it as a 0, the second will use the previous day which is present. There wasn't a case like this in your sample so I don't know if this is something you care about.

Query database in weekly interval

I have a database with a created_at column containing the datetime in Y-m-d H:i:s format.
The latest datetime entry is 2011-09-28 00:10:02.
I need the query to be relative to the latest datetime entry.
The first value in the query should be the latest datetime entry.
The second value in the query should be the entry closest to 7 days from the first value.
The third value should be the entry closest to 7 days from the second value.
REPEAT #3.
What I mean by "closest to 7 days from":
The following are dates, the interval I desire is a week, in seconds a week is 604800 seconds.
7 days from the first value is equal to 1316578202 (1317183002-604800)
the value closest to 1316578202 (7 days) is... 1316571974
unix timestamp | Y-m-d H:i:s
1317183002 | 2011-09-28 00:10:02 -> appear in query (first value)
1317101233 | 2011-09-27 01:27:13
1317009182 | 2011-09-25 23:53:02
1316916554 | 2011-09-24 22:09:14
1316836656 | 2011-09-23 23:57:36
1316745220 | 2011-09-22 22:33:40
1316659915 | 2011-09-21 22:51:55
1316571974 | 2011-09-20 22:26:14 -> closest to 7 days from 1317183002 (first value)
1316499187 | 2011-09-20 02:13:07
1316064243 | 2011-09-15 01:24:03
1315967707 | 2011-09-13 22:35:07 -> closest to 7 days from 1316571974 (second value)
1315881414 | 2011-09-12 22:36:54
1315794048 | 2011-09-11 22:20:48
1315715786 | 2011-09-11 00:36:26
1315622142 | 2011-09-09 22:35:42
I would really appreciate any help, I have not been able to do this via mysql and no online resources seem to deal with relative date manipulation such as this. I would like the query to be modular enough to be able to change the interval weekly, monthly, or yearly. Thanks in advance!
Answer #1 Reply:
SELECT
UNIX_TIMESTAMP(created_at)
AS unix_timestamp,
(
SELECT MIN(UNIX_TIMESTAMP(created_at))
FROM my_table
WHERE created_at >=
(
SELECT max(created_at) - 7
FROM my_table
)
)
AS `random_1`,
(
SELECT MIN(UNIX_TIMESTAMP(created_at))
FROM my_table
WHERE created_at >=
(
SELECT MAX(created_at) - 14
FROM my_table
)
)
AS `random_2`
FROM my_table
WHERE created_at =
(
SELECT MAX(created_at)
FROM my_table
)
Returns:
unix_timestamp | random_1 | random_2
1317183002 | 1317183002 | 1317183002
Answer #2 Reply:
RESULT SET:
This is the result set for a yearly interval:
id | created_at | period_index | period_timestamp
267 | 2010-09-27 22:57:05 | 0 | 1317183002
1 | 2009-12-10 15:08:00 | 1 | 1285554786
I desire this result:
id | created_at | period_index | period_timestamp
626 | 2011-09-28 00:10:02 | 0 | 0
267 | 2010-09-27 22:57:05 | 1 | 1317183002
I hope this makes more sense.
It's not exactly what you asked for, but the following example is pretty close....
Example 1:
select
floor(timestampdiff(SECOND, tbl.time, most_recent.time)/604800) as period_index,
unix_timestamp(max(tbl.time)) as period_timestamp
from
tbl
, (select max(time) as time from tbl) most_recent
group by period_index
gives results:
+--------------+------------------+
| period_index | period_timestamp |
+--------------+------------------+
| 0 | 1317183002 |
| 1 | 1316571974 |
| 2 | 1315967707 |
+--------------+------------------+
This breaks the dataset into groups based on "periods", where (in this example) each period is 7-days (604800 seconds) long. The period_timestamp that is returned for each period is the 'latest' (most recent) timestamp that falls within that period.
The period boundaries are all computed based on the most recent timestamp in the database, rather than computing each period's start and end time individually based on the timestamp of the period before it. The difference is subtle - your question requests the latter (iterative approach), but I'm hoping that the former (approach I've described here) will suffice for your needs, since SQL doesn't lend itself well to implementing iterative algorithms.
If you really do need to determine each period based on the timestamp in the previous period, then your best bet is going to be an iterative approach -- either using a programming language of your choice (like php), or by building a stored procedure that uses a cursor.
Edit #1
Here's the table structure for the above example.
CREATE TABLE `tbl` (
`id` int(10) unsigned NOT NULL auto_increment PRIMARY KEY,
`time` datetime NOT NULL
)
Edit #2
Ok, first: I've improved the original example query (see revised "Example 1" above). It still works the same way, and gives the same results, but it's cleaner, more efficient, and easier to understand.
Now... the query above is a group-by query, meaning it shows aggregate results for the "period" groups as I described above - not row-by-row results like a "normal" query. With a group-by query, you're limited to using aggregate columns only. Aggregate columns are those columns that are named in the group by clause, or that are computed by an aggregate function like MAX(time)). It is not possible to extract meaningful values for non-aggregate columns (like id) from within the projection of a group-by query.
Unfortunately, mysql doesn't generate an error when you try to do this. Instead, it just picks a value at random from within the grouped rows, and shows that value for the non-aggregate column in the grouped result. This is what's causing the odd behavior the OP reported when trying to use the code from Example #1.
Fortunately, this problem is fairly easy to solve. Just wrap another query around the group query, to select the row-by-row information you're interested in...
Example 2:
SELECT
entries.id,
entries.time,
periods.idx as period_index,
unix_timestamp(periods.time) as period_timestamp
FROM
tbl entries
JOIN
(select
floor(timestampdiff( SECOND, tbl.time, most_recent.time)/31536000) as idx,
max(tbl.time) as time
from
tbl
, (select max(time) as time from tbl) most_recent
group by idx
) periods
ON entries.time = periods.time
Result:
+-----+---------------------+--------------+------------------+
| id | time | period_index | period_timestamp |
+-----+---------------------+--------------+------------------+
| 598 | 2011-09-28 04:10:02 | 0 | 1317183002 |
| 996 | 2010-09-27 22:57:05 | 1 | 1285628225 |
+-----+---------------------+--------------+------------------+
Notes:
Example 2 uses a period length of 31536000 seconds (365-days). While Example 1 (above) uses a period of 604800 seconds (7-days). Other than that, the inner query in Example 2 is the same as the primary query shown in Example 1.
If a matching period_time belongs to more than one entry (i.e. two or more entries have the exact same time, and that time matches one of the selected period_time values), then the above query (Example 2) will include multiple rows for the given period timestamp (one for each match). Whatever code consumes this result set should be prepared to handle such an edge case.
It's also worth noting that these queries will perform much, much better if you define an index on your datetime column. For my example schema, that would look like this:
ALTER TABLE tbl ADD INDEX idx_time ( time )
If you're willing to go for the closest that is after the week is out then this'll work. You can extend it to work out the closest but it'll look so disgusting it's probably not worth it.
select unix_timestamp
, ( select min(unix_tstamp)
from my_table
where sql_tstamp >= ( select max(sql_tstamp) - 7
from my_table )
)
, ( select min(unix_tstamp)
from my_table
where sql_tstamp >= ( select max(sql_tstamp) - 14
from my_table )
)
from my_table
where sql_tstamp = ( select max(sql_tstamp)
from my_table )