Birthday MySQL Query Hates Timestamps


AKA Why I Hate UNIX Timestamps Disguised as Integers

For a long time now I've been converting old code that programmers before me at work have brutalized. I'm not saying I am the cleanest/most efficient coder ever but at least I employ some amount of updated logic in my code.

Timestamps are great and all, as long as you have them stored in the MySQL database as a TIMESTAMP data type. The trouble I was running into is that the fields I was handling had the timestamp stored as INT data types. I'd much rather store dates as a DATE or DATETIME data type rather than TIMESTAMP data types because TIMESTAMP data types cannot store dates before 1970 or after 2038. Though it is nice that you can have a TIMESTAMP data type be set to always update when the record on the table is changed, I don't mind adding that to my code and being able to override that data easily if necessary.
By using any of the three date data types (DATE, DATETIME and TIMESTAMP) I can quickly and easily make updates directly in the database without using PHP or a calculator as an interface to calculate the date to a Unix time stamp.

Original Table Structure

Here's my original table structure, as a point of reference.

MySQL

CREATE TABLE `users` (
  `id` int(10) unsigned NOT NULL auto_increment,
  `fname` varchar(100) NOT NULL default '',
  `lname` varchar(100) NOT NULL default '',
  `dob` int(10) unsigned NOT NULL default '0',
  PRIMARY KEY  (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;

When I needed to pull users by their birthday, kind-of a "Look Whose Birthday" module, there is no easy query. The only thing I could do was add a new field to the table that used the DATETIME time stamp:

MySQL

ALTER TABLE `users` ADD `birthdate` DATETIME NOT NULL ;

Unfortunately there is no query to convert and update all 1000 users birthday (datetime data type) with the data from dob (Unix time stamp stored as INT) since the date is originally stored as an integer that the developers must understand and know is actually a Unix time stamp.

Here's the quick script I used to make the conversion.

PHP

<?php
    // Make MySQL connection

    $query1 = 'SELECT * FROM `users`';

    $result1 = mysql_query($query1,$sql);
    if(mysql_num_rows($result1) > 0){
        while ($row = mysql_fetch_assoc($result1)) {
            $birthdate = date('Y-m-d H:i:s',$row['dob']);
            $id = $row['id'];

            $query2 = "UPDATE `users` SET `birthdate` = '$birthdate' WHERE `id` = $id";
            mysql_query($query2,$sql);
        }
    }
?>

Grab those Birthday Girls and Boys

Now that the table has been updated I can grab people by their birthdays. I have two options for queries, which both accomplish the same thing.

MySQL

SELECT * FROM `users` WHERE MONTH(`birthdate`)=MONTH(NOW()) AND DAYOFMONTH(`birthdate`)=DAYOFMONTH(NOW());
# OR
SELECT * FROM `users` WHERE DATE_FORMAT(`birthdate`, '%m%d') = DATE_FORMAT(NOW(), '%m%d');

Ran a simple benchmark and the first query seems to have a faster success rate in a table of 1000 records, though I wouldn't mark it as monumentally faster. Run some tests on your table selecting the data you need to determine which method would work best for you.

Related Links

Popularity: 39% [?]


Write a Comment

Take a moment to comment and tell us what you think. Some basic HTML is allowed for formatting.

Reader Comments

This is close to what I am looking for.

How would I get all the birthdays for tomorrow or all the birthdays for the next week/month?

Would I have to use PHP to set the time range or can i do it with one quick query????

I know this post is a little old but just wanted to post this...

"Unfortunately there is no query to convert and update all 1000 users birthday (datetime data type) with the data from dob (Unix time stamp stored as INT)"

How about this?

UPDATE users SET birthdate=from_unixtime(dob);

Ignore my last comment :) It doesn't work for dates before jan 1 1970 since they're not supported by from_unixtime

@mick - You learn something new every day, thanks. I haven't seen the from_unixtime() function before. Even though it doesn't work before 1/1/1970 it's useful to add to the repository!

@mark - Oh, I know this one, give me a little bit to find this snippet somewhere in my library and I'll post it right up!

To select next/last week's birthdays you'll have to use the DATE_ADD() and DATE_SUB() to subtract INTERVALs from the current date. We'll select form the range between NOW() and DATE_ADD(NOW(), INTERVAL 7 DAY) which is 7 days (1 week) from now.

# next week's birthdays (inclusive)
SELECT * FROM `users` WHERE
 DATE_FORMAT(`birthdate`, '%m%d') >= DATE_FORMAT(NOW(), '%m%d') AND
 DATE_FORMAT(`birthdate`, '%m%d') <= DATE_FORMAT(DATE_ADD(NOW(), INTERVAL 7 DAY), '%m%d')
 ORDER BY DATE_FORMAT(`birthdate`, '%m%d') ASC;
 # 28 total, Query took 0.0309 sec - 1000 row table

Don't forget that if you want to sort the birthdays by birthday (aka day) you'll need to eliminate the year from the sort order or MySQL wll order things using the year too. That's why my ORDER in these sample queries are ORDER BY DATE_FORMAT(birthdate, '%m%d') ASC.

# last week's birthdays (inclusive)
SELECT * FROM `users` WHERE
 DATE_FORMAT(`birthdate`, '%m%d') <= DATE_FORMAT(NOW(), '%m%d') AND
 DATE_FORMAT(`birthdate`, '%m%d') >= DATE_FORMAT(DATE_SUB(NOW(), INTERVAL 7 DAY), '%m%d')
 ORDER BY DATE_FORMAT(`birthdate`, '%m%d') ASC;
 # 20 total, Query took 0.0469 sec - 1000 row table

If anyone knows of a more efficent way to set this up, let me know. I haven't run any extensive benchmarks and don't intend on it any time soon. If you do, send your responses this way!