Моделирование необязательных свойств сущности на основе изменяемого дискриминатора в родительской сущности - PullRequest
0 голосов
/ 05 апреля 2020

Мне трудно вывести реляционную модель из требований моего проекта.

Общий контекст касается устройств, публикующих данные датчиков.

В табличных представлениях PK каждой таблицы выше пунктирной линии, и я также включаю некоторые примеры данных. Любой несоответствующий столбец опущен.

  • Существуют различные типы датчиков, различаемых по типу считываемых данных. У нас есть датчики «Measure», измеряющие величину в виде значения NUMERI C (без температуры) и датчики, считывающие изменение состояния / переключателя в виде значения BIT (при открытии-закрытии двери).
[SensorType]
########################    | SensorTypeCode | ReadingTypeCode |
# SensorTypeCode       #    ------------------------------------
# -------------------- #    | Temperature    | Measure         |     
# ReadingTypeCode      #    | Humidity       | Measure         |
########################    | Door           | Switch          |
  • Существуют различные типы устройств в зависимости от типов датчиков, которые они содержат. В устройстве может содержаться только один датчик каждого типа. Тип устройства является неизменным относительно типов датчиков, которые он содержит.
[DeviceType]
########################    | DeviceTypeCode |
# DeviceTypeCode       #    ------------------
# -------------------- #    | D_TYPE_1       |   
########################    | D_TYPE_2       |

[DeviceTypeSensorType]
########################    | DeviceTypeCode | SensorTypeCode |
# DeviceTypeCode       #    -----------------------------------
# SensorTypeCode       #    | D_TYPE_1       | Temperature    |
# ---------------------#    | D_TYPE_1       | Humidity       |
########################    | D_TYPE_2       | Temperature    |
                            | D_TYPE_2       | Door           |
  • Устройство имеет уникальный идентификатор и тип. Тип устройства может меняться, это означает, что датчики могут быть добавлены или удалены.
[Device]
########################    | DeviceMacAddress  | DeviceTypeCode |
# DeviceMacAddress     #    -------------------------------------
# ---------------------#    | 00:00:00:00:00:00 | D_TYPE_1       |
# DeviceTypeCode       #    | FF:FF:FF:FF:FF:FF | D_TYPE_2       |
########################
  • Устройства публикуют показания, содержащие некоторые общие поля, независимо от типа устройства и типов датчиков, содержащихся в устройстве.
[Reading]
########################
# DeviceMacAddress     #
# ReadingDtm           #
# ---------------------#
# BatteryLevel         #
# ...                  #
########################

Кроме того, чтение устройства содержит данные, относящиеся ко всем датчикам устройства. Это будут две дочерние таблицы Reading, ReadingMeasure и ReadingSwitch, в которых хранятся данные датчика, различаемые дискриминатором ReadingTypeCode.

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


1-й подход

Создайте таблицу истории устройств, сохраняющую любые предыдущие типы устройств, и добавьте поле UpdatedDtm в Devices, следуя описанному процессу моделирования здесь .

[DeviceHistory]
########################   
# DeviceMacAddress     # 
# AuditedDtm           #
# ---------------------#
# DeviceTypeCode       #
# UpdatedDtm           #
########################

Структурирование таблиц Reading{Type} как:

[ReadingMeasure]               [ReadingSwitch]
########################      ########################
# DeviceMacAddress     #      # DeviceMacAddress     #
# ReadingDtm           #      # ReadingDtm           #
# DeviceTypeCode       #      # DeviceTypeCode       # 
# SensorTypeCode       #      # SensorTypeCode       #  
# ---------------------#      # ---------------------#
# Value (NUMERIC)      #      # Value (BIT)          #
########################      ########################

с

  • (DeviceMacAddress, ReadingDtm) FK-ссылка Reading
  • (DeviceType, SensorType) FK-ссылка DeviceTypeSensorType

Таким образом, я могу просто обновить Device.DeviceTypeCode и (DeviceType, SensorType) FK по-прежнему будет иметь действительную ссылку на неизменяемую строку в DeviceTypeSensorType. Любые новые показания будут вставлены с учетом нового типа устройства.

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


2-й подход

I дополнение к таблицам предыдущего подхода также создайте таблицу Sensor и два эксклюзивных подтипа Sensor, SensorMeasure и SensorSwitch

[Sensor]
########################   
# DeviceMacAddress     #    
# DeviceTypeCode       #
# SensorTypeCode       #  
# ---------------------#
# IsObsolete           #
########################

| DeviceMacAddress  | DeviceTypeCode | SensorTypeCode |  IsObsolete |
---------------------------------------------------------------------
| 00:00:00:00:00:00 | D_TYPE_1       | Temperature    | 0           |
| 00:00:00:00:00:00 | D_TYPE_1       | Humidity       | 0           |
| FF:FF:FF:FF:FF:FF | D_TYPE_1       | Temperature    | 0           |
| FF:FF:FF:FF:FF:FF | D_TYPE_1       | Door           | 0           |


[SensorMeasure]               [SensorSwitch]
########################      ########################
# DeviceMacAddress     #      # DeviceMacAddress     #
# DeviceTypeCode       #      # DeviceTypeCode       #
# SensorTypeCode       #      # SensorTypeCode       #
# ---------------------#      # ---------------------#
########################      ########################

В этом случае:

  • ReadingMeasure и ReadingSwitch будут иметь FK (DeviceMacAddress, DeviceTypeCode, SensorTypeCode), ссылающиеся на SensorMeasure и SensorSwitch соответственно.
  • Sensor будет иметь FK (DeviceTypeCode, SensorTypeCode), ссылающийся на DeviceTypeSensorType.
  • Вставка в Device приводит к соответствующим Sensor вставкам, в зависимости от тип устройства.
  • При изменении типа устройства я отмечу любой параметр удаления или переустановки датчика. Sensor.IsObsolete

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


Есть мысли?

Ответы [ 2 ]

0 голосов
/ 07 апреля 2020

Это довольно многословно, но надежда может помочь. По сути, представлены таблицы конфигурации устройства и таблицы текущей конфигурации. Таким образом, показания отслеживают пару (DEV_MAC, DEV_CFG_NO), которая сохраняет исторические данные при изменении конфигурации устройств. Я также несколько переименовал атрибуты, чтобы сэкономить на экране.

[p 1] Тип датчика SNS_TYP типа чтения REA_TYP существует.

(c 1.1) Каждый тип датчика имеет только один тип чтения; возможно, что более одного типа датчика имеют одинаковый тип чтения.

SensorType {SNS_TYP, REA_TYP}  -- p 1
        PK {SNS_TYP}           -- c 1.1

CHECK REA_TYP in (Measure, Switch)


-- sample
(Temperature, Measure)
(Humidity,    Measure)
(Door,        Switch)

[p 2] Тип устройства DEV_TYP существует.

(c 2.1) Тип устройства обозначается DEV_TYP.

DeviceType {DEV_TYP} -- p 2
        PK {DEV_TYP} -- c 2.1

-- sample
(d_type_1)
(d_type_2)

[p 3] Тип устройства DEV_TYP содержит тип датчика SNS_TYP.

(c 3.1) Для каждого типа устройства этот тип может содержать более одного типа датчика; для каждого типа датчика этот тип датчика может содержаться более чем в типе устройства.

(c 3.2) Для каждого типа устройства и типа датчика этот тип датчика может содержаться не более в этом типе устройства. один раз.

(c 3.3) Если тип устройства содержит тип датчика, то этот тип устройства должен существовать.

(c 3.4) Если тип устройства содержит тип датчика, то этот датчик тип должен существовать.

DeviceTypeSensorType {DEV_TYP, SNS_TYP} -- p 3
                  PK {DEV_TYP, SNS_TYP} -- c 3.1, 3.2

FK1 {DEV_TYP} REFERENCES DeviceType -- c 3.3
FK2 (SNS_TYP} REFERENCES SensorType -- c 3.4


-- sample
(d_type_1, Temperature)
(d_type_1, Humidity)
(d_type_2, Temperature)
(d_type_2, Door)

[p 4] Устройство с MA C адресом DEV_MAC существует.

(c 4.1) Устройство идентифицируется по адресу MA C.

Device {DEV_MAC} -- p 4
    PK {DEV_MAC} -- c 4.1


-- sample
(00:00:00:00:00:00)
(FF:FF:FF:FF:FF:FF)

[p 5] Конфигурация устройства DEV_MAC изменена на тип устройства DEV_TYP в качестве номера конфигурации устройства DEV_CFG_NO, на дату изменения VALID_FROM.

(c 5.1) Конфигурация устройства определяется устройством и номером конфигурации устройства.

(c 5.2) Для каждого устройства и даты изменения это устройство меняло конфигурацию на эту дату ровно один раз; возможно, что более чем одно устройство изменило конфигурацию на эту дату.

(c 5.3) Для каждой конфигурации устройства и типа устройства эта конфигурация устройства имеет ровно один тип устройства; возможно, что для этого типа устройства используется более одной конфигурации устройства.

(c 5.4) Если конфигурация устройства изменилась, то это устройство должно существовать.

(c 5.5) Если конфигурация устройства изменилась на тип устройства, то этот тип устройства должен существовать.

DeviceConfig {DEV_MAC, DEV_CFG_NO, DEV_TYP, VALID_FROM} -- p 5
          PK {DEV_MAC, DEV_CFG_NO} -- c 5.1, 5.3
          AK {DEV_MAC, VALID_FROM} -- c 5.2

FK1 {DEV_MAC} REFERENCES Device     -- c 5.4
FK2 {DEV_TYP} REFERENCES DeviceType -- c 5.5


-- sample
(00:00:00:00:00:00, 1, d_type_1, 2019-05-01)
(00:00:00:00:00:00, 2, d_type_2, 2019-07-01)
(00:00:00:00:00:00, 3, d_type_1, 2019-09-01)
(FF:FF:FF:FF:FF:FF, 1, d_type_2, 2020-01-01)
(FF:FF:FF:FF:FF:FF, 2, d_type_1, 2020-03-01)

Время остановиться здесь и рассмотреть некоторые предположения. Я предполагаю, что новая конфигурация устройства известна и введена в DeviceConfig до того, как устройство начнет потоковую передачу данных.

Данные устройства могут выглядеть примерно так (JSON):

{ "device":"00:00:00:00:00:00",
  "time":"2019-07-01T17:15:12",
  "data":{ "Battery":75,
           "Temperature":35.5,
           "Humidity":1,
         }
}

Этот формат обеспечивает:

{DEV_MAC, SNS_TYP, SNS_VALUE, READ_DTM}

Хотелось бы иметь:

{DEV_MAC, DEV_CFG_NO, DEV_TYP, SNS_TYP, REA_TYP, SNS_VALUE, REA_TYP, READ_DTM}

Чтобы "обогатить" данные датчика, ток создается вид конфигурации.

[P 6] Устройство DEV_MAC в текущей конфигурации DEV_CFG_NO, типа устройства DEV_TYP, содержит тип датчика SNS_TYP типа чтения REA_TYP.

-- implement as a view
CurrentConfig {DEV_MAC, DEV_CFG_NO, DEV_TYP, SNS_TYP, REA_TYP}
          KEY {DEV_MAC, SNS_TYP} -- Logical


-- sample
(00:00:00:00:00:00, 3, d_type_1, Temperature, Measure)
(00:00:00:00:00:00, 3, d_type_1, Humidity, Measure)
(FF:FF:FF:FF:FF:FF, 2, d_type_1, Temperature, Measure)
(FF:FF:FF:FF:FF:FF, 2, d_type_1, Humidity, Measure)
-- sql server, postgreSQL
CREATE VIEW CurrentConfig AS
WITH
q_00 as (
        SELECT DEV_MAC
             , max(VALID_FROM) as LATEST
        FROM DeviceConfig
)
SELECT c.DEV_MAC
       c.DEV_CFG_NO
       c.DEV_TYP
       w.SNS_TYP
       s.REA_TYP
FROM DeviceConfig         AS c
JOIN q_00                 AS q  ON q.DEV_MAC = c.DEV_MAC
                               AND q.LATEST  = c.VALID_FROM
JOIN DeviceTypeSensorType AS w  ON w.DEV_TYP = c.DEV_TYP
JOIN SensorType           AS s  ON s.SNS_TYP = w.SNS_TYP

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

{(DEV_MAC, DEV_CFG_NO, DEV_TYP, SNS_TYP, REA_TYP, SNS_VALUE, REA_TYP, READ_DTM)}

Теперь эти кортежи можно вставлять в таблицы чтения.


[p 7] Устройство DEV_MAC в конфигурации устройства DEV_CFG_NO отправленные данные во время чтения READ_DTM; этому чтению был присвоен номер READING_ID.

Reading {READING_ID, DEV_MAC, DEV_CFG_NO, READ_DTM} -- p 7
     PK {READING_ID}
     AK {DEV_MAC, DEV_CFG_NO, READ_DTM}

FK1 {DEV_MAC, DEV_CFG_NO} REFERENCES DeviceConfig

Подтипы теперь различаются по типу чтения (REA_TYP). Добавлены некоторые дополнительные элементы управления для проверки типов для SNS_TYP для подтипов.

[p 7.1] Показание измерения READING_ID для типа датчика SNS_TYP равно SNS_VALUE.

Reading_Meas {READING_ID, SNS_TYP, SNS_VALUE::Numeric} -- p 7.1
          PK {READING_ID, SNS_TYP}

FK {READING_ID} REFERENCES Reading

CHECK SNS_TYP in (Temperature, Humidity, Battery)

[стр. 7.2] Показания переключателя READING_ID для типа датчика SNS_TYP равно SNS_VALUE.

Reading_Sw {READING_ID, SNS_TYP, SNS_VALUE::Boolean}  -- p 7.2
        PK {READING_ID, SNS_TYP}

FK {READING_ID} REFERENCES Reading

CHECK SNS_TYP in (Door)

Примечание:

All attributes (columns) NOT NULL

[p x]   = predicate  x
(c x.y) = constraint x.y

PK = Primary Key
AK = Alternate Key   (Unique)
SK = Proper Superkey (Unique)
FK = Foreign Key
0 голосов
/ 07 апреля 2020

Это довольно сложный вопрос, поэтому я хотел бы начать с перезапуска проблемного домена - пожалуйста, исправьте, если я неправильно понял.

  • Система содержит много устройств
  • Устройство однозначно идентифицируется неизменным Ma c адресом
  • Устройство принадлежит ровно одному типу устройства в любой точке время
  • Устройство может изменять типы устройств во времени
  • Тип устройства содержит один или несколько типов датчиков и является неизменяемым во времени
  • Датчик Тип обеспечивает тип чтения , который является одним из (переключатель, мера)
  • Устройство производит ноль или более показаний
  • Показание содержит «заголовок», определяющий показания (например, дату / время)
  • Показание содержит ровно одно измерение для каждого датчика, подключенного к устройству, в зависимости от типа устройства, которое активно во время чтения.
  • Измерение относится только к одному показанию.

Если это так, я бы смоделировал отношение устройства к типу устройства как зависящее от времени:

[Device]
| DeviceMacAddress  | DeviceTypeCode | ValidFrom            | ValidUntil
----------------------------------------------------------------
 00:00:00:00:00:00  | D_TYPE1        | 1 Jan 2020 00:00:01. | 23 Jan 2020 12:23:23
 00:00:00:00:00:00  | D_TYPE2        | 23 Jan 2020 12:23:23 | null

Каждый раз, когда тип устройства изменяется, вы устанавливаете значение validUntil для текущей строки (обозначенное validuntil is null) на текущее время и вставляете новую строку с validfrom из той же временной метки и новым типом устройства code.

Это позволяет отслеживать изменение типа устройства с течением времени и воссоздавать его в любой заданный момент времени.

ValidUntil логически избыточен - вы можете использовать только validFrom, и найдите наибольшее значение, меньшее интересующего вас времени; это немного сложнее в больших запросах.

Первичный ключ будет DeviceMacAddress, ValidFrom - строка уникально идентифицируется этой комбинацией. Если вы включите DeviceTypeCode в первичный ключ, вы логически скажете, что каждая строка однозначно идентифицируется этими 3 атрибутами, что предполагает, что может быть строка для того же Ma c и validfrom, но для другого типа devicetype, что, вероятно, не правильно.

Таблица чтения, как вы обрисовали; при объединении показаний с устройствами вы используете временные метки:

where reading.timestamp between device.validfrom and device.validuntil

В каждой из таблиц Reading<type> будет по одной строке для каждой записи, которая действительна на момент чтения в DeviceType. таблица связана с устройством. Вам не нужен столбец DeviceTypeCode в этой таблице, потому что вы можете получить эту информацию, посмотрев соединение с Device до DeviceType, которое было активным во время чтения.

...