Пересмотрено 01 января 11
Хорошо, поэтому существует разрыв между тем, где я сижу (поставляю полностью проверяемые базы данных; ваши требования требуют этого) и где вы сидите: основываясь на ваших вопросах и комментариях.Что мы, вероятно, отработаем в комментарии.Вот позиция, с которой нужно начинать.
Чтобы обеспечить это требование, совсем не нужно: триггеры;массовое дублирование;нарушение целостности;и т. д.
Это не классическое временное требование, поэтому не нужно для возможности "period", но вы можете .
ValidFrom и ValidTo - это ошибка нормализации: ValidTo - это данные, которые легко получить;ValidTo в любой строке дублируется, в ValidFrom следующей строки;у вас есть аномалия обновления (когда вы обновляете один столбец в одной строке, вам дополнительно необходимо обновить другой столбец в следующей строке);Вы должны использовать фиктивное значение для «текущего».
Все ненужное, используйте только ValidFrom и сохраняйте db в чистоте и чистоте 5NF.
Предостережение, если PostgreSQL не может выполнитьПодзапросы, не попадая в кучу (аля Oracle), затем нормально, Kep ValidTo.
Все эти вещи доступны для редактирования в системепо пользователям, и могут быть удалены.
Ну нет.Это база данных, содержащая важную информацию;с ссылочной целостностью, а не блокнотом, поэтому пользователь не может просто подойти к нему и «удалить» что-то.Это будет противоречить требованию пользователей о ведении исторических данных (в разделе «Чтение; Предупреждение; Подтверждение; Действие; Загрузить»).
Каскадное удаление не допускается.Эти функции являются флажками для не-баз данных, типов MS Access.Для реальных баз данных ограничения RI препятствуют удалению родителей с детьми.
Первичные ключи не могут (не должны) изменяться.Например.Идентификатор пользователя;LocationId;NetworkSlaveCode никогда не меняется;помните, они тщательно считаются идентификаторами .Одной из характеристик ПК является то, что они стабильны.
Вы можете добавлять новых пользователей;Вы можете изменить текущий Имя пользователя;но вы не можете удалить пользователя, у которого есть записи в разделе «Загрузка», «Подтверждение», «Действие».
В основном, если он редактируемый, он должен быть историческим (поэтому исключая показанияоповещения).
Также не включает: Загрузки;Выражение признательности;Действия.
И Справочные таблицы: SensorType;AlertType;ActionType.
И новые таблицы истории: они вставляются, но не могут быть обновлены или удалены.
Я обнаружил проблему с флагом isObselete ..Скажем, если вы измените местоположение, внешний ключ датчика теперь будет указывать на устаревшую запись, то есть вам придется дублировать каждую запись датчика.Эта проблема экспоненциально ухудшается по мере увеличения иерархии.
Хорошо, теперь вы понимаете, что LocationId
(FK) в Sensor
не изменится;нет массового дублирования и т. д.?Во-первых, нет проблем (и есть в этой глупой книге!), Во-вторых, экспоненциально ухудшается.
IsObsolete
не соответствует вашим требованиям. (см. Ниже)
UpdatedDtm
в любой реальной строке (Reading
и т. Д.) Идентифицирует строку истории родительского (от FK до Sensor
) (ее AuditedDtm
), который действовал в то время.
Полная реляционная возможность;Декларативная целостность и т. Д.
Поддерживать IDEF1X, реляционная концепция сильных идентификаторов ... Существует только одна текущая родительская строка (например, Местоположение)
Строки в Истории - это Изображения текущей строки, до того как она была изменена, в указанном AuditedDtm
.В текущей строке (не в истории) отображается последний обновленный элемент UpdateDtm, когда строка была изменена.
AuditedDtm
показывает всю серию UpdatedDtms
для любого заданного ключа; и поэтому я использовал его для «разделения» реального ключа во временном смысле.
Все, что требуется, это таблица истории для каждой изменяемой таблицы. Я предоставил таблицы Hiistory для четырех идентификационных таблиц: Location; Датчик; NetworkSlave; и пользователь.
Пожалуйста, прочитайте это для понимания Аудит в смысле бухгалтерского учета .
Модель данных
Ссылка на Модель данных датчика с историей (Страница 2 содержит таблицы истории и контекст).
Читатели, не знакомые со стандартом реляционного моделирования, могут найти нотацию IDEF1X полезными.
Ответ на комментарий
(1) Моя первая проблема - это проблема ссылочной целостности с историческими данными, в которой я не уверен, что есть, и если есть, я не уверен, как она работает. Например, в SensoryHistory можно было бы добавить запись, у которой было значение UpdateDtm, указывающее дату и время до появления самого местоположения, если вы понимаете, что я имею в виду. Является ли это на самом деле проблемой, я не уверен - принудительное выполнение этого может быть чрезмерным.
(Вы подняли аналогичную проблему в другом вопросе.) Возможно, что у dbs, которые вы испытали, на самом деле не было ссылочной целостности; что отношения были там только для документации; что RI был «реализован в коде приложения» (что означает, что RI отсутствует).
Это база данных SQL стандарта ISO / IEC / ANSI. Это позволяет декларативную ссылочную целостность. Каждая строка отношения реализована в виде ссылки PK :: FK, фактического ограничения, которое объявлено. Например:
<code>CREATE TABLE Location
...
CONSTRAINT UC_PK
PRIMARY KEY (LocationId)
...
CREATE TABLE Sensor
...
CONSTRAINT UC_PK
PRIMARY KEY (LocationId, SensorNo)
CONSTRAINT Location_Sensor_fk
FOREIGN KEY (LocationId)
REEFERENCES Location(LocationId)
...
CREATE TABLE SensorHistory
...
CONSTRAINT UC_PK
PRIMARY KEY (LocationId, SensorNo, UpdatedDtm))
CONSTRAINT Sensor_SensorHistory_fk
FOREIGN KEY (LocationId, SensorNo)
REEFERENCES Sensor (LocationId, SensorNo)
...
Эти объявленные ограничения применяются сервером; не через триггеры; не в коде приложения. Это значит:
- A
Sensor
с LocationId
, который не существует в Location
, не может быть вставлен
- A
LocationId
в Location
, в котором есть строки в Sensor
, удалить нельзя
- A
SensorHistory
с LocationId+SensorNo
, который не существует в Sensor
, не может быть вставлен
- A
LocationId+SensorNo
в Sensor
, в котором есть строки в SensorHistory
, удалить нельзя.
(1.1) Все столбцы должны иметь ПРАВИЛА и ПРОВЕРКУ Ограничения, чтобы ограничить их диапазон значений. Это в дополнение к тому факту, что все INSERT / UPDATE / DELETE являются программными, в хранимых процессах, поэтому несчастных случаев не происходит, и люди не подходят к базе данных и не запускают против нее команды (за исключением SELECTS).
Обычно я держусь подальше от триггеров. Если вы используете хранимые процедуры и обычные разрешения, то это:
в SensoryHistory можно было бы добавить запись, для которой было указано UpdateDtm, указывающее дату и время до появления самого Местоположения, если вы понимаете, что я имею в виду
запрещено. Таким образом, вставляется SensorHistory с UpdatedDtm раньше, чем сам Sensor. Но процы не являются декларативными правилами. Однако, если вы хотите быть уверены вдвойне (а я имею в виду вдвойне, потому что ВСТАВКИ все через proc, прямая команда пользователей), тогда, конечно, вы должны использовать триггер. Для меня это слишком.
(2) как указать удаление? Я мог бы просто добавить флаг к неисторической версии таблицы.
Пока не уверен. Например. Согласны ли вы с тем, что при удалении Sensor
оно является окончательным ... (да, история сохраняется) ... и затем, когда к Location
добавляется новый Sensor
, он будет иметь новый SensorNo
... логически не заменяется Sensor
на новый, с разрывом во времени или без него?
С точки зрения конечного пользователя, с помощью программного обеспечения они должны иметь возможность добавлять, редактировать и удалять датчики по своему желанию без ограничений. Но да, после удаления он удаляется и не может быть восстановлен. Ничто не помешает им повторно добавить датчик позже, хотя с такими же параметрами.
И "удалить" Locations, NetworkSlaves
, а также Users
.
Ok. Тогда новый Sensor
с теми же параметрами действительно новый, он имеет новый SensorNo
и не зависит от любого предыдущего логического Sensor
. Мы можем добавить IsObsolete
BOOLEAN к четырем идентифицирующим таблицам; теперь он идентифицирован как адекватный. Удалить теперь мягкое удаление.
(2.1) Для NetworkSensor
и LoggerSensor
, которые фактически зависят от двух родителей: они устарели, если один из их родителей устарел. Поэтому нет смысла давать им столбец IsObsolete
, который имеет двойное значение и может быть получен из соответствующего родителя.
(2.2) Для ясности, пользователи не могут удалять строки из таблиц транзакций и истории, верно?
(3) При обновлении таблицы, какой метод будет лучше всего вставить новую строку в историческую таблицу и обновить основную таблицу? Может быть, просто обычные операторы SQL внутри транзакции?
Да. Это классическое использование Транзакции, согласно ACID Properties, это Atomic; он либо завершается успешно, либо завершается неудачей (чтобы повторить попытку позже, когда проблема будет устранена).
(4) Справочная книга
Окончательный и оригинальный текст: Временные данные и реляционная модель C J Date, H Darwen, N A Lorentzos. Например, те из нас, кто принимает RM, знакомы с расширениями и тем, что требуется от преемника RM; а не какой-то другой метод.
Указанная книга ужасна и бесплатна. PDF не PDF (без поиска; без индексации). Открытие моего MS и Oracle говорит; несколько хороших кусочков в куче пуха. Много искажений. Не стоит подробно отвечать (если вы хотите получить правильный обзор, откройте новый вопрос).
(4.1) ValidTo
в дополнение к ValidFrom
. Серьезная ошибка (как указано в верхней части моего ответа), которую совершает книга; затем кропотливо решает. Во-первых, не совершайте ошибку, а во-вторых, вам нечего решать. Насколько я понимаю, это устранит ваши триггеры.
(4.2) Простые правила, учитывающие как нормализацию, так и временные требования. Прежде всего, вы должны глубоко понимать (а) временные требования и (б) типы данных, правильное использование и ограничения. Всегда хранить:
Мгновенно как DATETIME, например. UpdatedDtm
Интервал как INTEGER, четко идентифицирующий Единицу в названии столбца, например. IntervalSec
Период. Зависит от соединенного или несвязанного.
- Для конъюнктуры, к которой относится это требование, (4.1): используйте один DATETIME; конец периода может быть получен из начала периода следующей строки.
- Для разделенных периодов да, вам нужно 2 x DATETIME, например,
RentedFrom
и RentedTo
с промежутками между ними.
(4.3) Они связываются с «временным первичным ключом», что усложняет код (в дополнение к требованию триггеров контролировать аномалию обновления). Я уже доставил чистый (испытанный и проверенный) временный первичный ключ.
(4.4) Они связываются с фиктивными значениями, нереальными значениями и значениями Null для «сейчас». Я не допускаю такие вещи в базе данных. Так как я не храню дублированный ValidTo
, у меня нет проблемы, здесь нечего решать.
(4.5) Следует задаться вопросом, почему «учебник» на 528 страниц доступен бесплатно в Интернете, в плохой PDF-форме.
(5) Я [пользователь] мог бы спокойно удалить, например, все строки LocationHistory (оставив только текущую версию в таблице Location) - даже если может существовать строка SensorHistory, которая концептуально «принадлежит» к предыдущей версии Местоположения, если это имеет смысл.
Это не имеет смысла для меня, в общении, которое мы должны закрыть, все еще есть пробел. Пожалуйста, продолжайте взаимодействовать, пока он не закроется.
В реальной (стандартной ISO / IEC / ANSI SQL) базе данных мы делаем не GRANT INSERT / UPDATE / DELETE пользователям. Мы ПРЕДОСТАВЛЯЕМ ВЫБОР и ССЫЛКИ * только 1278 * (для выбранных пользователей) Все ВСТАВКИ / ОБНОВЛЕНИЯ / УДАЛЕНИЯ кодируются в Транзакциях, что означает хранимые процедуры. Затем мы предоставляем GREC EXEC для каждого сохраненного процесса выбранным пользователям (используйте ROLES для сокращения администрирования).
Поэтому никто не может удалить из любой таблицы, не выполняя процедуру.
Не пишите процедуру для удаления из любой таблицы истории. Эти строки не должны быть удалены. В этом случае отсутствие разрешения и отсутствие кода является Ограничением.
Технически, все строки Истории действительны, нет Периода для беспокойства. Самая старая строка LocationHistory содержит предварительное изображение исходной строки Location до ее изменения. Самые младшие строки LocationHistory - это изображение перед текущей строкой Location. Таким образом, каждая промежуточная строка LocationHistory действительна и применяется к промежуточному периоду.
Нет необходимости «обрезать» или искать несколько строк LocationHistory, которые можно удалить на основании того, что они применяются к неиспользуемому периоду: все они используются . (Определенно, без необходимости проверять какое-либо отображение дочерних объектов Location на любую строку (и) LocationHistory, чтобы доказать это.)
Итог: пользователь не может удалить таблицу из любой истории (или транзакции).
Или ты опять имеешь в виду что-то другое?
Примечание. Я добавил (1.1) выше.
(6) Исправлена одна ошибка в DM. Alert
является выражением Reading
, а не Sensor
.
(7) Исправил бизнес-правила в другом вопросе / ответе, чтобы отразить это; и новые правила, изложенные в этом вопросе.
(8) Понимаете ли вы / цените ли вы, что, поскольку у нас полностью совместимая с IDEF1X модель, повторно Идентификаторы :
Идентификаторы передаются по всей базе данных, сохраняя свою силу. Например. при перечислении Acknowledgements
они могут быть соединены напрямую с Location
и Sensor
; промежуточные таблицы не нужно читать (и они должны быть, если используются ключи Id
). Вот почему на самом деле требуется меньше объединений в реляционной базе данных (и больше соединений требуется в ненормализованной).
Подтипы и т. Д. Необходимо перемещаться только , когда этот конкретный контекст имеет значение.