Справочник с интервалом дат
Справочник с интервалом дат
Задача:
Нужно сделать справочник процентных ставок которые действуют с какой-то даты и по какую-то дату.
В результате надо получить процентную ставку действовавшую на определенную дату.
Предполагаемое решение:
Завести два поля типа дата в которых будут занесены даты начала и окончания действия это % ставки. Затем BETWEEN -ом найти нужноую строку.
Проблема:
Как быть с % ставкой действующей на текущий момент, у нее нет даты окончания. Как вариант можно занести в это поле заоблачную дату (типа 3000 год). Но тогда при вводе новой ставки надо у предыдущих изменять эту дату на другую - реальную, а для новой опять ставить заоблачную...
Вопрос:
Может есть какое более изящное решение?
Нужно сделать справочник процентных ставок которые действуют с какой-то даты и по какую-то дату.
В результате надо получить процентную ставку действовавшую на определенную дату.
Предполагаемое решение:
Завести два поля типа дата в которых будут занесены даты начала и окончания действия это % ставки. Затем BETWEEN -ом найти нужноую строку.
Проблема:
Как быть с % ставкой действующей на текущий момент, у нее нет даты окончания. Как вариант можно занести в это поле заоблачную дату (типа 3000 год). Но тогда при вводе новой ставки надо у предыдущих изменять эту дату на другую - реальную, а для новой опять ставить заоблачную...
Вопрос:
Может есть какое более изящное решение?
Хранить дату начала действия ставки,
а потом выбирать ближайшую к искомой вниз
типа этого:
а потом выбирать ближайшую к искомой вниз
типа этого:
Код: Выделить всё
select * from stavki
where startdate<=:искомой
order by startdate desc
rows 1
Т.е. для FB будет типа следующего:
Правильно?
Код: Выделить всё
select frist 1 * from stavki
where startdate<=:искомой
order by startdate desc
Вариант без диапазона медленнее, на существенном объёме данных будет тормозить, и не предусматривает варианта, когда в какой-то период вообще нет процентной ставки.
Я бы диапазоном делал. А что апдейтить надо - ничего страшного не вижу.
У меня это выглядит примерно так (изменения вношу процедурой):
Особенность такого подхода - можно сколько угодно раз в день вставлять "текущие" значения процедурой - количество диапазонов не изменится, будет запомнено последнее решение.
Я бы диапазоном делал. А что апдейтить надо - ничего страшного не вижу.
У меня это выглядит примерно так (изменения вношу процедурой):
Код: Выделить всё
MaxDate = '01.01.3000';
/* находим ставку, если уже есть в архиве */
ArchValue=0; ArchDateBeg='01.01.1900'; RecID=null;
select RecID, aValue, DateBeg, DateEnd
from Archive
where ValId = :ValId and :CheckDate between DateBeg and DateEnd
into RecID, ArchValue, ArchDateBeg, ArchDateEnd;
/* если значение отличается от существующей записи */
if (ArchiveValue != aValue) then begin
/* если дата начала совпадает с найденным диапазоном, то меняем существующую запись */
if (ArchDateBeg = CheckDate) then
update Archive
set aValue = :NewValue, DateBeg = :CheckDate, DateEnd = :MaxDate
where RecID = :RecID and
aValue != :NewValue and DateBeg != :CheckDate and DateEnd != :MaxDate;
else
/* иначе проверяем изменение значения */
if (ArchDateBeg < CheckDate) then begin
/* закрываем предыдущий диапазон */
if (RecID is not null) then
update Archive set DateEnd = :CheckDate-1 where RecID = :RecID;
/* вставляем новый */
insert into Archive (ValId, aValue, DateBeg, DateEnd)
values (:ValId, :NewValue, :CheckDate, :MaxDate);
end
end
Re: Справочник с интервалом дат
Хранить только дату начала.AnViSe писал(а): Может есть какое более изящное решение?
WildSery писал(а):Вариант без диапазона медленнее, на существенном объёме данных будет тормозить
Это не есть факт, мистер Дюк Нулл - секретное оружие пролетарьятаWildSery писал(а): и не предусматривает варианта, когда в какой-то период вообще нет процентной ставки.
Обеспечения условия непересекаемости диапазонов в конкурентной среде - в общем случае задача нерешаемая на вразумительных уровнях изоляции транзакций.WildSery писал(а): Я бы диапазоном делал. А что апдейтить надо - ничего страшного не вижу.
А особеннось подхода с хранением только начала диапазона - можно сколько угодно раз в день вставлять "текущие" значения простым инсёртомWildSery писал(а): Особенность такого подхода - можно сколько угодно раз в день вставлять "текущие" значения процедурой
-
- Сообщения: 44
- Зарегистрирован: 14 мар 2008, 21:01
Можно так
И выбирать
только сколько раз оно по справочнику пробежит чтобы выбрать одно зачете смотрите сами
Код: Выделить всё
CREATE TABLE NEW_TABLE (
ADATE TIMESTAMP NOT NULL,
VAL INTEGER NOT NULL
);
Код: Выделить всё
WITH RECURSIVE
DD (SDATE, EDATE, VAL) AS
(SELECT FIRST 1 B.ADATE, (SELECT FIRST 1 DATEADD(SECOND, -1, D.ADATE) FROM NEW_TABLE D WHERE D.ADATE > B.ADATE), VAL
FROM NEW_TABLE B
UNION ALL
SELECT FIRST 1 A.ADATE, (SELECT FIRST 1 DATEADD(SECOND, -1, D.ADATE) FROM NEW_TABLE D WHERE D.ADATE > A.ADATE), A.VAL
FROM NEW_TABLE A, DD
WHERE A.ADATE > DD.SDATE
)
SELECT * FROM DD
WHERE :ADATE BETWEEN SDATE AND COALESCE(EDATE, '01.01.5000')
-
- Сообщения: 44
- Зарегистрирован: 14 мар 2008, 21:01
Я не претендую на идеальную прямоту рук, но в моих тестах заметно быстрее было диапазоном.Merlin писал(а):
Ты меня засмущал своим авторитетом, пойду ещё потестирую.
В поле значения? Нда, действительно вариант. (Не люблю я эти нуллы...)Merlin писал(а):Нулл - секретное оружие пролетарьята
Задача задаче рознь. Я наивно считаю, что выставление почти справочных величин, типа ставок чего-нибудь, или курсов валют, или торговых цен, дело сугубо монопольное.Merlin писал(а):Обеспечения условия непересекаемости диапазонов в конкурентной среде - в общем случае задача нерешаемая на вразумительных уровнях изоляции транзакций.
...А лучше опять-таки проверять существование нужной записи, потому как "select value from" в конкретную дату писать всегда удобнее, чем "select first 1 value from" Кроме того, это ещё и инстрУмент, чтобы донести до разработчика что с БД что-то не так, если должно быть ограничение на хранение одного значения в конкретную дату, экстремальным методом "multiple rows..."Merlin писал(а):А особеннось подхода с хранением только начала диапазона - можно сколько угодно раз в день вставлять "текущие" значения простым инсёртом
У меня так не будетarmagedon2007 писал(а):А что будет в твоем примере...
Нужна дополнительный алгоритм для внесения таких данных. Типа, чего вообще хотим получить такой вставкой.
И, кстати, не факт, что такая "вставка задним числом" в структуру хранения "датой начала" будет соответствовать ожиданиям. Не во всех задачах требуется тупо делить диапазон на два.
И ты, Брутто, про эстетикуWildSery писал(а):лучше опять-таки проверять существование нужной записи, потому как "select value from" в конкретную дату писать всегда удобнее, чем "select first 1 value from"
Ты тут про курсы вспоминал... А я чо-то про Кириенковский дефолт вспомнилWildSery писал(а): если должно быть ограничение на хранение одного значения в конкретную дату
Именно так и делаю. А объединяю данные в процедуре с for select по данным и курсором по процентным ставкам за указанную дату.mdfv писал(а):Хранить дату начала действия ставки,
а потом выбирать ближайшую к искомой вниз
Примерно так:
Код: Выделить всё
WHILE (DATE_CURRENT <= DATE_END) DO
BEGIN
IF (DATE_CURRENT > DATE_TMP AND ROW_COUNT > 0) THEN
BEGIN
REST_CURRENT = COALESCE(REST_CURRENT, 0) + REST_TMP;
IF (REST_CURRENT IS NOT NULL AND REST IS NOT NULL AND REST_CURRENT <> REST) THEN
INTERVAL_ID = INTERVAL_ID + 1;
FETCH SUM_OPERATIONS INTO :DATE_TMP, :REST_TMP;
END
REST = REST_CURRENT;
"DATE" = DATE_CURRENT;
IF (REST IS NOT NULL) THEN
SUSPEND;
DATE_CURRENT = DATE_CURRENT + 1;
END