2010-02-08 26 views
5

Estoy tratando de ordenar un conjunto de resultados que da a los 5 usuarios más cercanos ordenados por el próximo cumpleaños. Esto funciona perfectamente hasta que entren los años bisiestos. Por ejemplo:recordatorio de cumpleaños de mysql, año bisiesto

  • mayo 15ta - 96 días dejaron
  • 15 de mayo - 97 días dejaron

El resultado superior es una luz en 1987 y la inferior es de 1988. u_birth se almacena como aaaa-mm-dd ¿Existe una manera simple de resolver este problema sin tener que volver a escribir la consulta completa?

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 

Esta consulta se toma y se modificó a partir del manual de MySQL: http://dev.mysql.com/doc/refman/5.1/en/date-and-time-functions.html#c7489

Respuesta

7

Obviamente hay un problema si su algoritmo depende del año de nacimiento de la persona. Para solucionar esto, primero encuentre el próximo cumpleaños de cada persona después de la fecha actual, luego calcule la diferencia entre esa fecha y ahora.

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 

Resultados: Los datos

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

Prueba:

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

Tenga en cuenta que alguien nacido en un día bisiesto se supone que tiene un cumpleaños del 28 de febrero en un año no bisiesto.

Además, su consulta no incluye el ID de usuario del usuario. Probablemente quieras agregar esto también, me imagino.

+0

¡Gracias! Echaré un vistazo, dame un par de minutos para probarlo =) – moodh

+0

@tired: ten en cuenta que he hecho un pequeño cambio para que todas las personas cuyos cumpleaños sean hoy tengan distancia 0, no distancia 365. Cambié un 'AHORA()' a 'FECHA (AHORA())'. –

+0

Funciona como debe; sin embargo, tengo una pregunta de seguimiento: ¿Cómo recupero filas adicionales de la tabla? Intenté las tres selecciones pero no tuve suerte, digamos que también quiero recuperar s_agehide. ¡Gracias por toda la ayuda! – moodh

1

No utilice dayofyear como nada después Feb 29 ° en un año bisiesto es de un día más de lo habitual. En su lugar, extraiga el mes y el día y concatúnelos junto con el año de hoy. Luego haz la comparación.

así:

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

ETA:

Sólo por lo que no tiene que volver a escribir la expresión para la conversión de la fecha de nacimiento de cumpleaños, me quedo en una variable. Te recomiendo que escribas una función que realice la conversión para que puedas usarla en una consulta directamente.

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

He intentado reemplazar todo el día de cada año con los ejemplos que mostró, sí, la edición del año bisiesto desaparece pero el valor y la clasificación están completamente desactivados. ¿Podría mostrarme toda la consulta reescrita? – moodh

1

Quizás alguien se inspire en esta paz de código.

Funcionó a la perfección en mi caso, pero creo que hay mucho a la fecha cálculo ...

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

Por esta paz de SQL-Code obtendrá todos los usuarios de los fe_users tabla cuyo fecha_de_nacimiento (guardado como unix-timestamp) estará dentro de los próximos 7 días. Puede extender esto fácilmente a 21 días cambiando el intervalo utilizado por la función date_add().

+0

Por cierto, este script es el mejor en caso de rendimiento, como lo he probado ... – SimonSimCity

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: fecha de nacimiento es la columna donde i almacenada la fecha de nacimiento de los empleados. empleados es el nombre de la tabla.

Cuestiones relacionadas