Концептуально говоря, хотя в вашей бизнес-среде « Порядок» и « Адрес» являются тесно связанными идеями, в действительности они представляют собой два отдельных типа сущностей, каждый из которых имеет свой собственный набор применимых свойств (или атрибутов) и ограничений.
Поэтому, как уже говорилось в комментариях, я согласен с @Erik , и вы должны организовать логическую структуру вашей базы данных, объявляя среди других элементов:
- одна дискретная таблица для хранения информации об элементах адреса ;
- одна таблица для хранения специфичных для клиента данных;
- одна таблица для размещения точек данных заказа ; и
- одна таблица, содержащая факты об отношениях между Клиентом (-ами) и Адресом (-ами ) ;
как я приведу в качестве примера ниже.
Описательная схема IDEF1X
Изображение стоит тысячи слов, поэтому я создал диаграмму IDEF1X, показанную на рисунке 1, чтобы проиллюстрировать некоторые возможности, открываемые моим предложением:
Заказчик , адрес и их ассоциации
Как продемонстрировано, я изобразил связь с отношением ко многим (M: N) кардинальности между типами сущностей Customer a и Address ; этот подход обеспечит гибкость в будущем, поскольку, как вы знаете, клиент может хранить несколько адресов во времени или даже одновременно, и один и тот же адрес может совместно использоваться несколькими клиентами .
Конкретный адрес может быть использован несколькими способами одним клиентом (1: M) ; например, он может быть определен как физический , и / или он может быть установлен для доставки и / или для выставления счетов . Возможно, один и тот же экземпляр адреса может одновременно служить каждой из вышеупомянутых целей, или он может охватывать два варианта использования, в то время как другой экземпляр адреса охватывает оставшееся.
a В некоторых бизнес-средах заказчиком может быть либофизическое лицо, либо организация (ситуация, которая подразумевает несколько отличную договоренность, как подробно описано в этом ответе о структуре подтипа супертипа), но с целью предоставления упрощенного примера я решил не включать эту возможность здесь. В случае, если вам нужно охватить эту ситуацию в вашей базе данных, пост предыдущей ссылки показывает метод решения указанного требования.
Заказ , адрес , CustomerAddress и адреса Роли
Обычно для заказа требуются только два вида адресов : один для доставки и один для выставления счетов . Таким образом, один и тот же экземпляр Address может заполнить обе роли для отдельного заказа , но каждая роль отображается соответствующим свойством, то есть ShippingAddressId или BillingAddressId .
Порядок связан с Address через тип ассоциативного объекта CustomerAddress с помощью двух многофункциональных FOREIGN KEY, т.е.
- ( CustomerNumber , ShippingAddressId ) и ( CustomerNumber , BillingAddressId ),
оба указывают на многозначный PRIMARY KEY CustomerAddress, показанный как
- ( CustomerNumber , AddressId )
... который помогает представить бизнес - правило , которое гласит , что (а) заказ экземпляр должно быть связанно исключительно с (б) адрес вхождением ранее связанные с конкретным заказчиком , который сделал что заказ , и никогда с (с) случайным не- Заказчиком - связанный адрес .
История для (1) адреса и (2) ассоциации CustomerAddress
Если вы хотите предоставить возможность изменения адресов информации, то вы должны отслеживать все изменения данных. Таким образом, я изобразил Address как «проверяемый» тип сущности, который поддерживает свою собственную AddressHistory .
Поскольку характер связи между Заказчиком и Адресом также может подвергаться одной или нескольким модификациям, я также изобразил возможность обработки такой ассоциации как «проверяемой» с помощью типа сущности CustomerAddressHistory .
В этом отношении различные факторы рассматриваются в Q & A нет. 1 и Q & A нет. 2 , - о включении временных возможностей в базе данных - действительно актуальны.
Иллюстративная логическая схема SQL-DDL
Следовательно, с точки зрения диаграммы, показанной и объясненной выше, я объявил следующую схему логического уровня (которую вы можете точно адаптировать для удовлетворения своих потребностей):
-- You should determine which are the most fitting
-- data types and sizes for all your table columns
-- depending on your business context characteristics.
-- Also, you should make accurate tests to define the
-- most convenient INDEX strategies based on the exact
-- data manipulation tendencies of your business domain.
-- As one would expect, you are free to utilize
-- your preferred (or required) naming conventions.
CREATE TABLE Customer (
CustomerNumber INT NOT NULL,
SpecificAttribute CHAR(30) NOT NULL,
ParticularAttribute CHAR(30) NOT NULL,
CreatedDateTime DATETIME NOT NULL,
--
CONSTRAINT Customer_PK PRIMARY KEY (CustomerNumber)
);
CREATE TABLE Address (
AddressId INT NOT NULL,
SpecificAttribute CHAR(30) NOT NULL,
ParticularAttribute CHAR(30) NOT NULL,
CreatedDateTime DATETIME NOT NULL,
--
CONSTRAINT Address_PK PRIMARY KEY (AddressId)
);
CREATE TABLE CustomerAddress (
CustomerNumber INT NOT NULL,
AddressId INT NOT NULL,
IsPhysical BIT NOT NULL,
IsShipping BIT NOT NULL,
IsBilling BIT NOT NULL,
IsActive BIT NOT NULL,
CreatedDateTime DATETIME NOT NULL,
--
CONSTRAINT CustomerAddress_PK PRIMARY KEY (CustomerNumber, AddressId),
CONSTRAINT CustomerAddressToCustomer_FK FOREIGN KEY (CustomerNumber)
REFERENCES Customer (CustomerNumber),
CONSTRAINT CustomerAddressToAddress_FK FOREIGN KEY (AddressId)
REFERENCES Address (AddressId)
);
CREATE TABLE MyOrder (
CustomerNumber INT NOT NULL,
OrderNumber INT NOT NULL,
ShippingAddressId INT NOT NULL,
BillingAddressId INT NOT NULL,
SpecificAttribute CHAR(30) NOT NULL,
ParticularAttribute CHAR(30) NOT NULL,
OrderDate DATE NOT NULL,
CreatedDateTime DATETIME NOT NULL,
--
CONSTRAINT Order_PK PRIMARY KEY (CustomerNumber, OrderNumber),
CONSTRAINT OrderToCustomer_FK FOREIGN KEY (CustomerNumber)
REFERENCES Customer (CustomerNumber),
CONSTRAINT OrderToShippingAddress_FK FOREIGN KEY (CustomerNumber, ShippingAddressId)
REFERENCES CustomerAddress (CustomerNumber, AddressId),
CONSTRAINT OrderToBillingAddress_FK FOREIGN KEY (CustomerNumber, BillingAddressId)
REFERENCES CustomerAddress (CustomerNumber, AddressId)
);
CREATE TABLE AddressHistory (
AddressId INT NOT NULL,
AuditedDateTime DATETIME NOT NULL,
SpecificAttribute CHAR(30) NOT NULL,
ParticularAttribute CHAR(30) NOT NULL,
CreatedDateTime DATETIME NOT NULL,
--
CONSTRAINT AddressHistory_PK PRIMARY KEY (AddressId, AuditedDateTime),
CONSTRAINT AddressHistoryToAddress_FK FOREIGN KEY (AddressId)
REFERENCES Address (AddressId)
);
CREATE TABLE CustomerAddressHistory (
CustomerNumber INT NOT NULL,
AddressId INT NOT NULL,
AuditedDateTime DATETIME NOT NULL,
IsPhysical BIT NOT NULL,
IsShipping BIT NOT NULL,
IsBilling BIT NOT NULL,
IsActive BIT NOT NULL,
CreatedDateTime DATETIME NOT NULL,
--
CONSTRAINT CustomerAddressHistory_PK PRIMARY KEY (CustomerNumber, AddressId, AuditedDateTime),
CONSTRAINT CustomerAddressHistoryToCustomerAddress_FK FOREIGN KEY (CustomerNumber, AddressId)
REFERENCES CustomerAddress (CustomerNumber, AddressId)
);
Если вы хотите посмотреть, я протестировал его в этой скрипте db <>, которая работает на SQL Server 2017.
В History
таблицах
Следующая выдержка из вашего вопроса очень важна:
То, что я ищу, - это то, как я могу настроить свои адреса, чтобы при их редактировании на заказ не влиял тот факт, что клиент обновляет свой адрес или перемещает его.
AddressHistory
И CustomerAddressHistory
столы помощи в обеспечении , что заказ не зависят от адресных изменений, так как все «предыдущие» строки должны быть сохранены в соответствующей History
таблице и могут быть запрошены при необходимости. Операции UPDATE и DELETE в этих двух таблицах должны быть запрещены (попытки изменить историю могут даже иметь негативные юридические последствия).
Интервал охватывает между значениями , вложенных в AddressHistory.CreatedDateTime
и AddressHistory.AuditedDateTime
выступает за весь период , в течение которого некоторая «прошлого» Address
строка была сочтена «присутствует», «текущий» или «эффективный». Аналогичные соображения применимы к CustomerAddressHistory
строкам.
CustomerAddress.IsActive
Столбец БИТ (булев) предназначаются , чтобы указать, является ли некоторая Address
является «полезной» с помощью строки Customer
строки или нет; например, если для него установлено значение «false», это будет означать, что Клиент больше не использует этот адрес и, следовательно, его нельзя использовать для новых заказов .
Примечание : С другой стороны, я видел некоторые системы , в которых каждый раз, когда новый заказ будет осуществляться в адресной информации необходимо ввести ( в несколько раз повторно), и адрес (адрес) , используемые для последних Заказов никогда не стертые (отсюда что заказы не зависит от адресных изменений).
Этот курс действий может включать значительные объемы избыточности, но есть вероятность, что - в зависимости от точных информационных требований вашей бизнес-сферы - может сработать, поэтому вам также может понравиться оценить ее плюсы и минусы.
Поиск данных
«Присутствует», «текущая» или «эффективная» версия Адреса возникновения должна содержаться в виде строки в Address
таблице, но выбор предыдущих «состояний» в адрес ОТ AddressHistory
(или из CustomerAddressHistory
таблицы) легко, и это может быть интересным упражнением для улучшения ваших навыков кодирования SQL.
Что касается одной из ситуаций, которые вы упомянули в комментариях, если вы хотите получить «от второй до последней версии» отдельной Address
строки из ее AddressHistory
, вы должны принять во внимание MAX(AddressHistory.AuditedDateTime)
то, AddressHistory.AddressId
что соответствует конкретному Address.AddressId
значению под рукой.
В этом отношении - по крайней мере, при создании реляционной базы данных - довольно удобно сначала определить соответствующую концептуальную схему (на основе применимых бизнес-правил ), а затем объявить ее последующее логическое расположение DDL. Как только вы получите стабильные и надежные версии этих основных элементов (которые, конечно, могут развиваться с течением времени), настало время проанализировать и определить лучшие способы манипуляции (с помощью операций INSERT, UPDATE, DELETE и SELECT или их комбинаций) относительно данных.
Восприятие конечных пользователей, взгляды и помощь прикладных программ
Очевидно, что на внешнем уровне абстракции адресная информация воспринимается (конечными пользователями) как часть Ордена , и в этом нет ничего плохого, но это не означает, что разработчики моделей должны проектировать значимые части Ордена. база данных в вопросе, как это. На этом этапе, если есть необходимость, например, напечатать «полный» Заказ (очень выполнимо), вы можете «воспроизвести» его по требованию с помощью нескольких операторов JOIN и предложений WHERE (с учетом соответствующего периода действия и т. д.) может быть зафиксировано в представлениях для будущего потребления, посылая соответствующий набор результатов в соответствующие прикладные программы, которые, в свою очередь, могут при необходимости улучшить его форматирование.
Конечно, прикладная программа (-ы) также будет очень полезна, когда исполняется Орден ; например, окно настольного / мобильного приложения или веб-страница могут:
- отображать только тот адрес (а), который вовлеченный клиент определил как «используемый» (через
CustomerAddress.IsActive
);
- составить список всех адресов, которые Клиент включил для выставления счетов (через
CustomerAddress.IsBilling
); и
- сгруппировать все адреса, которые Клиент определил для службы доставки (через
CustomerAddress.IsShipping
);
облегчая таким образом все вовлеченные процессы в GUI (то есть внешний уровень абстракции компьютеризированной системы).
Предлагаемое чтение
Вы запросили (в уже удаленных комментариях) несколько указателей о звуковой литературе по базам данных; Поэтому, как и для теоретического материала, я настоятельно советую вам прочитать всю работу , написанную доктором Е. Ф. Кодда , в премии Тьюринга получателя и, конечно же , единственным виновником в реляционной модели данных (возможно , сейчас более актуальна , чем когда - либо). Этот список включает некоторые из его чрезвычайно влиятельных статей и статей.
Двумя важными работами, которые не включены в вышеупомянутый список, являются, в частности, его лекция ACM Turing Award, озаглавленная « Реляционная база данных: практическая основа для повышения производительности» от 1981 года, и его книга под названием «Реляционная модель для управления базами данных: версия 2» , которая была опубликована в 1990 году.
На переднем плане концептуального проектирования Интегрированное определение для информационного моделирования (IDEF1X) является серьезно рекомендуемой техникой, которая была определена в качестве стандарта в декабре 1993 года Национальным институтом стандартов и технологий США (NIST).
MyOrder.ShippingAddressId
иMyOrder.BillingAddressId
должны ссылатьсяCustomerAddress.AddressId
(а не наAddress.AddressId
); таким образом гарантируется, что Заказ может быть связан исключительно с адресом (ами), ранее связанным с Клиентом, который сделал этот Заказ . Диаграмма предполагает такое расположение, поэтому DDL будет более точным. Спасибо за запрос об этом разъяснении.History
таблицы, должно ли быть то же самое дляAddress
таблицы? Что делать, если Клиент заказывает что-то, а затем меняет только почтовый индекс или город только на одно поле. Мы должны вставить существующий адрес вHistory
и затем сделать новую вставку вAddress
таблицу, верно?Address
строка, которая «присутствовала» до того, как произошло изменение, вставлена вAddressHistory
таблицу, а также (b) ) рассматриваемаяAddress
строка ОБНОВЛЕНА новыми значениями. Было бы выгодно выполнять этот процесс как единую единицу работы внутри транзакции.Этот ответ был составлен из комментариев к вопросу.
Одним из решений будет использование FK для таблицы адресов в таблице заказов. Это позволит вам увидеть адреса, которые использовались для заказа, и отделить адрес от текущего адреса пользователя.
Чтобы сделать это, вам нужно будет вставить новый адрес и связать этот новый адрес с таблицей пользователей. Это означает, что адреса пишутся один раз, и редактирование является иллюзией для конечного пользователя. Вы можете эффективно хранить историю всех адресов, с которыми был связан пользователь, путем перемещения ассоциации из таблицы User в таблицу ассоциации с отметкой времени. Это даст вам историю изменений / адресов и сохранит неизменные данные в таблице адресов.
@MDCCL заявил:
MDCCL также дал обзор о том, как найти текущий адрес для пользователя здесь:
источник