Гость
Целевая тема:
Создать новую тему:
Автор:
Форумы / Firebird, InterBase [игнор отключен] [закрыт для гостей] / История значений - покритикуйте решение / 13 сообщений из 13, страница 1 из 1
02.09.2014, 16:03
    #38735146
Kirill Razuvaev
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
История значений - покритикуйте решение
Здравствуйте!
Ранее изменения в таблицах учетной системы хранились в единой таблице изменений в формате "ИмяТаблицы-ИмяПоля-СтароеЗначение-НовоеЗначение-Автор-ОтметкаВремени". И, в общем-то, все было хорошо до тех пор, пока не понадобилось иметь возможность посмотреть "слепок" данных на любую дату.
Дано - базовая таблица вида (к примеру - справочник):
Код: sql
1.
2.
3.
4.
5.
6.
7.
create table USERS(ID integer not null primary key,
LOGIN varchar(30) unique,
FULL_NAME varchar(255) unique,  // для упрощения
CREATED_AT timestamp,
CREATED_BY varchar(30),
UPDATED_AT timestamp,
UPDATED_BY varchar(30));

Что хочется получить:
1. Возможность просмотра состояния БД на любую дату.
2. Минимальные изменения в структуре БД, возможность автоматизации процесса (скрипты и т,д.), сохранение бизнес-логики.
Предлагаемое решение:
Дату для "слепка" задать через контекстную переменную.
Исходную таблицу модифицировать:
Код: sql
1.
2.
3.
4.
alter table USERS
add is_deleted d_boolean not null default 0,
add deleted_at timestamp,
add deleted_by varchar(30);

Плюс создать для нее новый индекс по IS_DELETED и PK. Создать таблицу истории со структурой, аналогичной базовой таблице, но без вспомогательных полей, unique-ограничений:
Код: sql
1.
2.
3.
4.
5.
6.
create table USERS_H(ID integer not null primary key,
TS timestamp not null,
AUTHOR varchar(30),
LOGIN varchar(30),
FULL_NAME varchar(255),
IS_DELETED d_boolean);

Для эффективного поиска (select first 1 * from... where TS<:DDATE order by TS desc) дополняем ее desc-индкесом по ID и TS.
Значения из базовой таблицы в таблицу истории переносим в триггере AIU базовой таблицы.
Для выборки данных из таблицы создаем изменяемый просмотр, на который переключаем все обращения к базовой таблице:
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
CREATE OR ALTER VIEW V_USERS(
    ID,
    LOGIN,
    FULL_NAME,
    CREATED_AT,
    CREATED_BY,
    UPDATED_AT,
    UPDATED_BY)
AS
select
    fs.id,
    case when rdb$get_context('USER_SESSION','BASE_DATE_SET')<FS.updated_at then FSH.login else FS.login end,
    case when rdb$get_context('USER_SESSION','BASE_DATE_SET')<FS.updated_at then FSH.full_name else FS.full_name end,
    FS.created_at,
    FS.created_by,
    case when rdb$get_context('USER_SESSION','BASE_DATE_SET')<FS.updated_at then FSH.ts else FS.updated_at end,
    case when rdb$get_context('USER_SESSION','BASE_DATE_SET')<FS.updated_at then FSH.author else FS.updated_by end
from USERS FS
left join USERS_H FSH on FS.id=FSH.id and (FS.updated_at>rdb$get_context('USER_SESSION','BASE_DATE_SET')) and
(select first 1 FSH1.ts from i_fin_sources_h FSH1
  where FSH1.id=FS.id and (rdb$get_context('USER_SESSION','BASE_DATE_SET')>=FSH1.ts)
  order by FSH1.ts desc)=FSH.ts
where FS.is_deleted=0
;

Судя по анализу производителтьности, запрос получился довольно удачным и для варианта с заданной "датой слепка" и при не заданной дате. Таким образом, пользователю видны только те значения из базовой таблицы, которые на момент "слепка" не были помечены как удаленные. При удалении данных по команде пользователя проставляется метка удаления. Теоретически можно вообще избавиться от дублирования текущих значений в базовой таблице, но, во-первых, это может негативно сказаться на производительности, а во-вторых - начинаются проблемы с ограничениями бизнес-логики (unique и прочее).

Может, я чего не учел? :-0
...
Рейтинг: 0 / 0
02.09.2014, 16:28
    #38735180
wadman
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
История значений - покритикуйте решение
В спойлеры не заглядывал. У меня историчность делается на трех дополниительных полях таблиц с датами:
begin_date not null, end_date null, sp_end_date not null (последний заполняется триггером исходя из end_date).
Пользователь не нужен пока что. Удалить нельзя, можно только выставить end_date и добавить новую строку с begin_date = (prev)end_date+1.
...
Рейтинг: 0 / 0
02.09.2014, 16:54
    #38735213
Kirill Razuvaev
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
История значений - покритикуйте решение
wadmanbegin_date not null, end_date null,А какой смысл в диапазонах, если по смыслу одно значение должно сменять другое? Достаточно же по идее только даты вступления в силу значения. Или за счет этого Вы разрывы (отсутствие данных) в промежутке времени отслуживаете?
...
Рейтинг: 0 / 0
02.09.2014, 16:55
    #38735215
wadman
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
История значений - покритикуйте решение
Kirill RazuvaevА какой смысл в диапазонах, если по смыслу одно значение должно сменять другое?
Для индекса по sp_end_date.
...
Рейтинг: 0 / 0
02.09.2014, 16:58
    #38735220
Kirill Razuvaev
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
История значений - покритикуйте решение
wadmanKirill RazuvaevА какой смысл в диапазонах, если по смыслу одно значение должно сменять другое?
Для индекса по sp_end_date.А в чем разница между двумя последними полями?
...
Рейтинг: 0 / 0
02.09.2014, 17:00
    #38735222
wadman
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
История значений - покритикуйте решение
Kirill Razuvaevwadmanпропущено...

Для индекса по sp_end_date.А в чем разница между двумя последними полями?
wadmanend_date null, sp_end_date not null (последний заполняется триггером исходя из end_date)
...
Рейтинг: 0 / 0
02.09.2014, 17:03
    #38735225
wadman
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
История значений - покритикуйте решение
Честно говоря я и не помню уже деталей и преимуществ этой схемы (три даты вместо одной). Придумал не я, но в свое время погонял её, результатом остался доволен и теперь безусловно её применяю. :)
...
Рейтинг: 0 / 0
02.09.2014, 20:03
    #38735398
Гаджимурадов Рустам
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
История значений - покритикуйте решение
Нафига нужен sp_end_date я тоже не понял,
а две даты вместо одной удобны тем, что
позволяют выбрать "конец" сразу, одним
запросом, без доп.запроса (ну или без EB).
Posted via ActualForum NNTP Server 1.5
...
Рейтинг: 0 / 0
03.09.2014, 09:17
    #38735712
wadman
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
История значений - покритикуйте решение
Гаджимурадов РустамНафига нужен sp_end_date я тоже не понял
Вот так делается:
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
CREATE TABLE TEST (
    BEGIN_DATE   DATENOTNULL DEFAULT current_date /* DATENOTNULL = DATE NOT NULL */,
    END_DATE     DATENULL /* DATENULL = DATE */,
    SP_END_DATE  DATENOTNULL /* DATENOTNULL = DATE NOT NULL */,
)

CREATE OR ALTER TRIGGER TEST_BU9 FOR TEST
ACTIVE BEFORE INSERT OR UPDATE POSITION 9
as
begin
  new.sp_end_date = coalesce(new.end_date, cast('01.01.3000' as date));
end

CREATE INDEX TEST_IDX_END_DATE ON TEST (SP_END_DATE);



Гаджимурадов Рустама две даты вместо одной удобны тем, что
позволяют выбрать "конец" сразу, одним
запросом, без доп.запроса (ну или без EB).
Верно, без ограничения выборки, если брать до определенной даты и сразу одной строкой. Экономия...
...
Рейтинг: 0 / 0
03.09.2014, 10:13
    #38735776
Гаджимурадов Рустам
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
История значений - покритикуйте решение
wadman> Вот так делается

Я не про триггер говорил, он итак
очевиден, а про два идентичных поля.

wadman> Верно, без ограничения выборки, если брать до определенной
wadman> даты и сразу одной строкой. Экономия...

Нет там никакой экономии и пр., любые выборки -
активная, активная на дату, до даты, последняя и
даже предпоследняя, предыдущая и пр. - можно
получить и без второго поля. А само второе поле
нужно именно для *информационных* целей, а
не для фильтра - чтобы когда понадобится не
лезть в таблицу второй раз и не выбирать сразу
2 строки - эдакая мини-денормализация.
Posted via ActualForum NNTP Server 1.5
...
Рейтинг: 0 / 0
03.09.2014, 10:27
    #38735802
wadman
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
История значений - покритикуйте решение
Гаджимурадов РустамЯ не про триггер говорил, он итак
очевиден, а про два идентичных поля.
Уже не помню "очевидных преимуществ", которые поведали об этой схеме. end_date null зачем-то нужен был той системе, где такая схема использовалась, т.к. внутрях используется исключительно sp_end_date и иногда begin_date.

P.S. Не показывать-же пользователю дату 01.01.3000
...
Рейтинг: 0 / 0
03.09.2014, 10:28
    #38735807
wadman
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
История значений - покритикуйте решение
wadmanт.к. у меня внутрях
...
Рейтинг: 0 / 0
03.09.2014, 13:58
    #38736179
Гаджимурадов Рустам
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
История значений - покритикуйте решение
wadman> P.S. Не показывать-же пользователю дату 01.01.3000

Это, конечно, от конкретного бизнес-случая зависит,
но пользователю обычно вообще конечную дату не
нужно показывать, а чаще всего - вообще только 1
запись - активная "на сегодня" или "на дату".
Posted via ActualForum NNTP Server 1.5
...
Рейтинг: 0 / 0
Форумы / Firebird, InterBase [игнор отключен] [закрыт для гостей] / История значений - покритикуйте решение / 13 сообщений из 13, страница 1 из 1
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]