[SQL] Извращённая задачка

artimon

Дано:
Postgresql.
Таблица с днями рождения. ДР хранятся в виде timestamp.
Надо получить список в следующем порядке:
1. сначала те люди, у которых ДР в ближайшие n дней (т.е. сегодня, завтра, ... сегодня + n) отсортированные по удалённости от сегодняшнего дня.
2. люди, у которых ДР уже был отсортированные по удалённости от сегодня.
Короче, муть какая-то. Вот пример того, что должно получится (для n = 5, сегодня = 15.09):
15.09 // сегодня
17.09
18.09
20.09 // сегодня + 5
13.09 // далее прошедшие ДР в обратном порядке с перескакиванием через год.
21.08
03.03
30.11
12.10
21.09
В принципе, не обязательно это делать на чистом SQL. Можно программировать на PHP/Perl/ещё чём-то, что я смогу понять и переписать на PHP.
Хочется найти какое-то красивое решение устойчивое к високосным годам.

laki

13.09 // далее прошедшие ДР в обратном порядке с перескакиванием через год.
поподробнее чето не в курю.

Dasar


DateTime UpdateDate(DateTime date)
{
DateTime d = date;
d.Year = DateTime.Now.Year; //грубо выравниваем год
if d - DateTime.Now).TotalDays > n) //хм. перелетели
d = d.AddYears(-1);
if d - DateTime.Now).TotalDays < n-365) //хм. недолетели
d = d.AddYears(1);
return d;
}
int GetDays(DateTime date)
{
return UpdateDate(date) - DateTime.Now).TotalDays;
}

> сначала те люди, у которых ДР в ближайшие n дней

select d from days
where GetDays(d) >=0 && GetDays(d) <= n
order by GetDays(d)

> люди, у которых ДР уже был отсортированные по удалённости от сегодня

select d from days
where GetDays(d) < 0
order by GetDays(d) desc

ps
при отладке пошаманить с меньше-равно vs меньше, больше-равно vs больше и 365 vs 366 (текущий год - високосный/не високосный).

laki

во решение первой на постгресе

create temp table t1(
d timestamp,
name varchar(255)
);
insert into t1 values ('1983-9-15', 'Vasya');
insert into t1 values ('1983-9-16', 'Petya');
insert into t1 values ('1983-9-17', 'Shurik');
insert into t1 values ('1983-9-18', 'Fekla');
CREATE OR REPLACE FUNCTION f1(timestamp)
RETURNS timestamp AS '
DECLARE
result TEXT;
use_rec RECORD;
BEGIN
select into use_rec (select extract(year from now || \'-\' || extract(month from $1) || \'-\'
|| extract(day from $1 as d;
result := use_rec.d;
RETURN result;
END;
' LANGUAGE plpgsql;
select * from t1 where select f1(t1.d + '1 day' interval '0') overlaps (now interval '2 day') order by d;

laki

select *, (select f1(t1.d + '1 day' as g from t1 where select f1(t1.d + '1 day' interval '0') overlaps (now interval '-1 year') order by g desc;
это ко второй только хз какая сортировка

Dasar

> select into use_rec (select extract(year from now || \'-\' || extract(month from $1) || \'-\'
> || extract(day from $1 as d;
грубое выравнивание сделал, а более тонкое забыл.
посмотри, что будет:
если сегодня - 31.декабря,
а др - 1 января

laki

ага вижу и високосный не прокатит

eduard615

все вместе одним запросом и без pgsql

select date,
abs(sign(extract(doy from date)-extract(doy from now/n as c1,
(1-abs(sign(extract(doy from date)-extract(doy from now/n*(extract(doy from date)-extract(doy from now as c2,
sign(extract(doy from date)-extract(doy from now as c3
from ..
order by c1,c2,c3, date desc

вопчем, надо для каждой даты взять какой это день в году и далее несколько нескложных сортировок

Dasar

на первый взгляд та же самая проблема с:

если сегодня - 31.декабря,
а др - 1 января

artimon

Пока все в пролёте.
,
Что будет с выравниванием года, если кто-то умудрится родится 29 февраля?

На первый взгляд сглючит, если кто-то родился в високосном году после 29 февраля.
P.S. Замечания, высказанные 'ем тоже верны.

Dasar

> Что будет с выравниванием года, если кто-то умудрится родится 29 февраля?
так я тебе и говорю, отладь до товарного вида.
если человек родился 29 февраля, то при выравниии, если сейчас год не високосный - надо делать 28 февраля.
и посмотреть, в каких местах, должно быть 365, а где 366
ps
т.к. select-ы удобнее делать на sql-е, а функцию пересчета делать на нормальном языке,
то можешь при отладке через нормальный язык заполнять в таблице лишную колонку (например, результатом функции GetDays а select-ы строить уже с использованием этой вспомогательной колонки
далее, как отладишь, можно будет уже рисовать полностью sql-ный вариант.

laki

мое решение надо проапгрейдить до следующего.
intYears = (extract(year from now - extract(year from DR) - 1); - это ключевой момент.
ND := DR + interval intYears + ' years';
и делать overlaps now с ND
все просто.
тоже самое и спервой задачей только intYears = (extract(year from now - extract(year from DR
високосный год и всякую лабуду учтет постгрес
комментарии.
Оставить комментарий
Имя или ник:
Комментарий: