Ответ 1
Отредактировано 01 янв 11 21:50 UTC
Модель данных
Я думаю, что ваша модель данных должна выглядеть так: ▶ Модель данных датчиков ◀. (Страница 2 относится к вашей другой истории вопроса).
Читатели, которые не знакомы с стандартом реляционного моделирования, могут найти ▶ IDEF1X Notation ◀.
Бизнес (правила, разработанные в комментариях)
Я идентифицировал некоторые ранние бизнес-правила, которые теперь устарели, поэтому я удалил их
Они могут быть "прочитаны" в отношениях (читаются рядом с моделью данных). Бизнес-правила и все подразумеваемые ссылочные данные и целостность данных могут быть реализованы и, следовательно, гарантированы правилами RULES, CHECK Constraints в любой базе данных ISO SQL. Это демонстрация IDEF1X в разработке как реляционных ключей, так и сущностей и отношений. Обратите внимание, что фразы глаголов более чем просто процветают.
Помимо трех справочных таблиц, единственными статическими идентифицирующими объектами являются Location, NetworkSlave и User. Датчик является центральным для системы, поэтому я получил его собственный заголовок.
Местоположение
- A
Location
содержит один-ко-многимSensors
- A
Location
может иметь один Logger
NetworkSlave
- NetworkSlave собирает показания для сетевых сенсоров "один ко многим"
Пользователь
- An
User
может поддерживать нуль-ко-многимLocations
- An
User
может поддерживать нуль-ко-многимSensors
- An
User
может поддерживать нуль-ко-многимNetworkSlaves
-
User
может выполнять нуль-ко-многимDownloads
- An
User
может делать ноль-ко-многимAcknowledgements
, каждый на одномAlert
- An
User
может принимать значение "0 ко многим"Actions
, каждый из которыхActionType
датчик
-
A
SensorType
устанавливается как ноль-ко-многимSensors
-
A
Logger
(дома и) собираетReadings
для одногоLoggerSensor
-
A
Sensor
является либо однимNetworkSensor
, либо однимLoggerSensor
- A
NetworkSensor
записиReadings
, собранные однимNetworkSlave
,
- A
- A
Logger
периодическиDownloaded
один раз во много раз- A
LoggerSensor
записиReadings
, собранные однимLogger
,
- A
- A
Reading
можно считать вAlert
одногоAlertType
- Возможно, что
AlertType
может выполняться на ноль-ко-многимReadings
,
- Возможно, что
- An
Alert
может быть однимAcknowledgement
, одним пользователем . - An
Acknowledgement
может быть закрыто однимAction
, однимActionType
, однимUser
- An
ActionType
может быть применен к ноль-ко-многимActions
- An
Ответы на комментарии
-
Стоящие столбцы
Id
во всем, что движется, мешают определению идентификаторов, естественным реляционным ключам, которые дают вашей базе данных "силу". Это суррогатные ключи, что означает дополнительный ключ и индекс, и это мешает этой реляционной силе; что приводит к большему количеству объединений, чем в противном случае. Поэтому я использую их только тогда, когда реляционный ключ становится слишком громоздким, чтобы перейти к дочерним таблицам (и принять наложенное дополнительное соединение). -
Отказоустойчивые ключи - классический симптом Ненормализованной базы данных. Нуль в базе данных - плохая новость для производительности; но Nulls in FKs означает, что каждая таблица делает слишком много вещей, имеет слишком много значений, а результаты - очень плохой код. Хорошо для людей, которые любят "реорганизовывать" свои базы данных; совершенно ненужно для реляционной базы данных.
-
Разрешено:
Alert
может бытьAcknowledged
;Acknowledgement
может бытьActioned
. -
Столбцы над строкой являются Первичным ключом (см. Notation document).
SensorNo
- порядковый номер внутриLocationId
; обратитесь к бизнес-правилам, это не имеет смысла за пределамиLocation
; два столбца вместе образуют PK. Когда вы готовы ВСТАВИТЬ датчик (после того, как вы проверили, что попытка действительна и т.д.), Он получается следующим образом. Это исключает LoggerSensors, которые равны нулю:INSERT Sensor VALUES ( @LocationId, SensorNo = ( SELECT ISNULL(MAX(SensorNo), 0) + 1 FROM Sensor WHERE LocationId = @LocationId ) @SensorCode )
-
Для точности или улучшения значения я изменил
NetworkSlave monitors NetworkSensor
наNetworkSlave collects Readings from NetworkSensor
. -
Проверить ограничения.
NetworkSensor
иLoggerSensor
являются эксклюзивными подтипамиSensor
, и их целостность может быть задана ограничениями CHECK.Alerts, Acknowledgements
иActions
не являются подтипами, но их целостность устанавливается одним и тем же методом, поэтому я перечислил их вместе.-
Каждое отношение в модели данных реализуется как CONSTRAINT в дочернем (или подтипе) как FOREIGN KEY (child_FK_columns). ССЫЛКИ Родительский (PK_columns)
-
Для определения подтипа a
Sensor
требуется дискриминатор. ЭтоSensorNo = 0
дляLoggerSensors
; и ненулевое значение дляNetworkSensors
. - Существование
NetworkSensors
иLoggerSensors
ограничено FK CONSTRAINTS доNetworkSlave
иLogger
соответственно; а также к датчику. - В
NetworkSensor
включить ограничение CHECK, чтобы гарантировать, чтоSensorNo
отличен от нуля -
В
LoggerSensor
включить ограничение CHECK, чтобы гарантировать, чтоSensorNo
равен нулю -
Существование
Acknowledgements
иActions
ограничено идентифицированными FK CONSTRAINTS (AnAcknowledgement
не может существовать безAlert
, аAction
не может существовать безAcknowledgement
). И наоборот, aAlert
безAcknowledgement
находится в непризнанном состоянии; aAlert
с иAcknowledgement
, но noAction
находится в подтвержденном, но неактивном состоянии. ,
-
-
Оповещения. Концепция в дизайне такого приложения (живой мониторинг и предупреждение) - это множество небольших программ, работающих независимо друг от друга; все используют базу данных как единственную версию правды. Некоторые программы вставляют строки (
Readings, Alerts
); другие программы проводят опрос db на наличие таких строк (и отправляют SMS-сообщения и т.д., или ручные устройства выбирают предупреждения, относящиеся только к устройству). В этом смысле db является a, который можно охарактеризовать как поле сообщения (одна программа помещает строки в которые другая программа читает и действия).Предполагается, что
Readings
дляSensors
записывается "вживую" с помощьюNetworkSlave
, и каждую минуту или около того вставляется новый наборReadings
. Фоновый процесс выполняется периодически (каждую минуту или что-то еще), это основная "мониторная" программа, в ней будет много функций. Одной из таких функций будет мониторингReadings
и созданиеAlerts
, которые произошли со времени последней итерации (цикла программы).Внутри цикла будет выполняться следующий сегмент кода: по одному для каждого AlertType. Это классическая проекция:
-- Assume @LoopDateTime contains the DateTime of the last iteration INSERT Alert SELECT LocationId, SensorNo, ReadingDtm, "L" -- AlertType "Low" FROM Sensor s, Reading r WHERE s.LocationId = r.LocationId AND s.SensorNo = r.SensorNo AND r.ReadingDtm > @LoopDtm AND r.Value < s.LowerLimit INSERT Alert SELECT LocationId, SensorNo, ReadingDtm, "H" -- AlertType "High" FROM Sensor s, Reading r WHERE s.LocationId = r.LocationId AND s.SensorNo = r.SensorNo AND r.ReadingDtm > @LoopDtm AND r.Value > s.UpperLimit
Таким образом,
Alert
определенно является фактом, который существует как строка в базе данных. Впоследствии это может бытьAcknowledged
с помощьюUser
(другая строка/факт) иActioned
сActionType
наUser
.Другое, что это (создание действием Projection), т.е. общий и неизменный случай, я бы назвал
Alert
только как строку вAlert
; статический объект после создания. -
Проблемы, связанные с изменением
Users
. Об этом заботятся уже, как следует. В верхней части моего (пересмотренного вчера) ответа я утверждаю, что основные идентификационные элементы статические. Я изменил правила бизнес-правил, чтобы улучшить ясность.-
По причинам, о которых вы говорите,
User.Name
не является хорошим PK дляUser
, хотя он остается альтернативным ключом (уникальным) и тем, который используется для взаимодействия с человеком. -
User.Name
не может быть дублировано, не может быть более одногоFred
; может быть в терминахFirstName-LastName
; дваFred Bloggs
, но не в терминахUser.Name
. Наш второй Фред должен выбрать другойUser.Name
. Обратите внимание на идентифицированные индексы. -
UserId
- постоянная запись, и это уже ПК. Никогда не удаляйтеUser
, это имеет историческое значение. На самом деле ограничения FK остановят вас (никогда не используйте CASCADE в реальной базе данных, это чистое безумие). Нет необходимости в кодах или триггерах и т.д. -
Альтернативно (для удаления
Users
, который никогда ничего не делал и, таким образом, освободилUser.Name
для использования), разрешите Delete, пока не будут обнаружены FK (т.е.UserId
не ссылается наDownload, Acknowledgement, Action
).
Чтобы гарантировать, что только
Users
, которые являются текущими, выполняютActions
, добавьтеIsObsolete
boolean в User (Обновлено DM) и проверьте этот столбец, если эта таблица опрошена для любой функции (кроме отчетов). Вы можете реализовать a ViewUserCurrent
, который возвращает только теUsers
.То же самое относится к
Location
иNetworkSlave
. Если вам нужно различать текущий и исторический, дайте мне знать, я добавлю к нимIsObsolete
.Я не знаю: вы можете периодически очищать базу данных древних исторических данных, удалять строки, которые (например,) старше 10 лет. Сначала это нужно сделать из нижней (таблицы), обрабатывая отношения.
-
Не стесняйтесь задавать вопросы.
Обратите внимание, что документ IDEF1 Notation был расширен.