Форум программистов «Весельчак У»
  *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

  • Рекомендуем проверить настройки временной зоны в вашем профиле (страница "Внешний вид форума", пункт "Часовой пояс:").
  • У нас больше нет рассылок. Если вам приходят письма от наших бывших рассылок mail.ru и subscribe.ru, то знайте, что это не мы рассылаем.
   Начало  
Наши сайты
Помощь Поиск Календарь Почта Войти Регистрация  
 
Страниц: [1]   Вниз
  Печать  
Автор Тема: Создание структуры реляционной БД с изменяемыми справочниками  (Прочитано 11060 раз)
0 Пользователей и 1 Гость смотрят эту тему.
RomCom
Опытный

ru
Offline Offline
Пол: Мужской

WWW
« : 16-04-2006 05:55 » 

Вопрос в следующем. Есть реляционная БД, какая в принципе не суть важно (на данный момент FireBird), и стандартная структура: справочники (СП), и одна (две) основных таблиц с данными (ОТД).
Как организовать структуру (логику) так чтобы при изменении (удалении) записей в справочниках данные накопленные в ОТД до изменения оставались корректными?
Поясню на примере базы работников предприятия и их работы. Очевидно что тут необходимо как минимум две таблицы: справочник работников (паспортные данные, должность, оклад...) - СП, данные о выполненной роботе - ОТД. Задача тривиальна если не копать глубже. Данные нужно хранить не один месяц, и даже год. За это время данные о работниках могут претерпеть значительные изменения, но это никак не должно повлиять на отчет за прошедший месяц или год.
Можно конечно в каждую запись ОТД заносить и все текущие данные из СП, но это слишком накладно в плане размера и усложнит выборку из базы.
В 1С этот вопрос решается с помощью "истории версий" (кажется так называется).
Как организовать такое? Объясните или ткните носом в линк для познания Улыбаюсь
Записан

R.O.M.C.O.M.: Robotic Operational Mathematics and Ceaseless Observation Machine
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #1 : 16-04-2006 09:49 » 

Справочники должны быть двухуровневыми, с историей.

В ОТД сохранятся внешние ключи на справочник пользователей. Вводятся справочники второго уровня: "история изменения оклада", "история смены паспортных данных" (хотя о целесообразности такой истории ещё нужно подумать) и т.д.

В истории хранится само значение и дата начала его действия. Дата начала следующего действия означает дату завершения предыдущего. Последняя запись содержит значение, дата окончания действия которого не определена.

Случай с удалением элемента справочника реализуется путём установки логического флага "удалён", а не удалением записи. В этом случае и в справочниках, и в ОТД сохранятся исторические данные.

В случае большой по размерам БД, с которой осуществляется интенсивная работа, разумнее разбить её на 2: оперативную и архивную. В оперативной (небольшой по размерам) хранятся текущие значения, эта БД принимает на себя основную нагрузку частых запросов на вставку, удаление и модификацию записей ОТД. В архивной (большой по размерам) хранятся удалённые значения, к этой БД обращаются редко, зато запросы к ней достаточно "тяжёлые". В случае такого разделения следует разделить и функционал приложения: выделить функции оперативной работы и функции работы с архивом.
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
RXL
Технический
Администратор

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #2 : 16-04-2006 11:16 » 

Поддерживаю Димку, но я бы немного по другому реализовал:

table OTD
otd_id PK
sp1_id

table SP1
sp1_id PK
sp2_id

table SP2
sp2_id PK
sp1_id KEY /* сообщает, кому принадлежит, да же если на строку не ссылаются */

Как текущее значение СП2, так и история проиндексирована и находится за один запрос. Идентификаторы, по моему, удобнее, чем дата.
Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #3 : 16-04-2006 11:26 » 

RXL, малость не понял замыкания таблиц друг на друга в таком абстрактном виде. Можно на примере сотрудника и его меняющегося оклада?
« Последнее редактирование: 16-04-2006 11:30 от dimka » Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
RXL
Технический
Администратор

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #4 : 16-04-2006 12:27 » 

dimka, я не описывал "сотрудника и зарплату".

См.: история (включая текущее значение) хранится в СП2, в СП1 - статическая часть, история изменений которой не нужна.
Одна запись СП1 ссылается на одну строку СП2 через sp2_id - это текущее значение.
Выбрать всю историю изменений можно из СП2 по sp1_id.

Если о сотрудниках:
СП1 - сотрудники
СП2 - оклад
Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #5 : 16-04-2006 13:01 » 

Цитата
dimka, я не описывал "сотрудника и зарплату".
Ага

Цитата
См.: история (включая текущее значение) хранится в СП2, в СП1 - статическая часть, история изменений которой не нужна.
Одна запись СП1 ссылается на одну строку СП2 через sp2_id - это текущее значение.
Выбрать всю историю изменений можно из СП2 по sp1_id.
Я это и имел ввиду, только вместо SP1.sp2_id использовать текущую дату.

Сравни:

Код:
select ODT.*, e.*, p.*
from ODT
  inner join Employee e on e.eid = ODT.eid
  inner join Paid p on p.eid = e.eid
where
  p.begdate < @currentdate and
  not exists(
    select *
    from Paid p1
    where
      p1.eid = e.eid and
      p1.begdate > p.begdate and
      p1.begdate < @currentdate)

и

Код:
select ODT.*, e.*, p.*
from ODT
  inner join Employee e on e.eid = ODT.eid
  inner join Paid p on e.pid = p.pid

если добавить вторую дату (завершения), то

Код:
select ODT.*, e.*, p.*
from ODT
  inner join Employee e on e.eid = ODT.eid
  inner join Paid p on e.eid = p.eid
where @currentdate between p.begdate and p.enddate

В случае с ID нужна процедура перевода сотрудника на новую запись истории. Если, скажем, определена история на будущее - план, то по дате переход на новый план осуществляется автоматически, по ID - нет. Пополнение истории в обоих случаях выполняется через процедуру. В случае с ID нельзя легко переместить всю систему в прошлые состояния - нужно править ID в каждой записи справочника. В случае с датой достаточно изменить параметр @currentdate - всё приводится автоматически. Первый запрос тяжелее второго и третьего.

Выбор варианта зависит от специфики задачи. В случае разделения БД на оперативную и архивную, в оперативной целесообразно использовать в основном ID, фиксируя даты, в архивной - даты, игнорируя ID.
« Последнее редактирование: 16-04-2006 13:05 от dimka » Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Hooter
Опытный

ru
Offline Offline
Пол: Мужской

« Ответ #6 : 17-04-2006 06:47 » 

RomCom, думаю тебе могут помочь книги:
1. Developing Time-Oriented Database in SQL, Richard T. Snodgrass ( ~2,5 Мб)
2. Temporal Databases Research (~ 4,7 Мб)

В книгах описываются подходы по разработке темпоральных баз данных. Например, такая задача
Данные нужно хранить не один месяц, и даже год. За это время данные о работниках могут претерпеть значительные изменения, но это никак не должно повлиять на отчет за прошедший месяц или год.
это одна из задач, которую могут решить темпоральные базы данных.

Если не найдешь, где скачать - пиши, возможно, смогу скинуть на мыло.
Записан
RomCom
Опытный

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #7 : 19-04-2006 03:16 » 

Hooter, спасибо за наводку на "темпоральные базы данных" почитаю.

dimka, RXL, обмозговав ваши примеры реализовал на практике "выжимку" из них Улыбаюсь
Моя реализация немного отличается, прошу высказать свои мнения.
И так, я исходил из того что изменение справочника происходит редко, а запрсы (отчеты, просмотр) регулярно. Вот что у меня получилось исходя из этох предпосылок:
справочник (sp):
| sp_pk | sp_hist_id |
история строк справочника (sp_hist):
| sp_hist_pk | sp_id | FIO | PASPORT | ... |
ОТД:
| sp_hist_id | какие то данные |
Поясню. sp состоит из двух полей и хранит id текущих записей из sp_hist. sp_hist это собственно и есть сам справочник с историей всех записей. При изменении записи в справочнике, в sp_hist вставляется новая запись с новыми значениями всех полей, а в sp значение sp_hist_id изменяется на sp_hist_pk новой записи. Т.е. ведется история не по каждому полю в отдельности, а всей строки. В ОТД в поле sp_hist_id сохраняется значение sp_hist_pk актуальное на момент вставки записи.
Такая схема позволяет свести запрос к одному "inner join":
Код:
select ODT.*, sp_hist.* from ODT
inner join sp_hist on sp_hist.sp_hist_pk= ODT.sp_hist_id
« Последнее редактирование: 19-04-2006 03:18 от RomCom » Записан

R.O.M.C.O.M.: Robotic Operational Mathematics and Ceaseless Observation Machine
Страниц: [1]   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines