【问题标题】:mySQL SELECT upcoming birthdaysmySQL SELECT 即将到来的生日
【发布时间】:2013-09-11 17:42:15
【问题描述】:

我正在尝试编写一个查询来选择数据库中生日在未来 7 天内的用户。

我做了很多研究,但我无法提出一个可行的解决方案。

生日字段存储为 varchar,例如 '04/16/93' 有什么方法可以处理这个吗?

这是我目前所拥有的:

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

我应该更清楚地说明,我正在尝试查找生日而不是出生日期。所以我只是在寻找几天和几个月而不是几年。

【问题讨论】:

    标签: mysql sql database


    【解决方案1】:

    要获取未来 7 天内的所有生日,请将出生日期与今天之间的年份差添加到出生日期,然后查找它是否在未来 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

    【讨论】:

    • 第一个查询只返回新生儿,第二个只返回未来出生的人......
    • @GoatCO 呵呵!查看问题和提供的查询 OP,我只是假设当前年份的生日存储在表中。无论如何,更新的答案。希望有效。
    • @DannyWalsh 正如您已经发现的那样,您需要使用STR_TO_DATE(birthday, '%m/%d/%Y') 将字符串转换为日期格式,并在上述查询中使用它代替birthday
    • 如果您在 12 月 31 日运行此脚本并且生日是 1 月 1 日,则此脚本将不起作用。
    • 恐怕这个解决方案也存在闰年问题(参见我在 commtn 中对 Todor 的回答的示例)。常见的问题是闰年和非闰年的生日DAYOFYEAR 3-12 月不同
    【解决方案2】:

    它非常容易和简单。无需使用任何 if 条件或其他任何内容,您只需要使用 mysql 的 DATE_FORMAT() 函数即可。

    这是我的 sql 查询

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

    【讨论】:

    • 当您尝试在下一年获得未来的生日时,这实际上不起作用。
    【解决方案3】:

    这是我的解决方案。如果出生日期是 1 月 1 日并且今天的日期是 12 月 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;
    

    【讨论】:

    • 如果出生年份是闰年而当前不是闰年,恐怕这会出错: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
    • 好消息@HagenvonEitzen 我会尽力解决这个问题。
    【解决方案4】:

    我做了很多“研究”,这是我的解决方案,我想这很容易理解!

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

    【讨论】:

    • 有时这会将昨天的生日返回为 0 left_days :-(
    【解决方案5】:

    我发现这段代码运行良好:

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

    其实很简单,就是计算人的年龄,然后是当年的生日,再加1年。

    它基于 Robert Eisele 的回答,您可以在这里找到: http://dev.mysql.com/doc/refman/5.1/en/date-and-time-functions.html

    附言

    使用此解决方案,您可能会获取昨天过生日的人(这是因为 FROM_DAYS 计算中的 -1,但由于闰年而需要它)。这不应该考虑你太多,因为你只想要 7 天,所以你应该添加以下条件:

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

    【讨论】:

      【解决方案6】:

      试试这个:

       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)
      

      【讨论】:

      • 现在修复闰年问题
      【解决方案7】:

      我设法让它与这个查询一起工作。主要归功于 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);
      

      【讨论】:

      • 查看更新后的答案。有一个缺陷已被修复。
      【解决方案8】:

      当出生日期发生在闰年时,任何使用 DAYOFYEAR() 的解决方案都会有缺陷。在处理年龄或生日时考虑使用 TIMESTAMPDIFF()。

      当前年龄可以计算为

      timestampdiff(YEAR, person.date_of_birth, curdate())
      

      通过将年龄添加到他们的出生日期来计算即将到来的生日。

      DATE_ADD(
          person.date_of_birth, 
          INTERVAL timestampdiff(YEAR, person.date_of_birth, curdate())+1 YEAR
      )
      

      把这些放在一起解决 OP 的查询

      select
          DATE_ADD(
              person.date_of_birth, 
              INTERVAL timestampdiff(YEAR, date_add(person.date_of_birth,INTERVAL 1 DAY), curdate())+1 YEAR
          ) upcoming_birthday
      from person
      having upcoming_birthday between curdate() and DATE_ADD(curdate(), INTERVAL 7 DAY)
      

      请注意,我在计算年龄时在 date_of_birth 上加了一天,否则今天的生日将不会返回。

      【讨论】:

      • 这是我见过的唯一正确答案。其余的不适用于跨年、闰年等。
      【解决方案9】:

      上面接受的来自 Lobo 的答案存在缺陷,如果查询是在 12 月 31 日运行的,并且用户的生日是 1 月 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);
      

      【讨论】:

        【解决方案10】:

        我们遇到了必须显示接下来十个生日(包括今天的生日)的日历的问题。 我已将刚刚找到的解决方案简化为相关部分:

        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
        

        它将每年(包括实际)设置为闰年,因此不会有任何问题。 如果你接近年底,那也没有问题。 我很高兴终于找到了一个可行的解决方案,所以我想分享这个,也许它对其他人有用:)

        P.S.:如果您不想显示今天的生日,只需在 DAYOFYEAR(DATE_FORMAT(NOW(),'1980-%m-%d %T')) 之后添加 +1

        【讨论】:

        • 这个想法是天才! @Fanmade 为什么是“368”?为什么是“%T”?
        • 我需要显示接下来 7 天或过去 2 天内所有即将到来的生日,而不是接下来的 10 个。 SELECT user_id,full_name,dob, IF (DAYOFYEAR(DATE_FORMAT(dob,'1980-%m-%d')) &lt; DAYOFYEAR(DATE_FORMAT(NOW(),'1980-%m-%d')) - 7, DAYOFYEAR(DATE_FORMAT(dob,'1980-%m-%d'))+366, DAYOFYEAR(DATE_FORMAT(dob,'1980-%m-%d'))) - DAYOFYEAR(DATE_FORMAT(NOW(),'1980-%m-%d')) AS dist FROM users u WHERE dob IS NOT NULL HAVING (dist BETWEEN -2 AND 30) ORDER BY dist&lt;0 ASC, CASE WHEN dist&lt;0 THEN -dist ELSE dist END ASC
        • 天哪,这是四年前的事了,从那以后我换了两次工作,我什至不记得我给过这个答案^^ 除此之外,“%T”确实是不必要的并且“368”只是随机输入的,“366”应该更好,同时仍然考虑闰年。你的解决方案对我来说似乎相当复杂。这种方式怎么样?
        • SELECT user_id, full_name, dob, IF (DAYOFYEAR(DATE_FORMAT(dob,'1980-%m-%d %T')) &lt; (DAYOFYEAR(DATE_FORMAT(NOW(),'1980-%m-%d %T'))-2) , DAYOFYEAR(DATE_FORMAT(dob,'1980-%m-%d %T'))+366 , DAYOFYEAR(DATE_FORMAT(dob,'1980-%m-%d %T'))) AS dist FROM users WHERE dob IS NOT NULL AND dob !=0 HAVING dist &lt; (DAYOFYEAR(DATE_FORMAT(NOW(),'1980-%m-%d %T'))+7) ORDER BY dist ASC;
        • 再次检查您的代码,如果您想在日期之前和之后拆分结果,同时仍然让数据能够以您喜欢的任何方式排序,这确实是有意义的。现在我确实比我更喜欢你的结果(而且它看起来甚至比我的方式更简单)。感谢分享:)
        【解决方案11】:

        当我遇到同样的问题时,我就是这样做的:

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

        【讨论】:

          【解决方案12】:

          在尝试获取在一段时间内即将迎来生日的名单时,我们可能会遇到几个问题。

          当有闰年时,你的条件有可能无法处理闰年的情况。下一个问题可能是今天是2016-12-30,接下来的 7 天你需要 b'days。所以期间的结束是在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 的基础上订购这些,这也是最好的解决方案。您只需将其添加为字段即可。

          【讨论】:

            【解决方案13】:

            这是闰年问题的优化解决方案

            SELECT * 
            FROM  persons 
            WHERE DATE_FORMAT( CONCAT(YEAR(CURDATE()),'-',DATE_FORMAT(birth_date, '%m-%d') ), '%Y-%m-%d')
                 BETWEEN CURDATE() AND DATE_ADD(CURDATE(), INTERVAL 7 DAY)
            

            【讨论】:

              【解决方案14】:
              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
              

              这对我有用。

              【讨论】:

                【解决方案15】:

                基于 Lobo 的解决闰年的答案

                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)
                

                【讨论】:

                  【解决方案16】:

                  因此,您可以采取的另一种方法是在到达数据库层之前做更多的工作。例如,我最近在 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();
                  

                  【讨论】:

                    【解决方案17】:

                    这是一个简单的 PHP 代码和 SQL 查询,用于检索即将到来的生日。其中出生日期存储为 DATE(YYYY-MM-DD 格式)。 enter image description here

                    <?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>";
                    ?>
                    

                    【讨论】:

                      【解决方案18】:

                      我认为这个解决方案可能更简单。

                      创建查询字符串,例如,(我使用 PHP 和 MySql)

                      $query = "SELECT cols FROM table WHERE MONTH(DobCol)= ".date('m')." AND (DAY(DobCol) >= ".date('d')." AND DAY(DobCol) <= ".(date('d')+$NumOfDaysToCheck).")";
                      

                      然后执行这个 $query。

                      【讨论】:

                        猜你喜欢
                        • 2016-03-14
                        • 1970-01-01
                        • 1970-01-01
                        • 2010-09-10
                        • 1970-01-01
                        • 2014-05-03
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        相关资源
                        最近更新 更多