2010-02-08 13 views
5

Próbuję uporządkować zestaw wyników, który daje 5 najbliższych użytkowników posortowanych według nadchodzących urodzin. Działa to doskonale, dopóki nie wkroczą lata przestępne. NpPrzypominamy o mysql, rok przestępny

  • 15 maja - 96 dni na
  • 15 maja - 97 dni na

najlepszy wynik jest poród w 1987 roku, a dolna jest od 1988 roku u_birth jest przechowywana jako rrrr-mm-dd. Czy istnieje prosty sposób na posortowanie tego problemu bez konieczności przepisywania całego zapytania?

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 

Zapytanie to jest zrobione i modyfikowany instrukcji mysql: http://dev.mysql.com/doc/refman/5.1/en/date-and-time-functions.html#c7489

Odpowiedz

7

Istnieje oczywiście problem, jeśli algorytm zależy od roku urodzenia osoby. Aby to obejść, najpierw znajdź następne urodziny każdej osoby po bieżącej dacie, a następnie oblicz różnicę między tą datą a teraz.

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 

Wyniki: Dane

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

Test:

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

Zauważ, że ktoś urodził się w dniu przestępnym zakłada mieć urodziny 28 lutego na rok non-Leap.

Ponadto zapytanie nie zawiera identyfikatora użytkownika. Prawdopodobnie również chcesz to dodać, jak sobie wyobrażam.

+0

Dzięki! Popatrzę, daj mi kilka minut na wypróbowanie =) – moodh

+0

@tired: Zauważ, że wprowadziłem niewielką zmianę, aby wszystkie osoby, których urodziny są dzisiaj mają odległość 0, a nie odległość 365. Zmieniłem 'NOW()' na 'DATE (NOW())'. –

+0

Działa tak, jak powinien, jednak mam jedno dodatkowe pytanie: Jak mogę pobrać dodatkowe wiersze z tabeli? Próbowałem we wszystkich trzech wybranych, ale bez powodzenia, powiedzmy, że chcę również pobrać s_agehide. Dzięki za pomoc! – moodh

1

Nie używaj dayofyear jako coś po 29 lutego w roku przestępnym jest jeden dzień dalej niż zwykle. Zamiast tego wyodrębnij miesiąc i dzień i połącz je z dzisiejszym rokiem. Następnie wykonaj porównanie.

tak:

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

ETA:

Tak więc nie mam przepisać wyraz do konwersji datę urodzenia do urodzin, będę trzymać ją w zmiennej. Polecam napisać funkcję, która wykonuje konwersję, dzięki czemu można go użyć bezpośrednio w zapytaniu.

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

Próbowałem zastąpić wszystkie dayofyear przykładami, które pokazałeś, tak, problem z rokiem przestępnym zniknie, ale wartość i sortowanie są całkowicie wyłączone. Czy mógłbyś mi pokazać całe przepisane zapytanie? – moodh

1

Może ktoś zainspiruje się tym spokojem kodu.

To działało idealnie w moim przypadku, ale myślę, że jest za dużo data kalkulacji ...

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

W tym pokoju SQL-Code dostaniesz wszystkich użytkowników fe_users stołowych których date_of_birth (zapisany jak unix-timestamp) będzie w ciągu najbliższych 7 dni. Możesz łatwo przedłużyć to do 21 dni, zmieniając interwał używany przez funkcję date_add().

+0

Przy okazji - ten skrypt jest najlepszy w przypadku wydajności - jak już testowałem ... – 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: Data urodzenia jest kolumna, gdzie przechowywane datę urodzenia pracowników. pracowników jest nazwą tabeli.