MYSQL - 即将到来的X出生日期

时间:2014-06-29 10:32:23

标签: mysql sql date limit

我有一张包含姓名和出生日期的表格 我想得到与下一个即将到来的生日相对应的名字。 (其中n是整数)
使用LIMIT(我不想限制行数)是不可能的,因为它不是在限定的时间内使用BETWEEN或WHERE子句。 谢谢你的帮助

2 个答案:

答案 0 :(得分:0)

以下查询...

1)首先对不同的dob进行排序(默认情况下,今天先排序)。

2)使用LIMIT函数和

选择第一个dob

3)最后,选择所有拥有最早5个dob的用户。

SELECT u1.*
from USERS u1
INNER JOIN
(  
    SELECT DISTINCT 
      EXTRACT(MONTH FROM dob) dob_month,
      EXTRACT(DAY FROM dob) dob_day
    FROM users
    ORDER BY 
    CASE
      WHEN EXTRACT(MONTH FROM dob) = EXTRACT(MONTH FROM CURDATE()) AND EXTRACT(DAY FROM dob) = EXTRACT(DAY FROM CURDATE()) THEN 1
      WHEN EXTRACT(MONTH FROM dob) = EXTRACT(MONTH FROM CURDATE()) AND EXTRACT(DAY FROM dob) < EXTRACT(DAY FROM CURDATE()) THEN -1
      ELSE SIGN(EXTRACT(MONTH FROM dob) - EXTRACT(MONTH FROM CURDATE()))
    END DESC, 
    CASE
      WHEN EXTRACT(MONTH FROM dob) = EXTRACT(MONTH FROM CURDATE()) AND EXTRACT(DAY FROM dob) = EXTRACT(DAY FROM CURDATE()) THEN 1
      ELSE SIGN(EXTRACT(DAY FROM dob) - EXTRACT(DAY FROM CURDATE()))
    END DESC,     
    EXTRACT(MONTH FROM dob) ASC,
    EXTRACT(DAY FROM dob) ASC
  LIMIT 5
) selected_dobs
ON EXTRACT(MONTH FROM u1.dob) = selected_dobs.dob_month and EXTRACT(DAY FROM u1.dob) = selected_dobs.dob_day
ORDER BY
    CASE
      WHEN EXTRACT(MONTH FROM dob) = EXTRACT(MONTH FROM CURDATE()) AND EXTRACT(DAY FROM dob) = EXTRACT(DAY FROM CURDATE()) THEN 1
      WHEN EXTRACT(MONTH FROM dob) = EXTRACT(MONTH FROM CURDATE()) AND EXTRACT(DAY FROM dob) < EXTRACT(DAY FROM CURDATE()) THEN -1
      ELSE SIGN(EXTRACT(MONTH FROM dob) - EXTRACT(MONTH FROM CURDATE()))
    END DESC, 
    CASE
      WHEN EXTRACT(MONTH FROM dob) = EXTRACT(MONTH FROM CURDATE()) AND EXTRACT(DAY FROM dob) = EXTRACT(DAY FROM CURDATE()) THEN 1
      ELSE SIGN(EXTRACT(DAY FROM dob) - EXTRACT(DAY FROM CURDATE()))
    END DESC,     
    EXTRACT(MONTH FROM dob) ASC,
    EXTRACT(DAY FROM dob) ASC, 
    username ASC;

SQL Fiddle demo

注意

没有任何行从SELECT过滤掉,因为下一个5个生日(部分或全部)可能会在下一个日历年出现(例如今天可能是12月28日,接下来的5个生日可能会在12月发生2月31日,1月2日,1月5日,1月6日)。

如果您希望今天的生日选择在列表的底部(而不是列表的顶部,如上面的查询中所做的那样),只需在ORDER BY子句中将1更改为-1 ,如下所示

ORDER BY CASE WHEN EXTRACT(MONTH FROM dob) = EXTRACT(MONTH FROM CURDATE()) AND EXTRACT(DAY FROM dob) = EXTRACT(DAY FROM CURDATE()) THEN -1 WHEN EXTRACT(MONTH FROM dob) = EXTRACT(MONTH FROM CURDATE()) AND EXTRACT(DAY FROM dob) < EXTRACT(DAY FROM CURDATE()) THEN -1 ELSE SIGN(EXTRACT(MONTH FROM dob) - EXTRACT(MONTH FROM CURDATE())) END DESC, CASE WHEN EXTRACT(MONTH FROM dob) = EXTRACT(MONTH FROM CURDATE()) AND EXTRACT(DAY FROM dob) = EXTRACT(DAY FROM CURDATE()) THEN -1 ELSE SIGN(EXTRACT(DAY FROM dob) - EXTRACT(DAY FROM CURDATE())) END DESC, EXTRACT(MONTH FROM dob) ASC, EXTRACT(DAY FROM dob) ASC

答案 1 :(得分:0)

首先,您将需要Calendar table(此特定示例适用于SQL Server,但应具有适应性)。它是最有用的分析/维度表之一,您可以经常使用它们来获取通常不具备SARG能力的查询的基于索引的访问权限。

对于这种情况,我们需要一个像这样的最小表:

CREATE TABLE (isoDate DATE,
              dayOfMonth INTEGER,
              month INTEGER)

...和两个索引,从[isoDatedayOfMonthmonth]和[dayOfMonthmonthisoDate开始](或[monthdayOfMonthisoDate])。

我们现在可以使用它来获得下一个5岁生日:

SELECT DISTINCT Next.isoDate, Next.month, Next.dayOfMonth
FROM Users
JOIN Calendar Start
  ON Start.isoDate = Users.dateOfBirth
JOIN Calendar Next
  ON Next.isoDate >= CURDATE()
     AND Next.month = Start.month
     AND Next.dayOfMonth = Start.dayOfMonth
ORDER BY Next.isoDate 
LIMIT 5

这将获得月/日组合的所有下一个实例,循环年数(如果需要,可以多次包括人)。请注意,这不会让人们在2月29日过生日(因此这些人每年都不会过生日)。

我们现在可以再次加入以获取名称:

SELECT Birthdays.isoDate AS dateOfBirth, Users.name
FROM (SELECT DISTINCT Next.isoDate, Next.month, Next.dayOfMonth
      FROM Users
      JOIN Calendar Start
        ON Start.isoDate = Users.dateOfBirth
      JOIN Calendar Next
        ON Next.isoDate >= CURDATE()
           AND Next.month = Start.month
           AND Next.dayOfMonth = Start.dayOfMonth
      ORDER BY Next.isoDate 
      LIMIT 5) Birthdays
JOIN Calendar Origin
  ON Origin.month = Birthdays.month
     AND Origin.dayOfMonth = Birthdays.dayOfMonth
JOIN Users 
  ON Users.dateOfBirth = Origin.isoDate
ORDER BY Birthdays.isoDate

你是金色的