2010-02-08 88 views
5

我试图理清一个结果集,它给出了按照即将到来的生日排序的5个最接近的用户。在闰年发挥作用之前,这完美运作。例如:mysql生日提醒,闰年

  • 5月15日 - 97天了
  • 5月15日 - 98天了

顶部结果是在1987年出生和下从1988年 u_birth存储为YYYY-MM-DD。 有没有简单的方法来排序这个问题,而不必重写整个查询?

SELECT u_birth, IF(DAYOFYEAR(u_birth) >= DAYOFYEAR(NOW()), 
      DAYOFYEAR(u_birth) - DAYOFYEAR(NOW()), 
      DAYOFYEAR(u_birth) - DAYOFYEAR(NOW()) + 
     DAYOFYEAR(CONCAT(YEAR(NOW()), '-12-31')) 
) 
AS distance 
FROM (blog_users) 
WHERE `s_agehide` = 0 
ORDER BY distance ASC 
LIMIT 5 

此查询取并修改从MySQL手册:http://dev.mysql.com/doc/refman/5.1/en/date-and-time-functions.html#c7489

回答

7

如果你的算法取决于人的出生年份,那么显然存在一个问题。要解决这个问题,首先在当前日期之后查找每个人的下一个生日,然后计算该日期与现在之间的差异。

SELECT u_birth, DATEDIFF(next_birthday, NOW()) AS distance FROM (
    SELECT *, ADDDATE(birthday, INTERVAL birthday < DATE(NOW()) YEAR) AS next_birthday 
    FROM (
     SELECT *, ADDDATE(u_birth, INTERVAL YEAR(NOW()) - YEAR(u_birth) YEAR) AS birthday 
     FROM blog_users 
     WHERE s_agehide = 0 
    ) AS T1 
) AS T2 
ORDER BY distance ASC 
LIMIT 5 

结果:

'1992-02-29', 20 
'1993-03-01', 21 
'1987-05-15', 96 
'1988-05-15', 96 
'1988-09-18', 222 

测试数据:

CREATE TABLE blog_users (u_birth NVARCHAR(100) NOT NULL, s_agehide INT NOT NULL); 
INSERT INTO blog_users (u_birth, s_agehide) VALUES 
('1987-05-15', 0), 
('1988-05-15', 0), 
('1988-09-20', 0), 
('2000-01-02', 0), 
('2000-01-03', 1), 
('1988-09-19', 0), 
('1988-09-18', 0), 
('1992-02-29', 0), 
('1993-03-01', 0); 

注意,有人出生在一个闰日被假定为具有2月28日对非闰年生日。

另外,您的查询不包括用户的用户标识。我想可能你也想补充一点。

+0

谢谢!我会看看,给我几分钟试一下=) – moodh 2010-02-08 20:14:45

+0

@tired:请注意,我做了一个小的改变,以便所有今天生日的人都有距离0,而不是距离365。 'NOW()'到'DATE(NOW())'。 – 2010-02-08 20:18:51

+0

它的工作原理是,但我有一个后续问题: 如何从表中检索额外的行?我已经尝试过所有三次选择,但没有运气,可以说我也想检索s_agehide。 感谢您的帮助! – moodh 2010-02-08 20:20:35

1

不要在闰年使用dayofyear作为任何2月29日之后为一天进一步上比平常。取而代之的是提取月份和日期,并将它们与今年的一起进行连接。然后做比较。

像这样:

SELECT u_birth, 
    IF(DATE(CONCAT(YEAR(NOW()),'-',MONTH(u_birth),'-',DAY(u-birth))) >= DATE(NOW()), 
... 

ETA:

只是让我不必重写表达式出生日期转换为生日,我会在一个变量坚持下去。我建议你编写一个转换函数,以便你可以直接在查询中使用它。

SET @birthday= SELECT DATE(CONCAT(YEAR(NOW()),'-',MONTH(u_birth),'-',DAY(u-birth))) FROM users WHERE user_id=x; //obviously only good for one value, but use the example for the calculation 

SELECT u_birth, 
     IF(DATE(@birthday)>=DATE(NOW()), 
      DATEDIFF(DATE_ADD(@birthday, INTERVAL 1 YEAR),NOW()), 
      DATEDIFF(NOW(),@birthday) 
     ) as days 
FROM users WHERE user_id=x; 
+0

我尝试用您显示的示例替换所有dayofyear,是的,闰年问题消失,但值和排序完全关闭。你能告诉我整个重写的查询吗? – moodh 2010-02-08 20:13:00

1

也许有人从代码的和平中受到启发。

它完美地工作在我的情况,但我认为有得多日期计算......

SELECT CONCAT(YEAR(CURRENT_DATE()),'-',DATE_FORMAT(FROM_UNIXTIME(date_of_birth), '%m-%d')), fe_users.* 
FROM `fe_users` 
WHERE `date_of_birth` != 0 AND (
    CONCAT(YEAR(CURRENT_DATE())+1,'-',DATE_FORMAT(FROM_UNIXTIME(date_of_birth), '%m-%d')) BETWEEN CURRENT_DATE() AND DATE_ADD(CURRENT_DATE(), INTERVAL 7 DAY) 
    OR 
    CONCAT(YEAR(CURRENT_DATE()),'-',DATE_FORMAT(FROM_UNIXTIME(date_of_birth), '%m-%d')) BETWEEN CURRENT_DATE() AND DATE_ADD(CURRENT_DATE(), INTERVAL 7 DAY) 
) 

通过这种和平的SQL代码,你会得到表fe_users其DATE_OF_BIRTH的所有用户(保存作为unix-timestamp)将在未来7天内完成。通过更改date_add()函数使用的时间间隔,您可以轻松地将其延长至21天。

+0

顺便说一句 - 这个脚本是最好的表现 - 我已经测试过... – SimonSimCity 2011-02-09 01:18:12

2
SELECT 
    CONCAT(
     YEAR(CURRENT_DATE()), 
     '-', 
     DATE_FORMAT((birthDate), 
      '%m-%d' 
     ) 
    ), 
    fullName 
FROM 
    employees 
WHERE 
    birthDate != 0 
AND(
    CONCAT(
     YEAR(CURRENT_DATE())+ 1, 
     '-', 
     DATE_FORMAT((birthDate), 
      '%m-%d' 
     ) 
    )BETWEEN CURRENT_DATE() 
    AND DATE_ADD(
     CURRENT_DATE(), 
     INTERVAL 21 DAY 
    ) 
    OR CONCAT(
     YEAR(CURRENT_DATE()), 
     '-', 
     DATE_FORMAT((birthDate), 
      '%m-%d' 
     ) 
    )BETWEEN CURRENT_DATE() 
    AND DATE_ADD(
     CURRENT_DATE(), 
     INTERVAL 21 DAY 
    ) 
) 

PS:生日是我保存了员工的出生日期列。 员工是表名。