Hi There I spent an afternoon getting my sql to dynamically populate the date ranges for a chart.js graph and decided to share the code to help anyone in the future.
The code that i have written will take the date range you have inputted and check the time between the two dates then depending on the time between the two dates will give you a different time span for your graph.
Here is the full query
SELECT
name AS "Company"
IF( DATEDIFF( FROM_UNIXTIME( [end_date], '%Y-%m-%d' ), FROM_UNIXTIME(
[start_date], '%Y-%m-%d' )) <= 8,
DATE_FORMAT( FROM_UNIXTIME( insert_date, '%Y-%m-%d' ), '%a %D %M'),
IF( DATEDIFF( FROM_UNIXTIME( [end_date], '%Y-%m-%d' ), FROM_UNIXTIME(
[start_date], '%Y-%m-%d' )) BETWEEN 8 AND 62,
CONCAT("WC - ", DATE_FORMAT(DATE_ADD(MAKEDATE(FROM_UNIXTIME( insert_date,
'%Y' ), 1), INTERVAL WEEK(FROM_UNIXTIME( insert_date, '%Y-%m-%d' ))
WEEK), "%d %M %Y")),
IF( DATEDIFF(FROM_UNIXTIME( [end_date], '%Y-%m-%d' ), FROM_UNIXTIME(
[start_date], '%Y-%m-%d' )) > 62,
CONCAT(MONTHNAME(STR_TO_DATE(MONTH(FROM_UNIXTIME( insert_date, '%Y-%m-%d'
)), '%m'))," ",FROM_UNIXTIME( insert_date, '%Y' )),''))) AS labels ,
CONCAT(
"Generated (",COUNT( insert_date ),")"
) AS toolTip
FROM (inserted_cases)
WHERE insert_date BETWEEN [start_date] AND [end_date]
GROUP BY labels
Now the bit that does all the calculations for you is here and will work for quite a few other things as well but im suggesting it for chart.js as i have tested it on here.
IF( DATEDIFF( FROM_UNIXTIME( [end_date], '%Y-%m-%d' ), FROM_UNIXTIME(
[start_date], '%Y-%m-%d' )) <= 8,
DATE_FORMAT( FROM_UNIXTIME( insert_date, '%Y-%m-%d' ), '%a %D %M'),
IF( DATEDIFF( FROM_UNIXTIME( [end_date], '%Y-%m-%d' ), FROM_UNIXTIME(
[start_date], '%Y-%m-%d' )) BETWEEN 8 AND 62,
CONCAT("WC - ", DATE_FORMAT(DATE_ADD(MAKEDATE(FROM_UNIXTIME(
insert_date, '%Y' ), 1), INTERVAL WEEK(FROM_UNIXTIME(
insert_date, '%Y-%m-%d' )) WEEK), "%d %M %Y")),
IF( DATEDIFF(FROM_UNIXTIME( [end_date], '%Y-%m-%d' ), FROM_UNIXTIME(
[start_date], '%Y-%m-%d' )) > 62,
CONCAT(MONTHNAME(STR_TO_DATE(MONTH(FROM_UNIXTIME( insert_date, '%Y-
%m-%d' )), '%m'))," ",FROM_UNIXTIME( insert_date, '%Y' )),''))) AS
labels ,
I hope this can help to save so time in the future. also if you know of a better way im always open to suggestion and learning new things. happy coding guys. :)
Related
Below my query:
SELECT DATE_FORMAT( FROM_UNIXTIME( `temp_timing` ) , '%Y-%m-%d %H:%i:%s' ) AS timing, temperature, deviceid
FROM tempdata
WHERE deviceid =29
AND DATE_FORMAT( FROM_UNIXTIME( `temp_timing` ) , '%Y-%m-%d' ) >= '2018-07-20'
AND DATE_FORMAT( FROM_UNIXTIME( `temp_timing` ) , '%Y-%m-%d' ) <= '2018-07-27'
ORDER BY timing ASC
LIMIT 0 , 30
It can be by the following query
SELECT DATE_FORMAT( FROM_UNIXTIME( `temp_timing` ) , '%Y-%m-%d %H:%i:%s' ) AS timing, AVG( temperature ) AS avg, deviceid
FROM tempdata
WHERE deviceid =29
AND DATE_FORMAT( FROM_UNIXTIME( `temp_timing` ) , '%Y-%m-%d' ) >= '2018-07-20'
AND DATE_FORMAT( FROM_UNIXTIME( `temp_timing` ) , '%Y-%m-%d' ) <= '2018-07-27'
GROUP BY DATE( timing ) , HOUR( timing )
ORDER BY `timing` ASC
LIMIT 0 , 30
I have a table with two columns "date_from" and "date_to". When the user inputs a date i want to get the row where te picked date is between those two, however the years on those dates dont matter for me - only the months wich form something like seasons i have come to this :
SELECT `date_from` , `date_to`
FROM `language_courses_accommodation_periods`
WHERE DATE_FORMAT( `date_from` , '%c' ) <= DATE_FORMAT( STR_TO_DATE( '2017-02-03', '%Y-%m-%d' ) , '%c' )
AND DATE_FORMAT( `date_to` , '%c' ) >= DATE_FORMAT( STR_TO_DATE( '2017-02-03', '%Y-%m-%d' ) , '%c' )
AND DATE_FORMAT( `date_from` , '%e' ) <= DATE_FORMAT( STR_TO_DATE( '2017-02-03', '%Y-%m-%d' ) , '%e' )
AND DATE_FORMAT( `date_to` , '%e' ) >= DATE_FORMAT( STR_TO_DATE( '2017-02-03', '%Y-%m-%d' ) , '%e' )
This works in some cases but a season can start for example at november and
end at march and then my query doesent return the correct result.
Use BETWEEN, which doesn't care about the order of the arguments at the right:
SELECT `date_from`, `date_to`
FROM `language_courses_accommodation_periods`
WHERE DATE_FORMAT(STR_TO_DATE('2017-02-03', '%Y-%m-%d'), '%c')
BETWEEN DATE_FORMAT(`date_from`, '%c')
AND DATE_FORMAT(`date_to`, '%c')
I don't think there is any additional value in repeating this condition with the %e format.
UPDATED: Try this query it might work by splitting the days from the months you are creating too many possibilities for the answer to be.
SELECT `date_from` , `date_to`
FROM `language_courses_accommodation_periods`
WHERE DATE_FORMAT( `date_from` , '%c-%d' ) <= DATE_FORMAT( STR_TO_DATE( '2017-02-03', '%Y-%m-%d' ) , '%c-%d' )
AND DATE_FORMAT( `date_to` , '%c-%d' ) >= DATE_FORMAT( STR_TO_DATE( '2017-02-03', '%Y-%m-%d' ) , '%c-%d' )
Can anyone fix the following SQL code, which gets upcoming birthdays:
SELECT *
FROM personal
WHERE 1 = ( FLOOR( DATEDIFF( DATE_ADD( DATE( NOW( ) ) , INTERVAL :interval DAY ) , dob ) / 365.25 ) ) -
( FLOOR( DATEDIFF( DATE_ADD( DATE( NOW( ) ) , INTERVAL -1 DAY ) , dob ) / 365.25 ) )
ORDER BY MONTH( dob ), DAY( dob )
LIMIT :rangeStart,:limit
It works aside from there is a bug with the ordering if the year rolls over. E.g. your in December checking ahead 1 month, then January birthday will get ordered before December.
I've seen a few posts on here about doing this, but none of them seem to get this part correct. I tried using the following ORDER BY clause, from another post and which seemed to work for someone else, but when I run it I get a syntax error:
ORDER BY ( MONTH(dob) > MONTH(NOW() OR ((MONTH(dob) = MONTH(NOW()) AND DAY(dob) >= DAY(NOW()) DESC, MONTH(dob), DAY(dob)
Updated answer ...
Thought of a cleaner way to accomplish this:
SELECT *
FROM (
SELECT *
,DATE_ADD( MAKEDATE( YEAR( NOW() ), DAYOFYEAR( dob ) )
,INTERVAL IF( DAYOFYEAR( dob ) < DAYOFYEAR( NOW() ), 1, 0 ) YEAR
)
AS next_birthday
FROM personal
) a
WHERE a.next_birthday < DATE_ADD( NOW(), INTERVAL :interval DAY )
ORDER BY a.next_birthday ASC
LIMIT :rangeStart,:limit
Explanation: next_birthday is calculated by taking this year's birthday and adding either 1 or 0 years to it depending on whether or not the birthday has already occurred this year - determined by IF( DAYOFYEAR( dob ) < DAYOFYEAR( NOW() ), 1, 0 ).
See it in action in SQLFiddle at http://sqlfiddle.com/#!9/b6471/1. Play with the number of days to see January after December.
Original answer ...
Add the following to your SELECT:
SELECT *
, CASE WHEN MAKEDATE( YEAR( CURRENT_DATE ), DAYOFYEAR( dob ) ) >= CURRENT_DATE
THEN MAKEDATE( YEAR( CURRENT_DATE ), DAYOFYEAR( dob ) )
ELSE DATE_ADD( MAKEDATE( YEAR( CURRENT_DATE ), DAYOFYEAR( dob ) ), INTERVAL 1 YEAR )
END AS next_birthday
then
ORDER BY next_birthday ASC
Rewriting your original query you get something like:
SELECT a.*
FROM ( SELECT *
, CASE WHEN DAYOFYEAR( dob ) >= DAYOFYEAR( CURRENT_DATE )
THEN MAKEDATE( YEAR( CURRENT_DATE ), DAYOFYEAR( dob ) )
ELSE DATE_ADD( MAKEDATE( YEAR( CURRENT_DATE ), DAYOFYEAR( dob ) ), INTERVAL 1 YEAR )
END AS next_birthday
FROM personal
) a
WHERE a.next_birthday < DATE_ADD( CURRENT_DATE, INTERVAL :interval DAY )
ORDER BY a.next_birthday ASC
LIMIT :rangeStart,:limit
I've now found a solution. For those that would like to know, the final code is:
SELECT *, DATE_ADD( dob, INTERVAL YEAR( FROM_DAYS( DATEDIFF( NOW( ), dob) -1 ) ) + 1 YEAR) AS next_birthday
FROM personal
WHERE 1 = ( FLOOR( DATEDIFF( DATE_ADD( DATE( NOW( ) ) , INTERVAL :interval DAY ) , dob ) / 365.25 ) ) -
( FLOOR( DATEDIFF( DATE_ADD( DATE( NOW( ) ) , INTERVAL -1 DAY ) , dob ) / 365.25 ) )
ORDER BY next_birthday ASC
LIMIT :rangeStart,:limit
I haven't tested it fully, but on simple tests it works.
I have a MYSQL table with two columns, timestamp and span. timestamp is a unix-timestamp, while span is an integer.
I want to select all rows where the timestamp is between the current time +- half the span.
Eg. if the current time is 23:00, then I want all rows where the timestamp time is between 21 and 01. The issue is of-course that all the timestamps are from different days.
TO CLARIFY, I don't care about the date only the time
I've figured out a way to do this, but it seems like such a hack. I'm sure that someone with more MYSQL knowledge could show me a prettier way to do this.
SELECT *
FROM table
WHERE (
TIME( DATE_SUB( CURDATE( ) , INTERVAL( span /2 ) HOUR ) ) <= TIME( DATE_ADD( CURDATE( ) , INTERVAL( span /2 ) HOUR ) )
AND (
TIME( FROM_UNIXTIME( timestamp ) ) >= TIME( DATE_SUB( CURDATE( ) , INTERVAL( span /2 ) HOUR ) )
AND TIME( FROM_UNIXTIME( timestamp ) ) <= TIME( DATE_ADD( CURDATE( ) , INTERVAL( span /2 ) HOUR ) )
)
)
OR (
TIME( DATE_SUB( CURDATE( ) , INTERVAL( span /2 ) HOUR ) ) >= TIME( DATE_ADD( CURDATE( ) , INTERVAL( span /2 ) HOUR ) )
AND (
TIME( FROM_UNIXTIME( timestamp ) ) >= TIME( DATE_SUB( CURDATE( ) , INTERVAL( span /2 ) HOUR ) )
OR TIME( FROM_UNIXTIME( timestamp ) ) <= TIME( DATE_ADD( CURDATE( ) , INTERVAL( span /2 ) HOUR ) )
)
)
Two options I recommend.
Benchmark your "hack" query and see if that is faster than running two MySQL queries. Eg: One query to get your target row, another query to get results with ±1/2 span.
Write a proper MySQL stored procedure. This is effectively option (1) but all done inside the DB.
Another option I don't recommend, based on past pain, is to use MySQL variables to effectively do two selects avoiding the stored procedure, eg: http://dev.mysql.com/doc/refman/5.0/en/user-variables.html. This approach was common back in the old days of PHP/MySQL apps.
In my experience odds are good option (1) is both fast enough and readable.
Didn't really find a pretty solution, but this is how i ended up doing it.
SELECT *
FROM table
WHERE date_add( from_unixtime( timestamp ) , INTERVAL datediff( now( ) , from_unixtime( timestamp ) ) DAY )
BETWEEN date_sub( now( ) , INTERVAL (span/2) HOUR )
AND date_add( now( ) , INTERVAL (span/2) HOUR )
OR date_add( from_unixtime( timestamp ) , INTERVAL datediff( now( ) , from_unixtime( timestamp ) ) -1 DAY )
BETWEEN date_sub( now( ) , INTERVAL (span/2) HOUR )
AND date_add( now( ) , INTERVAL (span/2) HOUR )
In MySQL, how would I get a timestamp from, say 30 days ago?
Something like:
select now() - 30
The result should return a timestamp.
DATE_SUB will do part of it depending on what you want
mysql> SELECT DATE_SUB(NOW(), INTERVAL 30 day);
2009-06-07 21:55:09
mysql> SELECT TIMESTAMP(DATE_SUB(NOW(), INTERVAL 30 day));
2009-06-07 21:55:09
mysql> SELECT UNIX_TIMESTAMP(DATE_SUB(NOW(), INTERVAL 30 day));
1244433347
You could use:
SELECT unix_timestamp(now()) - unix_timestamp(maketime(_,_,_));
For unix timestamps or:
SELECT addtime(now(),maketime(_,_,_));
For the standard MySQL date format.
If you need negative hours from timestamp
mysql>SELECT now( ) , FROM_UNIXTIME( 1364814799 ) , HOUR( TIMEDIFF( now( ) , FROM_UNIXTIME( 1364814799 ) ) ) , TIMESTAMPDIFF( HOUR , now( ) , FROM_UNIXTIME( 1364814799 ) )
2013-06-19 22:44:15 2013-04-01 14:13:19 1904 -1904
this
TIMESTAMPDIFF( HOUR , now( ) , FROM_UNIXTIME( 1364814799 ) )
will return negative and positive values, if you need to use x>this_timestamp
but this
HOUR( TIMEDIFF( now() , FROM_UNIXTIME( 1364814799 ) ) )
will return only positive, hours