Pull to refresh

Выбор дней рождения на MySQL + мелочи

Reading time4 min
Views5.4K
Дали задание: выбрать из базы пользователей, у которых на днях день рождения, — для вывода на главной странице сайта.
Используем PHP (ZF) & MySQL.

Поле в нашей БД имеет тип “date”, что уже хорошо. Задание простое, начал писать, …
Быстрее, думаю, погуглить, зачем велосипед изобретать, явно уже решалось сотни раз.
Посмотрел первый запрос, — неправильно он работает, второй – тоже…
Нашел правильный, но он оказался таким монстроподобным.
Удивлению не было предела – некоторые из этих «велосипедов» изобретены в текущем году!

Написал в итоге сам. Для начала попроще (Здесь выбираются дни рождения с 01 сент. по 29 окт.):

SELECT * FROM users__accounts WHERE DATE_FORMAT(birthday,'%m%d') >= '0901' AND DATE_FORMAT(birthday,'%m%d') <= '1029'

* This source code was highlighted with Source Code Highlighter.


Но надо задать период на ближайшие N дней. И как же новогодний переход?
Написал нечто подобное:

SET @dayplus:=15;
SET @andor:=CASE WHEN YEAR(CURDATE() + INTERVAL @dayplus DAY) - YEAR(CURDATE()) THEN 'OR' ELSE 'AND' END;
SET @fromdate:=DATE_FORMAT(CURDATE(),'%m%d');
SET @todate:=DATE_FORMAT(CURDATE() + INTERVAL @dayplus DAY,'%m%d');
SET @birthday_query:=CONCAT("SELECT * FROM users__accounts WHERE DATE_FORMAT(birthday,'%m%d') >= @fromdate ",
                @andor, " DATE_FORMAT(birthday,'%m%d') <= @todate");
PREPARE birthday_query FROM @birthday_query;
EXECUTE birthday_query;
DEALLOCATE PREPARE archive_query;

* This source code was highlighted with Source Code Highlighter.


Решил, что процедуры SQL мне еще рано писать. К тому же сформированный на Zend Framework запрос будет проще. Получилось:

class Users extends Zend_Db_Table_Abstract
{
  protected $_name = 'users__accounts';
  /**
  ...
   */
  public function getBirthdayUsers($count, $days = 7)
  {
    $date = new Zend_Date();
    /* сегодня */
    $dateFrom = $date->toString('MMdd');
    /* мы заботимся о каждом ;) */
    if ($dateFrom == '0301' && !$date->isLeapYear()) {
      $dateFrom = '0229';
    }
      $yearFrom = $date->toString('YY');
    $date->addDay($days);
     /* сегодня + $days дней */
    $dateTo = $date->toString('MMdd');

    $nextYear = $yearFrom - $date->toString('YY');

    $select = $this->getAdapter()->select()
      ->from(array('u' => $this->_name))
      ->where("date_format(u.birthday,'%m%d') >= ?", $dateFrom)
      ->order('DAYOFYEAR(u.birthday)')
      ->limit($count);
    /* переход на следующий год - OR / AND */
    if ($nextYear == 0) {
      $select->where("date_format(u.birthday,'%m%d') < ?", $dateTo);
    } else {
      $select->orWhere("date_format(u.birthday,'%m%d') < ?", $dateTo);
    }

    $users = $select->query()->fetchAll();
    return $users;
  }  
}


* This source code was highlighted with Source Code Highlighter.


Константы передаются из PHP, а не вычисляются внутри запроса.
Теперь перечислю подводные камни ранее найденных велосипедов:
1. Не учитывался переход на новый год
2. Не учитывался високосный год при выборке по DAYOFYEAR() – ошибка на 1 день
3. К сожаленью, день рожденья, только раз… в 4 года
3. Слишком сложные вычисления
3. Выпадали крайние даты

Думаю тем, кто не использует ZF, тоже пригодится.
Сам SQL запрос получился довольно простой, но для очень больших БД надо заводить отдельное поле и индексировать. Часовые пояса не учитываются текущему по заданию.

Заодно и вычисление возраста приведу на PHP (в MySQL – еще проще). Т.к. встречал реализации от расчета в секундах (с високосной погрешностью) до «интегральных» на две страницы кода.

  /**
   * @param timestamp() $birth
   * @return INT
   */
  function getAge($birth)
  {
    
   $now = time();
   $age = date('Y', $now) - date('Y', $birth);
   if (date('md', $now) < date('md', $birth)) {
     $age--;
   }
   
   return $age;

  }

* This source code was highlighted with Source Code Highlighter.


П.С. Если существует более правильный велосипед, напишите. Тогда уж точно покончим с изобретением велосипедов данного типа. :)

Если минусуете, то ставьте плюс лучшему варианту из комментов, или предлагайте свой.

Tags:
Hubs:
0
Comments34

Articles

Change theme settings