2013-09-11 5 views
10

Я пытаюсь написать запрос, чтобы выбрать пользователей базы данных, чьи дни рождения находятся в следующие 7 дней.mySQL SELECT предстоящие дни рождения

Я провел много исследований, но я не могу придумать рабочее решение.

Поле рождения хранится как varchar, например '04/16/93 ', есть ли способ работать с этим?

Это то, что я до сих пор:

SELECT * 
FROM `PERSONS` 
WHERE `BIRTHDAY` > DATEADD(DAY, -7, GETDATE()) 

я должен был бы сделать его более ясным, я пытаюсь найти дни рождения не даты рождения. Поэтому я просто ищу годы и месяцы, а не годы.

ответ

36

Чтобы получить все дни рождения в течение следующих 7 дней, добавьте разницу в году между датой рождения и сегодняшним днем ​​до даты рождения, а затем найдите, не попадает ли он в течение следующих семи дней.

SELECT * 
FROM persons 
WHERE DATE_ADD(birthday, 
       INTERVAL YEAR(CURDATE())-YEAR(birthday) 
         + IF(DAYOFYEAR(CURDATE()) > DAYOFYEAR(birthday),1,0) 
       YEAR) 
      BETWEEN CURDATE() AND DATE_ADD(CURDATE(), INTERVAL 7 DAY); 

Если вы хотите исключить сегодняшние дни рождения просто изменить > к >=

SELECT * 
FROM persons 
WHERE DATE_ADD(birthday, 
       INTERVAL YEAR(CURDATE())-YEAR(birthday) 
         + IF(DAYOFYEAR(CURDATE()) >= DAYOFYEAR(birthday),1,0) 
       YEAR) 
      BETWEEN CURDATE() AND DATE_ADD(CURDATE(), INTERVAL 7 DAY); 

-- Same as above query with another way to exclude today's birthdays 
SELECT * 
FROM persons 
WHERE DATE_ADD(birthday, 
       INTERVAL YEAR(CURDATE())-YEAR(birthday) 
         + IF(DAYOFYEAR(CURDATE()) > DAYOFYEAR(birthday),1,0) 
       YEAR) 
      BETWEEN CURDATE() AND DATE_ADD(CURDATE(), INTERVAL 7 DAY) 
    AND DATE_ADD(birthday, INTERVAL YEAR(CURDATE())-YEAR(birthday) YEAR) <> CURDATE(); 


-- Same as above query with another way to exclude today's birthdays 
SELECT * 
FROM persons 
WHERE DATE_ADD(birthday, 
       INTERVAL YEAR(CURDATE())-YEAR(birthday) 
         + IF(DAYOFYEAR(CURDATE()) > DAYOFYEAR(birthday),1,0) 
       YEAR) 
      BETWEEN CURDATE() AND DATE_ADD(CURDATE(), INTERVAL 7 DAY) 
    AND (MONTH(birthday) <> MONTH(CURDATE()) OR DAY(birthday) <> DAY(CURDATE())); 

Вот DEMO всех запросов

+2

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

+1

@GoatCO дух! глядя на вопрос и запрос OP, я просто предположил, что день рождения в текущем году хранится в таблице. Во всяком случае, обновленный ответ. Надеюсь, что это сработает. – Lobo

+0

Справа у меня другая проблема, которую я не понимал. День рождения хранится как varchar, например '04/16/1993 ', каким-либо образом работать с этим? – Danny

2

попробовать это:

Select * From persons 
where (DayOfYear(birthday) >= 7 
     And DayOfYear(birthday) - DayOfYear(curdate()) Between 0 and 6) Or 
     (MOD(YEAR(curDate()),4) = 0) And MOD(YEAR(curDate()),100) != 0 
     And (DayOfYear(birthday) + 366 - DayOfYear(curdate())) % 366 < 7) Or 
     (DayOfYear(birthday) + 365 - DayOfYear(curdate())) % 365 < 7) 
+0

не для nitpick, но это будет не один день для високосных лет. – user2366842

+0

Фиксация високосного года выпуска n теперь –

2

мне удалось заставить его работать с этим запросом. В основном благодаря помощи ответа Лобо.

SELECT * 
FROM persons 
WHERE DATE_ADD(STR_TO_DATE(birthday, '%m/%d/%Y'), INTERVAL YEAR(CURDATE())-YEAR(STR_TO_DATE(birthday, '%m/%d/%Y')) YEAR) 
      BETWEEN CURDATE() AND DATE_ADD(CURDATE(), INTERVAL 7 DAY); 
+0

см. Обновленный ответ. Был дефект, который с тех пор был исправлен. – Lobo

1

Я нашел этот код работать очень хорошо:

DATE_ADD(user_birthdate, INTERVAL YEAR(FROM_DAYS(DATEDIFF(CURDATE(), user_birthdate)-1)) + 1 YEAR) AS next_birthday 

это на самом деле очень просто, это вычислить возраст человека, то год рождения текущего и затем добавьте 1 год.

его на основе ответа Роберта Айзеле, который вы можете найти здесь: http://dev.mysql.com/doc/refman/5.1/en/date-and-time-functions.html

P.S.

с этим решением вы могли бы получить людей, у которых был день рождения вчера (это потому, что -1 в расчете FROM_DAYS, но это необходимо из-за високосных годов). это не должно считать вас слишком много, так как вы хотите только 7 дней в голове, так что вы должны просто добавить следующее условие:

HAVING next_birthday BETWEEN CURDATE() AND DATE_ADD(CURDATE(), INTERVAL 7 DAY) 
1

Принятый ответ выше от Lobo несовершенен в том, что, если запрос выполняется на декабре 31, и у пользователя есть день рождения 1 января, они не будут сопоставлены.

Вы должны добавить логику в запрос, чтобы, если день рождения уже прошел в этом году, вы смотрите день рождения NEXT YEAR, а не в этом году. Подробности ниже:

SELECT *, IF(DAYOFYEAR(STR_TO_DATE(birthday, '%m/%d/%Y')) < DAYOFYEAR(NOW()), 
DATE_ADD(STR_TO_DATE(birthday, '%m/%d/%Y'), INTERVAL YEAR(CURDATE()) - YEAR(STR_TO_DATE(birthday, '%m/%d/%Y')) + 1 YEAR), 
DATE_ADD(STR_TO_DATE(birthday, '%m/%d/%Y'), INTERVAL YEAR(CURDATE()) - YEAR(STR_TO_DATE(birthday, '%m/%d/%Y')) YEAR)) 
AS nextBirthday FROM persons 
WHERE nextBirthday BETWEEN CURDATE() AND DATE_ADD(CURDATE(), INTERVAL 7 DAY); 
0

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

SELECT first_name, last_name, birth_date, 
    IF (DAYOFYEAR(DATE_FORMAT(birth_date,'1980-%m-%d %T')) < DAYOFYEAR(DATE_FORMAT(NOW(),'1980-%m-%d %T')) , 
      DAYOFYEAR(DATE_FORMAT(birth_date,'1980-%m-%d %T'))+368 , 
      DAYOFYEAR(DATE_FORMAT(birth_date,'1980-%m-%d %T'))) AS upcomming 
FROM users 
WHERE birth_date IS NOT NULL AND birth_date !=0 
ORDER BY upcomming ASC 
LIMIT 0, 10 

Он устанавливает каждый год (в том числе фактического) на високосный год, так что не будет никаких проблем с этим. Если вы приближаетесь к концу года, это тоже не проблема. Я вполне доволен окончательным поиском рабочего решения, поэтому хотел поделиться этим, может быть, он полезен для кого-то другого :)

PS: Если вы не хотите показывать сегодняшние дни рождения, просто добавьте +1 после DAYOFYEAR(DATE_FORMAT(NOW(),'1980-%m-%d %T'))

6

Это мое решение. Он также работает, если дата рождения 1 января и текущая дата 31 декабря

SELECT `id`, `name`, `dateofbirth`, 
    DATE_ADD(
     dateofbirth, 
     INTERVAL IF(DAYOFYEAR(dateofbirth) >= DAYOFYEAR(CURDATE()), 
      YEAR(CURDATE())-YEAR(dateofbirth), 
      YEAR(CURDATE())-YEAR(dateofbirth)+1 
     ) YEAR 
    ) AS `next_birthday` 
FROM `user` 
WHERE 
    `dateofbirth` IS NOT NULL 
HAVING 
    `next_birthday` BETWEEN CURDATE() AND DATE_ADD(CURDATE(), INTERVAL 7 DAY) 
ORDER BY `next_birthday` 
LIMIT 1000; 
+0

Я боюсь, что это пойдет не так, если год рождения - високосный год, а текущий - нет или наоборот: dateofbirth = 2000-07-04, curdate = 2015-07-05 вычисляет next_birthday как 2015-07-04 вместо от 2016-07-04 (что не наносит вреда запросу о следующей неделе); но dateofbirth = 2001-07-04, curdate = 2016-07-04 приводит к next_birthday = 2016-07-04 вместо 2015-07-04 –

+0

Хороший catch @HagenvonEitzen Я постараюсь предоставить исправление для этого. – Todor

1

Это то, что я сделал, когда я столкнулся с той же проблемой:

select * from users 
where (month(date_of_birth) > month(CURDATE()) 
and month(date_of_birth) < month(ADDDATE(CURDATE(),30))) 
or (month(CURDATE()) = month(date_of_birth) and day(date_of_birth) >= day(CURDATE()) 
or month(ADDDATE(CURDATE(),30)) = month(date_of_birth) and day(date_of_birth) <= day(ADDDATE(CURDATE(),30))) 
or (year(CURDATE()) > year(ADDDATE(CURDATE(),30)) and month(date_of_birth) < month(CURDATE()) and month(date_of_birth) > month(ADDDATE(CURDATE(),30))) 
0
SELECT * from persons as p 
WHERE 
MOD(DAYOFYEAR(p.bday) - DAYOFYEAR(CURRENT_DATE()) + 366, 366) 
BETWEEN 0 and 7 
ORDER by DAYOFYEAR(p.bday) ASC 

Это работает для меня ,

1

При попытке получить список, у которого есть свои b'days, предстоящие в течение определенного периода времени, мы можем выполнить пару вопросов.

Когда есть високосный год, существует вероятность того, что у вас не получается обработать случай високосных годов. Следующая проблема может быть как сегодня: 2016-12-30, и вам нужно b'days в течение следующих 7 дней. Таким образом, конец периода в году 2017. Некоторое условие терпит неудачу в этом случае. Это очень важные тестовые примеры.

Большинство исправленных в этой теме используют DAYOFYEAR(), который терпит неудачу, когда вы находитесь в високосный год.

например.

DAYOFYEAR('2016-03-01 00:00:00') является 61.
DAYOFYEAR('2015-03-01 00:00:00') является 60

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

  1. Рассчитать следующий предстоящий b'day для пользователя.
  2. Затем проверьте, пришел ли день в нашем ассортименте.

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

SELECT * 
FROM `PERSONS` 
WHERE 
    /* 
     Here we calculate the next coming b'day for user 
     and check if it is between our span 
    */ 
    CONCAT(IF( 
      CONCAT(YEAR(CURDATE()), substring(`BIRTHDAY`, 5, length(`BIRTHDAY`))) < CURDATE(), 
      YEAR(CURDATE()) + 1, /* Adds an year if already past */ 
      YEAR(CURDATE()) /* Use this year if it is upcoming */ 
     ), substring(`BIRTHDAY`, 5, length(`BIRTHDAY`))) 
    BETWEEN CURDATE() AND DATE_ADD(CURDATE(), INTERVAL @tot DAY) 

PS: Также это лучшее решение, если вы хотите заказать их на основе b'days. Вам просто нужно добавить это как поле.

0

Опираясь на ответ Лобо решать високосный

SELECT * FROM users 
WHERE DATE_ADD(dob,INTERVAL YEAR(CURDATE())-YEAR(dob) 
    + IF(MONTH(CURDATE()) > MONTH(dob), 1, 
    IF(MONTH(CURDATE()) = MONTH(dob) AND DAY(CURDATE()) > DAY(dob), 1, 0)) 
     YEAR) 
     BETWEEN CURDATE() AND DATE_ADD(CURDATE(), INTERVAL 7 DAY) 
0

Так что другой подход вы можете сделать, это сделать немного больше работы, прежде чем нажать слой базы данных. Например, я недавно сделал это в проекте Laravel (PHP) и Postgres. Я в основном создал массив дат (т.е.следующие 7 дней), и сделал запрос where in, чтобы найти пользователей с деньгами в эти даты.

User::whereNotNull('birth_date') 
    ->where('birth_date', '<=', $endDate) 
    ->whereIn(DB::raw("to_char(birth_date, 'MMDD')"), Carbon::range($startDate, $endDate)->map(function ($date) { 
     return $date->format('md'); 
    }))->get(); 
Смежные вопросы