2010-05-20 2 views
5

У меня есть таблица с датами юбилея. Я хочу запрос, который возвращает мне строки юбилеев, которые появятся в ближайшие 10 дней. Например:найти, если юбилей наступает в n дней в 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) 

Я хотел бы сохранить запрос в виде x <= 10, потому что я буду использовать это число 10 в других частях запроса, и если установить его в переменной, можно изменить он один раз повсюду, изменяя переменную и не переписывая запрос.

+1

Возможно, вы уже знаете это, но сохранение выражения в этом формате может заставить функцию с левой стороны выражения, что предотвратит использование MySQL индекса при рождении. –

+0

Имеет ли значение «односторонность»? Если это в форме '10 => x', это позволит использовать индекс? – user151841

+1

@ user151841, в идеале у вас будет только имя столбца в левой части выражения. В этом случае вы можете применить функцию к 10 для получения будущей даты и сделать что-то вроде этого 'WHERE birthdate> = DATE_FUNCTION (10)'. Если кто-то другой не победит меня, я посмотрю на конкретную функцию и дам вам ответ. –

ответ

6

Как уже говорилось, вам нужно игнорировать год в сравнении.Функция DAYOFYEAR() - это один из способов сделать это.

Вот быстрое решение с верхней части головы. Он вернет все дни рождения в ближайшие 10 дней, даже если это конец декабря, а день рождения - в следующем году.

Это НЕ обрабатывает високосные годы должным образом, поэтому он будет отключен на 1 день в дни рождения в начале марта, если этот год является високосным годом, и человек не родился в високосном году или наоборот. Високосные годы также приведут к тому, что в начале января дни рождения появятся в один выходной день в конце декабря. Если кто-то хочет, чтобы добавить поправку високосного года, не стесняйтесь :)

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

Попробуйте

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

Проверьте это для справки MySQL DATEDIFF

DATEDIFF (expr1, expr2)

DATEDIFF() возвращает expr1 - expr2 выражается как значение в днях с одной даты на Другие. expr1 и expr2 - это выражения даты или даты и времени. В расчетах используются только части даты.

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

Год не имеет значения для сравнения. Вы не собираетесь извлекать любые строки, если не будете игнорировать год. –

+0

Я получаю 'NULL' при выполнении' SELECT DATEDIFF (CURTIME(), дата рождения) FROM Anniversaries' ...? – user151841

+0

Какой тип «даты рождения», я предполагаю, что это DateTime? – alejandrobog

0

Фактически вы можете использовать индекс, если вы сохранили день рождения отдельно от даты рождения. У столбца дня рождения будет год и месяц, но все они будут иметь тот же год (например, 2000).

Тогда вы могли бы сделать запрос, как это:

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

Хотя это выражение не так, как вы хотели, число 10 все еще находится на правой стороне выражения. Применение функции в правой части выражения и сохранение только столбца в левой части выражения позволяет MySQL использовать индекс в столбце дня рождения (при условии, что у вас есть индекс).

0

На основании ответа alejandrobog в:

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

Я не думаю, что это то, чего хочет OP. Это дает дни рождения, которые уже произошли в этом году, а не дни рождения в ближайшие 10 дней. –

+0

Мне нужно избегать этих вопросов трюка SQL. Выберите * строку и выполните вычисления в коде. –

0

MAKEDATE() создает дату года и день года, так что я могу использовать его, чтобы получить старые года из картины:

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

Затем я могу использовать DATEDIFF() рассчитать дни до (или из):

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

Предполагая СЕЙЧАС() 5/20.


Редактировать поэтому выше не работает в течение года опрокидывания. Одно из решений - добавить еще один расчет, где юбилей - в следующем году. Здесь я жестко закодированы дата «2010-12-31», и использовал OR в пункте HAVING для фильтрации по дням, которые соответствуют либо в этом году или в следующем:

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

Посмотрите, как ваш запрос работает, когда NOW() = 2009-12-31 –

+0

Ну, я не могу вернуться назад вовремя, чтобы проверить его, теперь я могу? : D – user151841

1

То, что мы хотим достичь списка памятных дат, происходящих в недавнем диапазоне дат. Для достижения этого и для размещения опрокидывания нам необходимо создать список всех юбилеев, которые охватывают диапазон дат. Решение, которое я использовал, генерирует юбилейные записи за прошлый год, в этом году и в следующем году и выводит только те записи, которые попадают в данный диапазон дат. Запрос союз выглядит следующим образом:

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 

первом приращений оператора SELECT все даты в прошлом году, и выбирает любые предметы, которые попадают в диапазон дат при условии приращения Следующими оператора SELECT все даты в текущем году и выбирает любые элементы, попадающие в диапазон дат. Последний оператор SELECT увеличивает все даты до следующего года и выбирает любые элементы, попадающие в диапазон дат. Наконец, все упорядочено по дате.

У вас есть это. Это позволит надежно выбрать юбилейные даты в данный период. Если требуется более длительный период, может потребоваться генерация дополнительных операторов SELECT и объединение их с UNION. Я изменил свой код в соответствии с приведенным выше примером, но не протестировал его с момента его изменения.

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 

До сих пор это не «хороший» ответ, это не смешно, но с моей головы, которая работает. для достижения того, чего вы хотите. Я создал таблицу с именем, фамилией и DOB, вы можете просто заменить ее все вокруг.

+0

Также ... Использование MAKEDATE (YEAR (current_date), DAYOFYEAR (dob)) вместо concat в инструкции даты не работает, когда вы включаете високосные годы. Вышеприведенное заявление относится к високосным годам и т. Д. Я считаю. –

Смежные вопросы