Issue with SQL getting Upcoming Birthdays - mysql

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.

Related

check either startdate or end date is in last week

I want to check either startdate or enddate is in last week.I want to display the rows which has commRunStart or commRunEnd date is in last week .
SELECT A.commRunStart, A.commRunEnd FROM `commRuns` AS A
WHERE A.commRunStart >= CURDATE( ) - INTERVAL DAYOFWEEK( CURDATE( ) ) +6 DAY
OR A.commRunEnd < CURDATE( ) - INTERVAL DAYOFWEEK( CURDATE( ) ) -1 DAY
SELECT A.commRunStart, A.commRunEnd FROM `commRuns` AS A
WHERE (A.commRunStart >= CURDATE( ) - INTERVAL DAYOFWEEK( CURDATE( ) ) +6 DAY
AND A.commRunStart < CURDATE( ) - INTERVAL DAYOFWEEK( CURDATE( ) ) -1 DAY )
OR (A.commRunEnd >= CURDATE( ) - INTERVAL DAYOFWEEK( CURDATE( ) ) +6 DAY
AND A.commRunEnd < CURDATE( ) - INTERVAL DAYOFWEEK( CURDATE( ) ) -1 DAY )

SQL exception Parameter index out of range

I'm sending 4 values for the sql query through a jaggery script. Here is the sql query:
SELECT full_name , sum( amount ) AS total
FROM hourlyusage , user
WHERE DAY = DATE( DATE_SUB( NOW( ) , INTERVAL ? HOUR ) )
AND HOUR BETWEEN HOUR( DATE_SUB( NOW( ) , INTERVAL ? HOUR ) )
AND HOUR( DATE_SUB( NOW( ) , INTERVAL 1 HOUR ) )
AND hourlyusage.userIp = user.ip_address
AND (user.full_name LIKE '%?%' OR user.user_name LIKE '%?%') GROUP BY full_name
But it gives an exception like this.
Nested Exception:-
java.sql.SQLException: Parameter index out of range (3 > number of parameters, which is 2).
What would be the reason for that?
SELECT full_name , sum( amount ) AS total
FROM hourlyusage , user
WHERE DAY = DATE( DATE_SUB( NOW( ) , INTERVAL ? HOUR ) )
AND HOUR BETWEEN HOUR( DATE_SUB( NOW( ) , INTERVAL ? HOUR ) )
AND HOUR( DATE_SUB( NOW( ) , INTERVAL 1 HOUR ) )
AND hourlyusage.userIp = user.ip_address
AND (user.full_name LIKE ? OR user.user_name LIKE ?) GROUP BY full_name
You cannot use wildcard in the sql query so try to remove the wildcard from the sql and add it to the value.As you have done '%?%' in sql says that you need global match.Do that in your value not in sql query.

Select time range from unix-timestamp

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 )

MYSQL REPORTING

is there a more efficent way to get rows fro ma table from the current week, last week, current month and current year to this
// current month
SELECT * FROM crm_tasks WHERE YEAR( date_completed ) = YEAR( CURDATE( ) ) AND MONTH( date_completed ) = MONTH( CURDATE( ) )
// current week
SELECT * FROM crm_tasks WHERE YEAR( date_completed ) = YEAR( CURDATE( ) ) AND WEEK( date_completed ) = WEEK( CURDATE( ) )
// last week
SELECT * FROM crm_tasks WHERE YEAR( date_completed ) = YEAR( CURDATE( ) ) AND WEEK( date_completed ) = WEEK( CURDATE( ) ) - 1
// current year
SELECT * FROM crm_tasks WHERE YEAR( date_completed ) = YEAR( CURDATE( ) )
You should be able to use a query similar to this
SELECT * FROM crm_tasks
WHERE date_completed >= #FirstDayOfYear AND date_completed < #FirstDayOfNextYear
Where #FirstDayOfYear and #FirstDayOfNextYear are filled in appropriately. If you have an index on date_created, this should work reasonably fast. This can be altered for any period you want to use.

MySQL get the date n days ago as a timestamp

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