Правильный способ реализации отношения от 1 до 0..1 в реляционных базах данных - PullRequest
0 голосов
/ 22 января 2020

Представьте себе, в нашей модели данных у нас есть сущность (структура данных), которая имеет необязательные части. Мы можем реализовать эти «части» как пустые ссылки на другие (дочерние) сущности. Другими словами, каждый экземпляр главного объекта может иметь или не иметь единственный экземпляр другого (дочернего) объекта, связанного с ним, и любой экземпляр дочернего объекта имеет только один экземпляр главного объекта, связанный с ним. Таким образом, у нас есть отношение от 1 до 0..1.

Например, запись журнала аудита имеет общие поля, такие как (отметка времени, пользователь, операция), а также спецификация операции c part (расширенная информация), которая может быть совершенно разным для разных операций. Мы можем использовать отдельную сущность для представления каждого типа расширенной информации, а затем заставить основную сущность иметь обнуляемые ссылки на каждый возможный тип расширенной информации.

Я вижу 2 способа реализации ее в реляционных базах данных:

1. Обнуляемые ссылки на дочерние сущности в основной записи

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

Это кажется более простым вариантом: чтобы получить соответствующую информацию, мы просто следуем по прямой ссылке. В запросе SQL мы объединяем дочерние таблицы (расширения) по внешнему ключу. Нулевые значения внешнего ключа дадут нам нулевые значения для всех полей дочерних таблиц.

2. Записи дочерних сущностей ссылаются на записи главной сущности

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

???

Какой подход является правильным? Второй, кажется, более реляционный , и нам не нужно создавать дополнительные поля в основной таблице, но технически может потребоваться больше работы для поиска связанных записей, потому что вместо следующих прямых ссылок мы имеем искать основной идентификатор в дочерних таблицах. Или механизмы БД оптимизируют этот тип соединений, чтобы они были быстрыми, например, с помощью индексов? Поиск по индексу быстрее, чем сканирование, но все же медленнее, чем прямая ссылка. Плюс индексы занимают место. Я восполняю недостаток знаний о том, как работают механизмы БД ... Или, может быть, я просто упускаю что-то очевидное. Помощь будет высоко ценится.

Обновление

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

1 Ответ

1 голос
/ 22 января 2020

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

Второе решение не имеет нулевых значений, что часто усложняет запросы (и нулевые значения также делают решение «неправильным» для пуристов реляционной модели), но имеет и другие преимущества: оно требует меньше места (так что отношения более компактны и требуют меньше времени для обработки, для определенные виды операций). С другой стороны, для доступа к подробным данным требуются объединения (и поэтому для этих операций требуется дополнительный индекс).

Первое решение концептуально более простое, не требует объединений в случае доступа к подробным данным, но требует больше места, и это может замедлить определенные типы операций.

Используются оба решения в практическом контексте.

Я думаю, что решение этой дилеммы может быть обеспечено только путем знания типичной рабочей нагрузки приложения, которое должно быть развернуто в такой базе данных: чаще (или должно иметь меньшую задержку) одни запросы по отношению к другим? Например, запросы, которые смотрят только общие данные, используются чаще, чем запросы, которые требуют подробных данных?

Наконец, если трудно или невозможно провести такой «теоретический» анализ, единственный другой способ - попробовать одно решение, но при этом быть готовым попробовать и другое, если действия не удовлетворяют. Это можно сделать с помощью представлений, например, следующим образом:

  1. Начните со второго решения и определите представление, которое выполняет объединение.

  2. Напишите приложение, используя, при необходимости, либо базовую таблицу, либо представление объединения.

  3. Если производительность не удовлетворяет, переключитесь на другое решение, создав новую таблицу путем объединения с тем же именем старого представления и определения нового представления, которое выполняет только проекцию на ненулевые атрибуты.

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

...