web-dev-qa-db-fra.com

mySQL SELECT anniversaires à venir

J'essaie d'écrire une requête pour sélectionner les utilisateurs d'une base de données dont les anniversaires sont dans les 7 prochains jours.

J'ai fait beaucoup de recherches mais je ne peux pas trouver de solution efficace. 

Le champ anniversaire est stocké sous la forme d'un varchar, par exemple '04/16/93 ', y a-t-il un moyen de travailler avec cela?

Voici ce que j'ai jusqu'à présent:

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

J'aurais dû préciser, j'essaie de trouver des anniversaires et non des dates de naissance. Donc, je cherche seulement des jours et des mois, pas des années.

12
Danny

Pour obtenir tous les anniversaires dans les 7 prochains jours, ajoutez la différence d’année entre la date de naissance et aujourd’hui à la date de naissance, puis déterminez si elle tombe dans les sept prochains jours. 

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);

Si vous souhaitez exclure les anniversaires d'aujourd'hui, remplacez simplement > par >=

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()));

Voici une démo de toutes les requêtes

39
Praveen Lobo

C'est très facile et simple. Pas besoin d'utiliser n'importe quelle condition si ou autre chose, il vous suffit d'utiliser la fonction DATE_FORMAT () de mysql. 

Voici ma requête SQL qui est 

SELECT id,email ,dob FROM `users` where DATE_FORMAT(dob, '%m-%d') >= DATE_FORMAT(NOW(), '%m-%d') and DATE_FORMAT(dob, '%m-%d') <= DATE_FORMAT((NOW() + INTERVAL +7 DAY), '%m-%d')

8
Rakesh

Ceci est ma solution. Cela fonctionne également si la date de naissance est le 1er janvier et la date du jour est le 31 décembre.

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;
6
Todor

J'ai réussi à le faire fonctionner avec cette requête. En grande partie grâce à la réponse de Lobo.

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);
2
Danny

essaye ça:

 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)
2
Charles Bretana

Tout en essayant d'obtenir la liste des personnes qui auront leurs journées à venir dans un intervalle de temps, nous pouvons nous heurter à quelques problèmes.

Lorsqu'il y a une année bissextile, il se peut que votre condition ne vous aide pas à traiter le cas des années bissextiles. Le problème suivant pourrait être celui d’aujourd’hui est 2016-12-30 et vous avez besoin de jours ouvrables pour les 7 prochains jours Donc, la fin de la période correspond à l'année 2017. Certaines conditions échouent dans ce cas. Ce sont des cas de test très importants.

La plupart des problèmes résolus dans ce fil utilisent la DAYOFYEAR() qui échoue lorsque vous êtes sur une année bissextile. 

par exemple. 

DAYOFYEAR('2016-03-01 00:00:00') est61.
DAYOFYEAR('2015-03-01 00:00:00') est60

La façon la plus simple et la plus facile à comprendre est la suivante. 

  1. Calculez le prochain anniversaire pour un utilisateur.
  2. Ensuite, vérifiez si le jour vient dans notre gamme.

Cela fonctionne les années bissextiles et la plage de dates s'étend sur deux ans.

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: En outre, c’est la meilleure solution si vous souhaitez les commander sur la base des jours ouvrables. Vous devez juste ajouter ceci en tant que champ.

1
RRK

La réponse acceptée ci-dessus de Lobo est erronée en ce que, si la requête est exécutée le 31 décembre et que l'utilisateur a une date de naissance le 1 er janvier, elle ne sera pas appariée.

Vous devez ajouter une logique à la requête afin que, si l'anniversaire est déjà passé cette année, vous regardez l'anniversaire de l'ANNÉE PROCHAINE, pas celui de cette année. Détails ci-dessous:

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);
1
Tom McQuarrie

Voici ce que j'ai fait lorsque j'ai rencontré le même problème:

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)) )
1
VIVEK SEDANI

Nous avons eu des problèmes avec un calendrier qui devait afficher les dix prochains anniversaires (y compris les anniversaires d'aujourd'hui). J'ai réduit la solution que je viens de trouver aux parties appropriées:

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

Il fixe chaque année (année réelle) à une année bissextile, donc cela ne posera pas de problème. Si vous approchez de la fin de l'année, ce n'est pas un problème également. Je suis assez satisfait de trouver enfin une solution qui fonctionne. Je voulais donc partager ceci. Peut-être est-il utile à quelqu'un d'autre :)

P.S .: Si vous ne voulez pas montrer les anniversaires d'aujourd'hui, ajoutez simplement un +1 après DAYOFYEAR(DATE_FORMAT(NOW(),'1980-%m-%d %T'))

1
Fanmade

J'ai trouvé ce code très bien fonctionner:

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

en fait, il calcule l’âge de la personne, l’anniversaire de l’année en cours, puis ajoute un an.

basé sur la réponse de Robert Eisele que vous pouvez trouver ici: http://dev.mysql.com/doc/refman/5.1/fr/date-and-time-functions.html

p.s.

avec cette solution, vous pourriez chercher des personnes qui avaient un anniversaire hier (c'est-à-dire -1 dans le calcul de FROM_DAYS, mais il est nécessaire à cause des années bissextiles). cela ne devrait pas vous prendre trop au sérieux puisque vous ne voulez que 7 jours par tête, vous devez donc simplement ajouter la condition suivante:

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

Une autre approche consiste donc à faire un peu plus de travail avant d’atteindre la couche base de données. Par exemple, je l'ai récemment fait dans un projet Laravel (PHP) et Postgres. J'ai essentiellement construit un tableau de dates (c'est-à-dire les 7 prochains jours) et fait une requête where in pour trouver des utilisateurs avec des anniversaires à ces dates.

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();
0
Jonathan

J'ai fait beaucoup de "recherches" et voici ma solution, je suppose que c'est très facile à comprendre!

SELECT *,
(366 + DAYOFYEAR(birth_date) - DAYOFYEAR(NOW())) % 366 as left_days
FROM `profile`
ORDER BY left_days;
0

Voici un simple PHP Code & Requête SQL pour récupérer les anniversaires à venir. Où la date de naissance est enregistrée sous DATE (format AAAA-MM-JJ). entrez la description de l'image ici

<?php
$conn = mysqli_connect('localhost', 'user', 'paasword', 'databasename');
$nod = 5;     //Number of days upto which you want to retrieve birthdays
$delim = $filter = "";
for($i=0;$i<$nod;$i++){
    $date = date_create(date('Y-m-d'));
    date_add($date,date_interval_create_from_date_string("$i days"));
    $filter .= $delim."'".date_format($date,"m-d")."'";
    $delim = ", ";
}
$sql = "SELECT NAME, DOB, MOBILE, EMAIL, TITLE FROM tablename WHERE SUBSTR(DOB,6) IN ($filter) ORDER BY SUBSTR(DOB,6) ASC";
$result = mysqli_query($conn, $sql);    
$i=0;
echo '
<table class="table-1 mt-2 table-responsive table-bordered">
    <tr>
        <th>S. No.</th>
        <th>Name</th>
        <th>DOB</th>
        <th>MOBILE</th>
    </tr>';
while($row = mysqli_fetch_array($result)){
    $i++;
    $dob = date('Y-').date_format(date_create($row["DOB"]),'m-d');
    $day = date_format(date_create($dob),'w');
    switch ($day){
        case 0:
            $day = "Sunday"; break;
        case 1:
            $day = "Monday"; break;
        case 2:
            $day = "Tuesday"; break;
        case 3:
            $day = "Wednesday"; break;
        case 4:
            $day = "Thursday"; break;
        case 5:
            $day = "Friday"; break;
        case 6:
            $day = "Saturday"; break;
    }
    echo"
    <tr>
        <td>".$i."</td>
        <td>".$row["NAME"]."</td>
        <td>".date_format(date_create($row["DOB"]),'d-m-Y')." $day</td>
        <td>".$row["MOBILE"]."</td>
    </tr>
    ";
}
echo"
</table>";
?>
0
Ankit Verma

S'appuyant sur la réponse de Lobo pour s'attaquer aux années bissextiles

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
Tinnin
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

Cela fonctionne pour moi. 

0
Papick G. Taboada