Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > MySQL > операция с полем типа "дата"


Автор: Aazmandius 6.9.2006, 18:28
Помогите пожалуйста с запросом:
есть поле типа дата, там хранятся дни рождения людей, диапазон дат - от 1900 до наших дней, необходимо выбрать поля, день в которых находится в пределах плюс-минус N дней от текущего (месяц учитывается), невзирая на год.(То есть найти людей, у которых день рождения находится в этих пределах на текущий момент). Можно ли это проделать средствами MySQL без сторонних извращений со стороны приложения одним запросом? smile 
Заранее спасибо!

Автор: muzer 6.9.2006, 20:07
Из того что придумалось:

1.У данного способа есть погрешность плюс минус один день из-за високосных годов.
Код

SELECT * FROM table WHERE DAYOFYEAR(dt) BETWEEN DAYOFYEAR(NOW() - INTERVAL n DAY) AND DAYOFYEAR(NOW() + INTERVAL n DAY)
где dt - колонка даты, n - кол-во плюс-минус дней



2.
Код

SELECT * FROM table WHERE SUBSTRING(dt,5) BETWEEN SUBSTRING(NOW()-INTERVAL n DAY,5) AND SUBSTRING(NOW() + INTERVAL n DAY,5)


Оба варианта не быстрые, т.к. невозможно использовать индекс.

Автор: Aazmandius 7.9.2006, 19:13
Спасибо =)

Автор: muzer 7.9.2006, 20:12
Эмм..фигню я сказал какую-то..
Проблемы же будут в новый год, точнее всегда, когда даты начала и конца периода будут стоять в разных годах.

Придумал запрос, который решает эту проблему. Но будь я на вашем месте - использовал бы другой способ.

Идея такая: если конец и начало периода стоят в разных годах, то дни рождения первого полугодия ставим выше дней рождений второго полугодия путём прибавления к датам первого полугодия числа 20000, а к датам второго полугодия числа 10000 (чтобы сохранить порядок чисел). К началу периода прибавляем 10000 с той же целью. К концу периода прибавляем 20000, т.к. он должен быть выше начала периода.
Проверял, работает smile

Код

SELECT 
    *
FROM 
    TestTime
WHERE
    SUBSTRING(dt+0,n)
    +IF(
            SUBSTRING((NOW() + INTERVAL n DAY)+0,1,4)>SUBSTRING((NOW()-INTERVAL n DAY)+0,1,4)
        AND 
            SUBSTRING(dt+0,5,2) <= 6
    ,2,1)*10000 
    BETWEEN 
        SUBSTRING((NOW()-INTERVAL n DAY)+0,5)+10000 
    AND 
        SUBSTRING((NOW() + INTERVAL n DAY)+0,5)
        +IF(
            SUBSTRING((NOW() + INTERVAL n DAY)+0,1,4)>SUBSTRING((NOW()-INTERVAL n DAY)+0,1,4)
        ,2,1)*10000;


Но это всё  было проделано мной из спортивного интереса.
Правильное решение на мой взгляд - сгенерить массив дат (месяц-день) за которые надо вывести дни рождения, а потом выполнить запрос 
Код

SELECT * FROM table WHERE SUBSTRING(dt+0,5) IN (наш_массив_через_запятую)

Автор: Aazmandius 8.9.2006, 15:25
Есть еще один способ, недавно придуман =) Суть в том, что мы заменяем все годы, попавшие в выборку, на текущий, и сравниваем с текущей датой, получим как раз искомое значение.
Код

select * from person where CONCAT_WS('-', 'curYear', lpad(month(birthdate),2, '0'), lpad(dayofmonth(birthdate), 2, '0')) between 'begDate' and 'endDate';

где curYear, begDate, endDate - текущий год, нчало и конец диапазона соответственно.
Просто и быстро =)

Автор: muzer 8.9.2006, 16:35
Да нет же..
Та же проблема, если у нас начало период 2005-12-15 а конец 2006-01-15
Ваш запрос работать не будет.

В этом вся и проблема smile а так-то конечно, это тоже самое, что мой второй вариант из первого поста, только вместо отрезания года - добавляете год smile  Но операций нужно больше, ведь нужно ещё вычислить этот год, а потом ещё к каждой строке его приконкатить, это более сложная операция, чем взять часть готовой строки.

Автор: Aazmandius 8.9.2006, 18:41
Мда, точно =) как то сразу не подумал...

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)