Извлекать дни рождения в естественном порядке - PullRequest
3 голосов
/ 16 сентября 2009

Учитывая следующую таблицу MySQL:

ID|name|year|month|day
----------------------
1 |john|1978|5|1  
2 |mike|1979|7|23  
3 |bob |1985|2|14  
4 |joe |1964|2|16  
5 |jane|1975|9|22

Я пытаюсь извлечь пользователей в порядке событий, связанных с их днями рождения, в первую очередь по дням рождения. Итак, если запрос выполняется 16 сентября, порядок должен быть следующим: джейн, боб, джо, джон, майк.

Ответы [ 7 ]

8 голосов
/ 16 сентября 2009
SELECT  u.*, CAST(CONCAT_WS('.', YEAR(SYSDATE()) + (CAST(CONCAT_WS('.', YEAR(SYSDATE()), month, day) AS DATE) < SYSDATE()), month, day) AS DATE) AS nbd
FROM    t_users u
ORDER BY
        nbd;

Выражение ORDER BY берет логический результат сравнения дня рождения текущего года с текущей датой и добавляет его к текущему году.

Это приводит к следующему дню рождения (выражается какDATE), по которому вы можете заказать выписку.

В качестве бонуса вы можете легко получить этот следующий день рождения:

5, 'jane',  1975, 9, 22, '2009-09-22'
3, 'bob',   1985, 2, 14, '2010-02-14'
4, 'joe',   1964, 2, 16, '2010-02-16'
1, 'john',  1978, 5, 1,  '2010-05-01'
2, 'mike',  1979, 7, 23, '2010-07-23'

Обновление:

Этот запрос лучше обрабатывает високосные годы.

SELECT  u.*, CAST(CONCAT_WS('.', year, month, day) AS DATE),
        CAST(CONCAT_WS('.', 1980, month, day) AS DATE) + INTERVAL YEAR(SYSDATE()) - 1980 YEAR +
        INTERVAL CAST(CONCAT_WS('.', 1980, month, day) AS DATE) + INTERVAL YEAR(SYSDATE()) - 1980 YEAR < SYSDATE() YEAR AS nbd
FROM    t_users u
ORDER BY
        nbd;

Предполагается, что день рождения не високосного года для человека, рожденного в Feb 29, составляет Feb 28.

Я добавилчеловек по имени Alex, который родился Feb 29, 1980:

5, 'jane', 1975, 9, 22, '1975-09-22', '2009-09-22'
3, 'bob',  1985, 2, 14, '1985-02-14', '2010-02-14'
4, 'joe',  1964, 2, 16, '1964-02-16', '2010-02-16'
6, 'alex', 1980, 2, 29, '1980-02-29', '2010-02-28'
1, 'john', 1978, 5, 1,  '1978-05-01', '2010-05-01'
2, 'mike', 1979, 7, 23, '1979-07-23', '2010-07-23'
2 голосов
/ 16 сентября 2009

Если вы сохранили день рождения как объект даты, вы можете использовать это:

SELECT * 
FROM BirthdayTable
ORDER BY dayofyear(birthdayDateColumn - INTERVAL dayofyear(now()) -1 DAY)

Или с отдельными полями даты, я думаю, вы могли бы использовать что-то вроде этого:

SELECT * 
FROM BirthdayTable
ORDER BY dayofyear(cast(CONCAT_WS("-", year, month, day) as date) - INTERVAL dayofyear(now()) -1 DAY)

Эти операторы ORDER BY сортируют вещи на основе дня рождения в году, смещенного на текущий день года, чтобы получить порядок, начинающийся сегодня для каждого дня рождения.

1 голос
/ 16 сентября 2009

Самое элегантное решение - игнорировать годы .Нет необходимости преобразовывать что-либо в тип даты для выполнения сортировки.

Вместо этого создайте ключ сортировки, который предполагает, что все месяцы составляют 31 день (максимум).Когда месяц в будущем, добавьте месяц * 31 дней.Когда в прошлом, добавьте это плюс год.Затем добавьте смещение для дня.

Для текущего месяца посмотрите на день месяца и сделайте то же самое: если в будущем добавьте его.Если нет, добавьте его плюс 12 «длинных» (31-дневных) месяцев.

Это решение будет работать правильно в високосные годы и не требует преобразования отдельных полей в даты.

 SELECT * FROM mytable ORDER BY
     CASE 
       /* Month has passed this year, sort key considers it a "long year" further in the future) */
       WHEN month - MONTH(NOW()) < 0 THEN (month+12) * 31 + day
       /* Month has not passed year, sort key is a "long month" + days in the future */
       WHEN month - MONTH(NOW()) > 0 THEN month * 31 + day
       /* Same month, so we have to compare based on the day of the month */
       ELSE
         CASE 
           WHEN day - DAY(NOW()) < 0 THEN day + (12*31)
           ELSE day
       END CASE
     END CASE
1 голос
/ 16 сентября 2009

Я бы сохранил даты рождения в формате даты, но, если не считать этого, попробуйте сделать что-то вроде этого - выберите всех людей с их датами рождения, преобразованными в дату их следующего дня рождения, а затем закажите их. Создать дату из произвольной строки легко в MySQL, используя STR_TO_DATE. Тогда вам нужно только сделать оператор IF, чтобы выбрать правильный год. Попробуйте что-то вроде этого:

SELECT * FROM (
    SELECT name, IF(
        STR_TO_DATE(CONCAT(YEAR(NOW()), '-', month, '-', day)) < NOW(),
        STR_TO_DATE(CONCAT(YEAR(NOW())+1, '-', month, '-', day)), 
        STR_TO_DATE(CONCAT(YEAR(NOW()), '-', month, '-', day))) AS next_birthday 
    FROM people) as next_birthdays 
 ORDER BY next_birthday;

Это, я думаю, должно сработать. Конечно, было бы проще, если бы у вас был один столбец типа DATE.

0 голосов
/ 16 сентября 2009

Вот один из способов

select *
from foo 
order by 
if ( (dayofyear(concat(year(now()),'-',month,'-',day))-dayofyear(now())) < 0,
     (dayofyear(concat(year(now()),'-',month,'-',day))-dayofyear(now()))+365,
     (dayofyear(concat(year(now()),'-',month,'-',day))-dayofyear(now()))
   );

По сути, он упорядочивает количество дней до дня рождения в этом году, если только это не произошло в прошлом; в этом случае он добавляет к нему еще 365 дней, чтобы узнать, сколько дней наступит до следующего.

0 голосов
/ 16 сентября 2009
SELECT *
  FROM table
 ORDER BY CASE WHEN month*100+day > MONTH(getdate())*100+DAY(getdate())
           THEN (month - MONTH(getdate()))*100 + day - DAY(getdate())
           ELSE (12 + month - MONTH(getdate()))*100 + day - DAY(getdate())
      END
0 голосов
/ 16 сентября 2009

поэтому индекс будет включать только одно поле

ID|name|year|month|day|orderextrac
----------------------
1 |john|1978|5|1  |19780501
2 |mike|1979|7|23 |19790723 
3 |bob |1985|2|14 |19850214
4 |joe |1964|2|16 |19640216
5 |jane|1975|9|22 |19750922

select name from table1 order orderextrac
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...