views:

438

answers:

8

I have all users' birthdays stored as a unix timestamp and am wanting to send out e-mails each day to users that have a birthday that day. I need to make a MySQL query that will get all of the rows that contain a birthday on today's date. It seems like this should be fairly simple, but maybe I am just overcomplicating it.

A: 
set @now=now();
select * from user where (month(birthday) = month(@now) and day(birthday) = day(@now)) or
  (month(birthday) = 2 and day(birthday) = 29 and month(@now) = 2 and day(@now) = 28 and
  month(date_add(@now, interval 1 day)) = 3);
This doesn't take into account february 29, when there aren't leap years. The user has to send out emails on 28th february. You have to include 29th feb (if there isn't that day) into 28th feb.
Pentium10
+4  A: 

This should work:

   SELECT * 
      FROM USERS
      WHERE 
         DATE_FORMAT(FROM_UNIXTIME(birthDate),'%m-%d') = DATE_FORMAT(NOW(),'%m-%d')
Saggi Malachi
This doesn't take into account february 29, when there aren't leap years. The user has to send out emails on 28th february. You have to include 29th feb (if there isn't that day) into 28th feb.
Pentium10
February 29th birthday kids are already used to celebrate their birthday only once every 4 years. They get the no-birthday-spam privilege. Fair deal, isn't it?
Saggi Malachi
Let's take this serious, whould you miss a Wireless Carrier bonus for your birthday 3 years in row?
Pentium10
I can use PHP to alter the mysql query if they have a feb 20 birthday.
James Simpson
bad approach, see my answer below
Pentium10
A: 

Couldn't you just select all rows that matched the current day's date? You could also use the FROM_UNIXTIME() function to convert from unix timestamp to Date:

mysql> SELECT FROM_UNIXTIME(1196440219); -> '2007-11-30 10:30:19'

This is documented from http://dev.mysql.com/doc/refman/5.1/en/date-and-time-functions.html#function_from-unixtime

trinth
+1  A: 

The answer below doesn't actually work. It doesn't take into account the fact that a year is 365.24 (leap days now and then) days long, so the actual comparison against the users birthdate is complicated to say the least. I'm leaving it for historical reasons.

The other answers should work but if you want a slight optimization, say if there are many many rows, you are probably better off expressing the query directly in timestamp seconds. You can use the relations (slightly involved because of taking timezone into account):

today_starts = UNIX_TIMESTAMP(NOW()) - TIMESTAMPDIFF(SECOND, DATE(NOW()), NOW())
today_ends = today_starts + 86400

and then select records where the timestamp is between those values.

calmh
This doesn't take into account february 29, when there aren't leap years. The user has to send out emails on 28th february. You have to include 29th feb (if there isn't that day) into 28th feb.
Pentium10
@Pentium Actually, the more I think about it, the buggier this is. I'll edit and leave it as a warning against this kind of optimization. :)
calmh
@calmh: this reminds me of a quote I once heard: "It could be that the purpose of your life is only to serve as a warning to other". Kudos to you for standing to your answer! (btw: http://www.despair.com/mis24x30prin.html )
Cassy
+1  A: 

I took Saggi Malachi's answer and extended to include a birthday on 29th February into 28th February date, if in that year there is no such day.

SELECT * 
      FROM USERS
      WHERE 
         DATE_FORMAT(FROM_UNIXTIME(birthDate),'%m-%d') = DATE_FORMAT(NOW(),'%m-%d')
UNION
SELECT * 
      FROM USERS
      WHERE 
         DATE_FORMAT(NOW(),'%Y')%4 != 0 AND DATE_FORMAT(NOW(),'%m-%d')='02-28' and DATE_FORMAT(FROM_UNIXTIME(birthDate),'%m-%d') = '02-29'
Pentium10
Andrew Moore
technically that is not how you detect a leap year (1900, 2100) are not leap years.
Actually, if you need to be serious about this, you have to take into account, that every 100 years, there's no leap year, but every 400 there is ... You wouldn't wanna send the leap-day-birthday kids two mails on Feb 28th and Feb 29th 2100 :-)
Cassy
I am very much against adding any database overhead for such cases, this should be handled in your code and only run once a year. Databases are a very expensive resource, respect it. Also, why using UNION and not an OR?
Saggi Malachi
+1  A: 

Here is an answer that property takes into account leap-years and will always give you the users whose birthday is on the 29th of February at the same time as those on the 1st of March.

SELECT * 
  FROM USERS
  WHERE 
     DATE_FORMAT(FROM_UNIXTIME(birthDate),'%m-%d') = DATE_FORMAT(NOW(),'%m-%d')
     OR (
            (
                DATE_FORMAT(NOW(),'%Y') % 4 <> 0
                OR (
                        DATE_FORMAT(NOW(),'%Y') % 100 = 0
                        AND DATE_FORMAT(NOW(),'%Y') % 400 <> 0
                    )
            )
            AND DATE_FORMAT(NOW(),'%m-%d') = '03-01'
            AND DATE_FORMAT(FROM_UNIXTIME(birthDate),'%m-%d') = '02-29'
        )
Andrew Moore
A: 

Since this gets more and more to be a code-golf question, here's my approach on solving this including taking care of the leap years:

select * 
from user
where (date_format(from_unixtime(birthday),"%m-%d") = date_format(now(),"%m-%d"))
   or (date_format(from_unixtime(birthday),"%m-%d") = '02-29'
       and date_format('%m') = '02' 
       and last_day(now()) = date(now())
      );

Explanation: The first where clause checks if somebody's birthday is today. The second makes sure to only select those whose birthday is on Feb 29th only if the current day equals the last day of February.

Examples:

SELECT last_day('2009-02-01'); -- gives '2009-02-28'
SELECT last_day('2000-02-01'); -- gives '2009-02-29'
SELECT last_day('2100-02-01'); -- gives '2100-02-28'
Cassy
A: 

Here's my contribution

SELECT
  DAYOFYEAR(CURRENT_DATE)-(dayofyear(date_format(CURRENT_DATE,'%Y-03-01'))-60)=
  DAYOFYEAR(the_birthday)-(dayofyear(date_format(the_birthday,'%Y-03-01'))-60)
FROM
   the_table

The bits '(dayofyear(date_format(current_date,'%Y-03-01'))-60)' returns 1 on leap years since march 1st will be dayofyear number 61, and 0 on normal years.

From here it's just a matter of substracting that extra day to the "is-it-my-birthday"-calculation.

Thomas K Pedersen