LINUX.ORG.RU

Выборка по диапазону дат

 ,


0

1

Есть одна нехорошая таблица с парой миллионов записей, в которой день-месяц-год заданы отдельными столбцами. Теперь ВНЕЗАПНО понадобилось делать выборку в диапазоне от и до. Помогите составить корректный sql-запрос для этого чуда (ну, кроме как сделать alter table, добавив поле с типом datetime, проапдейтить его и потом уже сделать обычный запрос). Вот пример запроса, который скинули мне:

SELECT * FROM stat 
WHERE (year>=2014 AND ( (month=9 and day>=29) OR (day>0 and month>9) )) 
AND (year<=2015 AND ( (month=3 and day<=27) OR (day>0 and month < 3) )) 
Я в sql ни бум-бум, якобы у разработчика на локальной машине всё работает, а на сервере нет. Но это какой-то бред.

★★★

Как минимум условие «day>0» выглядит странно, как оно может на что-то влиять, если нумерация дней начинается с 1. А так решение задачи зависит от диапазона дат, и могут получаться всякие сложные, еще более многоскобочные выражения.

amomymous ★★★ ()
Ответ на: комментарий от orm-i-auga

В том что не работает.

SELECT count(*) FROM stat 
WHERE (year>=2014 AND ( (month=9 and day>=29) OR (day>0 and month>9) )) 
AND (year<=2015 AND ( (month=3 and day<=27) OR (day>0 and month < 3) ))
+----------+
| count(*) |
+----------+
|        0 |
+----------+
1 row in set (0.00 sec)
Хотя при просмотре данных я вижу, что записи в диапазоне от 29.09.2014 до 27.03.2015 есть.

lizard ★★★ ()

условия на month получаются через AND: month >=9 AND month <=3, поэтому ничего не выбирается.

Хотя, если предположить, что «Времени Не существует», то всё хорошо.

GPFault ★★ ()

Три столбца в нормальную дату преобразуй прямо в запросе функцией makedate/str_to_date, да отбирай. Да, будет медленно, так кто же виноват, что через жопу таблицу сделали.

crutch_master ★★★★★ ()
Последнее исправление: crutch_master (всего исправлений: 2)
Ответ на: комментарий от lizard

Проще наверное преобразовать в нормальную дату и сделать обычный запрос с between.

orm-i-auga ★★★★★ ()

Будет что-то типа

Select * from star 
where str_to_date(concat(year,".",month,".",day),"%Y.%m.%d") 
between A and B

crutch_master ★★★★★ ()
Последнее исправление: crutch_master (всего исправлений: 1)
Ответ на: комментарий от GPFault
SELECT * FROM stat WHERE (YEAR>=2014 and (MONTH >= 9 or YEAR > 2014) AND (day >= 29 or MONTH > 9))
OR (YEAR>=2015 and (MONTH <= 3 or YEAR < 2015) AND (day <= 27 or MONTH < 3))
order by YEAR, MONTH, DAY;

Извращение, но теперь работает.

lizard ★★★ ()
Ответ на: комментарий от lizard

А он у тебя точно правильно даты отбирает? В таком условии накосячить как нечего делать. Хоть комментарий напиши, что тут делается..

crutch_master ★★★★★ ()
Последнее исправление: crutch_master (всего исправлений: 1)

на любой быдлоскриптоте набросай функцию, которая будет по трём числам определять «годность» даты, проверь её, а потом переведи в sql. А то у тебя в обоих примерах срань господня...

yyk ★★★★★ ()

Тут у тебя в запросе взаимоисключающие условия, поэтому ничего не выбирается.

Вариант, несколько извращенный:

SELECT id FROM ( SELECT id, year * interval '1 year' + month * interval '1 month' + day * interval '1 day' AS "date_int" FROM stat ) AS "src" 
WHERE date_int BETWEEN interval '2014 years 9 month 29 days' AND '2015 years 3 month 27 days';
grondek ()
Последнее исправление: grondek (всего исправлений: 2)
Ответ на: комментарий от quest

выбирай между двумя диапазонами unixtime

Ему же с точностью до секунды не нужно

Должно такое сработать:

d1 = day1 + 31*month1 + 365*year1;
d2 = day2 + 31*month2 + 365*year2;
select * from stat where
((day + 31*month + 365*year) > :d1) and
((day + 31*month + 365*year) < :d2)
anonymous ()
Ответ на: комментарий от crutch_master

Твой вариант не позволяет использовать индекс по полям (если он есть), так что вариант с лексикографическим сравнением в общем случае годнее.

anonymous ()

Вариант 1: Через временную таблицу, столбцом которой будет собранное из этих трёх столбцов значение даты. Далее выбираешь из этой временной таблицы, типа:

-- при необходимости добавляем ограничения, например по году, чтобы не выбирать изначально из всех 2-х миллионов записей
declare table @my_table(my_date varchar(8));
insert into @my_table
select day + '.' + 'month' + '.' + 'year' from stat;

-- Далее выбираешь из этой временной таблицы, не забыв сконвертить строку в дату
select * from my_table where convert(date, my_date , 102 ) between 'blabla' and 'blabla';
-- параметры CONVERT смотреть в справке, число зависит от формата, в котором будет дата

Вариант 2: Через Join-ы. Делаешь выборку из таблицы, джойня её с выборками из самой себя, опять же с учётом условий:

select * 
from stat
    left join (select id, mont from stat where month betwee '3' and  '9') month_t 
        on stat.id = month_t.id -- или какой там ключ...
    left join (select 'бла бла бла, тоже самое только для дней') day_t 
        on month_t.id = day_t.id 

where year between '2014' and '2015'

Наводку дал, дальше сам. А вообще, с такими вопросами, лучше бы тебе на sql.ru

P.S. Подсветка кода для SQL хреново работает, перед коментом надо ставить ';' иначе не затеняет его :(

xterro ★★★★★ ()
Ответ на: комментарий от anonymous

Ему же с точностью до секунды не нужно

В том же postgresql timestamp УЖЕ хранит время с точностью до миллисекунд. делаем функцию выборки, строим функциональный индекс и вуаля

quest ★★★★ ()
Ответ на: комментарий от anonymous

общем случае годнее.

Годнее добавить нормальное поле с датой и не извращаться. У ОПа или условие не правильно поставишь или напишешь так, что хрен прочтешь сам.
Я вообще не понимаю, почему ОПу сложно сделать update 2М строк.

crutch_master ★★★★★ ()
Последнее исправление: crutch_master (всего исправлений: 1)

В условии для года нужно такую же фишку, как и с месяцем сделать. То есть, для нижней границы, если год больше 2014, то на месяц и день уже не смотрим. Для верхней — если год меньше 2015, на день и месяц не смотрим.

SELECT count(*) FROM stat 
WHERE (year > 2014 or year=2014 AND (month > 9 or month=9 and day>=29))
AND (year < 2015 or year=2015 AND (month < 3 or month=3 and day<=27))
kvap ()
Последнее исправление: kvap (всего исправлений: 1)

Кстати, разработчик врёт.

kvap ()

ну, кроме как сделать alter table, добавив поле с типом datetime, проапдейтить его и потом уже сделать обычный запрос

Так и сделай. Зачем так извращенно хранить даты?

KillTheCat ★★★★★ ()
Ответ на: комментарий от anonymous

При переходе через год будет неправильно работать

Да, точно. Нужно коэффициент у года поправить, должно быть 12*31

2000*12*31 + 12*31 + 31 = 744403
2001*12*31 + 01*31 + 01 = 744404
d1 = day1 + 31*month1 + 12*31*year1;
d2 = day2 + 31*month2 + 12*31*year2;

И наверное изменить сравнение

select * from stat where
((day + 31*month + 12*31*year) >= :d1) and
((day + 31*month + 12*31*year) <= :d2)
anonymous ()
Ответ на: комментарий от anonymous

Хм, интересное решение, надо будет запомнить :)

xterro ★★★★★ ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.