I have a table \"users\" with a column \"date_of_birth\" (DATE format with day, month, year). In frontend I need to list 5 upcoming birthdays.
Spent ages trying to w
Several answers have suggested calculating/storing day of year and sorting on that, but this alone won't do much good when you're close to the end of the year and need to consider people with birthdays in the beginning of the next year.
I'd go for a solution where you calculate the full date (year, month, day) of each person's next birthday, then sort on that. You can do this in Ruby code, or using a stored procedure in the database. The latter will be faster, but will make your app harder to migrate to a different db platform later.
It would be reasonable to update this list of upcoming birthdays once per day only, which means you can use some form of caching. Thus the speed of the query/code needed is less of an issue, and something like this should work fine as long as you cache the result:
class User
def next_birthday
year = Date.today.year
mmdd = date_of_birth.strftime('%m%d')
year += 1 if mmdd < Date.today.strftime('%m%d')
mmdd = '0301' if mmdd == '0229' && !Date.parse("#{year}0101").leap?
return Date.parse("#{year}#{mmdd}")
end
end
users = User.find(:all, :select => 'id, date_of_birth').sort_by(&:next_birthday).first(5)
Edit: Fixed to work correctly with leap years.
I found this worked for me. I didn't require to filter the most recent five results, so I used month is same as current.
def upcoming_birthdays
@resident = current_business.residents.where("extract(month from date_of_birth) = ?", Date.today.strftime('%m')).paginate(:page => params[:page])
end
If you're on Oracle, you can do it without creating a new column. IMO it's a smell to create a column that contains data you already have.
The SQL's a bit ugly - I'm sure there's a more elegant way to do it. Generally in these cases I'd ask my DBA friends for advice.
User.find(:all,
:conditions =>
"TO_NUMBER(TO_CHAR(dob, 'MMDD')) >= TO_NUMBER(TO_CHAR(SYSDATE, 'MMDD'))",
:order => "TO_NUMBER(TO_CHAR(dob, 'MMDD'))",
:limit => 5)
Some people think a duplicate column is faster, but if you have enough user data that speed's an issue, you should benchmark the duplicate column against a table without it that has a functional index on TO_NUMBER(TO_CHAR(dob, 'MMDD'))
.