I have a date stored as string in a Sqlite column, the format is YYYY-MM-DD ( Not separated into three columns for year date and month) Now I have a requirement to make a qu
Following will do:
SELECT *
FROM _table
ORDER BY SUBSTR(DATE('NOW'), 6)>SUBSTR(birthdate, 6), SUBSTR(birthdate, 6)
Check it in SQL Fiddle.
SUBSTR(date, 6)
will remove year part from date. Birthdates must be sorted in MM-DD
, but first should be shown those which will happen after today, and then those which happen before today.
SUBSTR(DATE('NOW'), 6)>SUBSTR(birthdate, 6)
will return 0 for birthdates MM-DD
greater or equal to today MM-DD
, 1 otherwise. So, first expression will ORDER
past dates after comming date. Second parameter just ORDER
dates by MM-DD
.
See step by step in SQL Fiddle.
There are quite a few ways to do it, some more efficient than others. I will suggest a solution that can be effective without changing your database format, but note that it can all get much faster (especially if we are talking about a big birthday list) if you can use separate Year,Month,Day integer columns, or a unix timestamp column.
select *,
strftime('%j',
strftime('%Y', 'now') || '-' ||
strftime('%m', birthday) || '-' ||
strftime('%d', birthday)
)
- strftime('%j',
strftime('%Y', 'now') || '-' ||
strftime('%m', 'now') || '-' ||
strftime('%d', 'now')
)
as daydiff
from users
where daydiff >=0
union all
select *,
strftime('%j',
strftime('%Y', 'now') || '-' ||
strftime('%m', birthday) || '-' ||
strftime('%d', birthday)
)
- strftime('%j',
strftime('%Y', 'now','+1 year') || '-' ||
strftime('%m', 'now') || '-' ||
strftime('%d', 'now')
) + 366
as daydiff
from users
where daydiff <366
order by daydiff
The above uses today's year and each users month and day parts to calculate day of year (e.g. today Dec 11, 2013 is day 345) and subtracts the day of year of today's date. Birthdays that occur in the current year will have a daydiff value >= 0, so we use them first. This was the first part of the union
.
The second part makes the same calculation but for those whose birthday is next year, so we add 366 to the daydiff value and make sure we only get the fields that we didn't get from the first part.
The same query can be rewritten with a CASE WHEN
instead of union
. The CASE alternative will be faster because it will only fetch the rows from the users table just once, instead of twice, but it would be really ugly for me to write in this forum. On second thought, I'll write it anyway, since it is faster
select *,
CASE WHEN
strftime('%j',
strftime('%Y', 'now') || '-' || strftime('%m', birthday) || '-' || strftime('%d', birthday)
)
- strftime('%j',
strftime('%Y', 'now') || '-' || strftime('%m', 'now') || '-' || strftime('%d', 'now')
) >= 0
THEN
strftime('%j',
strftime('%Y', 'now') || '-' || strftime('%m', birthday) || '-' || strftime('%d', birthday)
)
- strftime('%j',
strftime('%Y', 'now') || '-' || strftime('%m', 'now') || '-' || strftime('%d', 'now')
)
ELSE
strftime('%j',
strftime('%Y', 'now') || '-' || strftime('%m', birthday) || '-' || strftime('%d', birthday)
)
- strftime('%j',
strftime('%Y', 'now') || '-' || strftime('%m', 'now') || '-' || strftime('%d', 'now')
) + 366
END
as daydiff
from users
order by daydiff
Also a last note. I manually add 366 on next years birthdays, but the correct thing would be to add the days of year depending on the year (365 or 366). Since we only need it for ordering, this will not cause trouble because the worst case is that it will add one to daydiff for all next year's users. So birthday on '2013-12-31' will give daydiff=20, but birthday on '2014-01-01' will give daydiff=22.
EDIT:
Here's a fiddle