2010-05-20 8 views
5

Mam tabelę z datami rocznic. Chcę zapytania, które zwróci mi wiersze rocznice najbliższych w ciągu najbliższych 10 dni. Na przykład:ustalenie, czy rocznica nadchodzi za n dni w MySql

birthdate 
--------- 
1965-10-10 
1982-05-25 


SELECT birthdate FROM Anniversaries WHERE mystical_magical_mumbo_jumbo <= 10 

+------------+ 
| birthdate | 
+------------+ 
| 1982-05-25 | 
+------------+ 
1 row in set (0.01 sec) 

Chciałbym zachować zapytanie w formie x <= 10, ponieważ użyję tej liczby 10 w innych częściach zapytania, a jeśli ustawić go do zmiennej, mogę zmienić raz na zawsze, zmieniając zmienną i nie trzeba jej ponownie pisać.

+1

Prawdopodobnie już to wiesz, ale zachowanie wyrażenia w tym formacie może wymusić funkcję po lewej stronie wyrażenia, co uniemożliwi użycie przez MySQL indeksu daty urodzin. –

+0

Czy "jednostronność" ma znaczenie? Jeśli ma postać "10 => x", czy pozwoli na użycie indeksu? – user151841

+1

@ user151841, najlepiej, aby nazwa kolumny znajdowała się po lewej stronie wyrażenia. W tym przypadku możesz zastosować funkcję do 10, aby uzyskać przyszłą datę i zrobić coś takiego: GDZIE data urodzenia> = DATE_FUNCTION (10) '. Jeśli ktoś inny mnie do tego nie pobije, sprawdzę konkretną funkcję i dam ci odpowiedź. –

Odpowiedz

6

jak inni stwierdzili, ty trzeba zignorować rok w porównaniu.Funkcja DAYOFYEAR() jest jednym ze sposobów, aby to zrobić.

Oto szybkie rozwiązanie z mojej góry. Zwróci wszystkie urodziny w ciągu najbliższych 10 dni, nawet jeśli jest koniec grudnia, a urodziny w przyszłym roku.

Nie obsługuje on lat przestępnych prawidłowo, więc zostanie wyłączony o 1 dzień w przypadku urodzin z początkiem marca, jeśli ten rok będzie rokiem przestępnym, a osoba nie urodziła się w roku przestępnym lub odwrotnie. Lata przestępne spowodują też, że urodziny na początku stycznia pojawią się pewnego dnia pod koniec grudnia. Jeśli ktoś chce dodać korekcję rok przestępny, nie krępuj się :)

SELECT birthdate 
FROM Anniversaries 
WHERE dayofyear(birthdate) - dayofyear(curdate()) between 0 and 10 
or dayofyear(birthdate) + 365 - dayofyear(curdate()) between 0 and 10; 
1

Spróbuj

SELECT birthdate FROM Anniversaries 
WHERE DATEDIFF(CURTIME(),birthdate) >= 0 
AND DATEDIFF(CURTIME(),birthdate) <= 10 

sprawdzić ten odsyłające MySQL DATEDIFF

DATEDIFF (wyrażenie1, wyrażenie2)

DateDiff() zwraca wyrażenie wyrażenie1 - wyr2 wyrażone jako wartość w dniach od jednego dnia do inny. wyrażenie1 i wyrażenie2 to wyrażenia daty lub daty i czasu. W obliczeniach wykorzystywane są tylko części daty z wartości.

mysql> SELECT DATEDIFF ('2007-12-31 23:59:59', '2007-12-30'); -> 1

mysql> SELECT DATEDIFF ('2010-11-30 23:59:59', '2010-12-31'); -> -31

+1

Rok nie ma znaczenia dla porównania. Nie będziesz pobierać żadnych wierszy, dopóki nie zignorujesz roku. –

+0

Otrzymuję 'NULL's podczas wykonywania' SELECT DATEDIFF (CURTIME(), data urodzenia) FROM rocznic' ...? – user151841

+0

Jaki jest typ "daty urodzenia", zakładam, że ma prawo do korzystania z DateTime? – alejandrobog

0

Można rzeczywiście użyć indeksu, jeśli urodziny przechowywane są oddzielnie od daty urodzin. Kolumna urodzinowa będzie miała rok i miesiąc, ale wszystkie będą miały ten sam rok (na przykład 2000).

Potem robisz zapytanie tak:

SELECT birthdate 
FROM Anniversaries 
WHERE birthday >= DATE_ADD(MAKEDATE(2000, DAYOFYEAR(CURDATE())), INTERVAL 10 DAYS) 

Chociaż ekspresja nie jest dokładnie tak, jak trzeba, liczba 10 jest nadal po prawej stronie wyrażenia. Zastosowanie funkcji po prawej stronie wyrażenia i zachowanie tylko kolumny po lewej stronie wyrażenia pozwala MySQL używać indeksu na kolumnie urodzin (zakładając, że masz indeks).

0

podstawie alejandrobog za odpowiedź:

SELECT birthdate FROM Anniversaries 
WHERE DATEDIFF(MAKEDATE(YEAR(CURRENT_DATE()), DAYOFYEAR(birthdate), CURRENT_DATE()) >= 0 
AND DATEDIFF(MAKEDATE(YEAR(CURRENT_DATE()), DAYOFYEAR(birthdate), CURRENT_DATE()) <= 10 
+0

Nie sądzę, że właśnie tego chce OP. Daje to urodziny, które już się wydarzyły w tym roku, a nie urodziny w ciągu najbliższych 10 dni. –

+0

Muszę unikać tych podchwytów SQL. Wybierz * wiersz i wykonaj obliczenia w kodzie. –

0

MAKEDATE() tworzy datę z roku i dnia roku, więc można go używać, aby dostać stare lata z obrazka:

SELECT 
    anniversary 
    , MAKEDATE(YEAR(NOW()), DAYOFYEAR(anniversary)) AS thisyear 
FROM Anniversaries; 

+-------------+-------------+ 
| anniversary | thisyear | 
+-------------+-------------+ 
| 1978-07-29 | 2010-07-29 | 
| 1959-04-17 | 2010-04-17 | 
+-------------+-------------+ 

Następnie można użyć DATEDIFF() obliczyć dni do (lub od), a następnie:

SELECT 
    anniversary 
    , MAKEDATE(YEAR(NOW()), DAYOFYEAR(anniversary)) AS thisyear 
    , DATEDIFF(MAKEDATE(YEAR(NOW()),DAYOFYEAR(anniversary)), NOW()) as days 
FROM Anniversaries; 

+-------------+-------------+------+ 
| anniversary | thisyear | days | 
+-------------+-------------+------+ 
| 1978-07-29 | 2010-07-29 | 70 | 
| 1959-04-17 | 2010-04-17 | -33 | 
+-------------+-------------+------+ 

Zakładając, że TERAZ() jest 5/20.


Edit więc powyższe nie działa ponad rok dachowania. Jednym z rozwiązań jest dodanie kolejnego obliczenia, w którym rocznica przypada na następny rok. Tutaj mam zakodowane data „2010-12-31”, i użył OR w HAVING klauzuli aby filtrować według dni pasujących zarówno w tym roku lub następnym:

SELECT birth_date 
, MAKEDATE(YEAR('2010-12-31'),DAYOFYEAR(birth_date)) as anniversary 
, DATEDIFF(MAKEDATE(YEAR('2010-12-31'),DAYOFYEAR(birth_date)), '2010-12-31') as days 
, MAKEDATE(YEAR(NOW())+ 1,DAYOFYEAR(birth_date)) as next_anniversary 
, DATEDIFF(MAKEDATE(YEAR(NOW())+ 1,DAYOFYEAR(birth_date)), '2010-12-31') as next_days 
FROM Anniversaries 
HAVING ((days <= 25 AND days > 0) OR next_days <= 25); 

+------------+-------------+------+------------------+-----------+ 
| birth_date | anniversary | days | next_anniversary | next_days | 
+------------+-------------+------+------------------+-----------+ 
| 2010-01-23 | 2010-01-23 | -342 | 2011-01-23  |  23 | 
| 1975-01-11 | 2010-01-11 | -354 | 2011-01-11  |  11 | 
+------------+-------------+------+------------------+-----------+ 
2 rows in set (0.00 sec) 
+0

Zobacz, jak działa twoje zapytanie, gdy TERAZ() = 2009-12-31 –

+0

Cóż, nie mogę cofnąć się w czasie, aby go przetestować, teraz mogę? : D – user151841

1

Co chcemy osiągnąć jest lista jubileuszy występujących w ostatnim przedziale czasowym. Aby osiągnąć ten cel i uwzględnić tryb rolowania, musimy wygenerować listę wszystkich rocznic obejmujących zakres dat. Rozwiązanie, którego używam, generuje zapisy rocznicowe z zeszłego roku, w bieżącym i przyszłym roku i generuje tylko te rekordy, które mieszczą się w podanym zakresie dat. Zapytanie unia wygląda następująco: pierwsza przyrostach komunikat wybierz

SELECT Date_Add(birth_date,INTERVAL (Year(now())-Year(birth_date)-1) YEAR) as dt, 
FROM Anniversaries 
WHERE Date_Add(birth_date,INTERVAL (Year(now())-Year(Date)-1) YEAR) 
BETWEEN Date_sub(now(), INTERVAL 7 Day) AND Date_add(now(), INTERVAL 21 Day) 
UNION 
SELECT Date_Add(birth_date,INTERVAL (Year(now())-Year(birth_date)) YEAR) as dt, 
FROM Anniversaries 
WHERE Date_Add(birth_date,INTERVAL (Year(now())-Year(Date)) YEAR) 
BETWEEN Date_sub(now(), INTERVAL 7 Day) AND Date_add(now(), INTERVAL 21 Day) 
UNION 
SELECT Date_Add(birth_date,INTERVAL (Year(now())-Year(birth_date)+1) YEAR) as dt, 
FROM Anniversaries 
WHERE Date_Add(birth_date,INTERVAL (Year(now())-Year(Date)+1) YEAR) 
BETWEEN Date_sub(now(), INTERVAL 7 Day) AND Date_add(now(), INTERVAL 21 Day) 
ORDER BY dt 

wszystkie daty do ubiegłego roku i wybiera wszystkie elementy, które mieszczą się w przedziale czasowym przewidzianym Kolejne Przyrosty rachunku Zaznacz wszystkie daty, aby w bieżącym roku oraz wybiera wszystkie elementy, które mieszczą się w podanym zakresie dat Ostatnia instrukcja SELECT zwiększa wszystkie daty do następnego roku i wybiera wszystkie pozycje, które mieszczą się w podanym zakresie dat Ostatecznie wszystkie są uporządkowane według daty.

Masz to. To niezawodnie wybierze rocznice w danym okresie. Jeśli wymagany jest dłuższy okres, może być konieczne wygenerowanie dodatkowych instrukcji SELECT i połączenie ich z UNION. Zmieniłem kod, aby pasował do powyższego przykładu, ale nie testowałem go od czasu modyfikacji.

0
 SELECT firstName, lastName, DOB, if((
    datediff(date(concat(year(current_date) , '-', month(DOB) , '-', day(DOB))) , current_date)) >=0, (
    datediff(date(concat(year(current_date) , '-', month(DOB) , '-', day(DOB))) , current_date)), 
    datediff(date(concat(year(current_date) +1, '-', month(DOB) , '-', day(DOB))) , current_date) 
)DAYSTOBDAY 
    FROM birthinfo 
    WHERE (
    if((
    datediff(date(concat(year(current_date) , '-', month(DOB) , '-', day(DOB))) , current_date)) >=0, (
    datediff(date(concat(year(current_date) , '-', month(DOB) , '-', day(DOB))) , current_date)), 
    datediff(date(concat(year(current_date) +1, '-', month(DOB) , '-', day(DOB))) , current_date) 
    ) 
    ) 
    BETWEEN 0 
    AND 31 

    firstName lastName DOB    DAYSTOBDAY 
    Michelle  Smith  1979-04-16   17 
    Kirsten  Dunst  1982-04-30   31 

Jest tak daleki od bycia "miłą" odpowiedzią, że to nie jest śmieszne, ale z mojej głowy to działa. osiągnąć to, co chcesz. Stworzyłem tabelę z imieniem, nazwiskiem i DOB, można go oczywiście po prostu zastąpić.

+0

Również ... Używanie MAKEDATE (ROK (current_date), DAYOFYEAR (dob)) zamiast concat na wyciągu daty nie działa, gdy wliczasz lata przestępne. Powyższe stwierdzenie uwzględnia lata przestępne itd., Jak sądzę. –

Powiązane problemy