Зберігання найкращої практики платіжної адреси в таблиці замовлень


10

Чи може хтось допомогти мені зрозуміти відповідь цього користувача за таблицею CustomerLocation . Я дуже хочу хорошого способу зберігання адрес у таблиці замовлень.

Що я шукаю - це те, як я можу налаштувати свої адреси, тому, коли я їх редагую, замовлення не виконується тим, що клієнт оновлює свою адресу або переїжджає.

Як виглядає, моя схема схожа на:

 Person           |EntityID|
 EntityAddress    |EntityID|AddressID|
 Address          |AddressID|AddressType|AddressLine1|AddressLine2|
 Order            |OrderID|BillingAddressID|

Відповіді:


16

Концептуально кажучи, хоча у вашому бізнес-середовищі Порядок та Адреса - це ідеї, тісно пов’язані між собою, вони по суті є двома окремими типами об'єктів, кожен зі своїм набором застосовних властивостей (або атрибутів) та обмежень.

Тому, як було сказано раніше в коментарях, я погоджуюся з @Erik , і вам слід організувати логічний макет вашої бази даних, декларується серед інших елементів:

  • один дискретний стіл , щоб адресна частина інформації;
  • одна таблиця для збереження специфічних для клієнта деталей;
  • одна таблиця для додавання точок даних замовлення ; і
  • одна таблиця, яка містить факти про асоціації між клієнтами та адресами ;

як я поясню нижче.

Діаграма IDEF1X експозиторії

Малюнок вартує тисячі слів, тому я створив діаграму IDEF1X, показану на малюнку 1, щоб проілюструвати деякі можливості, відкриті моєю пропозицією:

Рисунок 1 - Діаграма IDEF1X сховища клієнтів, замовлень та адрес

Замовник , Адреса та їх асоціації

Як було продемонстровано, я зобразив асоціацію з коефіцієнтом кардинальності «багато до багатьох» (M: N) між типами сутності клієнта a та Адреса ; такий підхід забезпечить майбутню гнучкість, оскільки, як відомо, Клієнт може зберігати кілька адрес протягом певного часу, або навіть одночасно, і одну і ту ж адресу можуть надавати декілька клієнтів .

Конкретна адреса може бути використана декількома способами клієнтами від одного до багатьох (1: M) ; наприклад, його можна визначити як фізичний , та / або встановити для доставки та / або для виставлення рахунків . Можливо, той самий екземпляр адреси може одночасно служити кожній із вищезгаданих цілей, або він може охоплювати два використання, тоді як інше входження адреси охоплює решту.

a У деяких бізнес-середовищах Замовник може бути або Особою, або Організацією (ситуація, яка передбачає дещо чітке домовленість, як детально описано у цій відповіді про структуру підтипу підтипу), але з метою надання спрощеного прикладу, я вирішив не включати цю можливість сюди. Якщо вам потрібно висвітлити цю ситуацію у вашій базі даних, пост попереднього посилання показує спосіб вирішення зазначеної вимоги.

Замовлення , адреса , CustomerAddress і адреси Ролі

Зазвичай для замовлення потрібні лише два види адреси , одна для доставки та одна для виставлення рахунків . Таким чином, один і той же екземпляр адреси міг би заповнити обидві ролі для окремого замовлення , але кожну роль зображено відповідною властивістю, тобто ShippingAddressId або BillingAddressId .

Замовлення пов'язаний з адресою через CustomerAddress типу асоціативної сутності за допомогою два мульти-власності іноземних ключів, тобто

  • ( CustomerNumber , ShippingAddressId ) та ( CustomerNumber , BillingAddressId ),

і вказуючи на CustomerAddress декількох властивостей PRIMARY KEY , показаної в

  • ( CustomerNumber , AddressId )

... що допомагає представити ділове правило, яке передбачає, що (a) екземпляр замовлення повинен бути пов'язаний виключно з (b) подіями адреси, які раніше були пов'язані з конкретним замовником, який зробив це замовлення , і ніколи з (c) випадковим не- замовником - пов'язана адреса .

Історія для (1) Адреса та для (2) асоціації CustomerAddress

Якщо ви хочете , щоб надати можливість зміни адресних частин інформації, то ви повинні стежити за все зміни даних. Таким чином я зобразив Адреса як "ревізований" тип сутності, який підтримує власну Історію адреси .

Оскільки характер зв'язку між Клієнтом та Адресою також може зазнати однієї або декількох модифікацій, я також змальовував можливість обробляти таку асоціацію як "ревізійну" в силу типу юридичної особи 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таблиці і можуть бути запитані при необхідності. Операції оновлення та видалення на цих двох таблицях повинні бути заборонені (спроби змінити історію можуть мати навіть негативні юридичні наслідки).

Інтервал охоплює між значеннями , вкладених в 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 або їх комбінації) щодо даних.

Сприйняття, погляди та допомога кінцевих користувачів допомога кінцевих користувачів

Очевидно, що на зовнішньому рівні абстрагування адресна інформація сприймається (кінцевими користувачами) як частина замовлення , і в цьому немає нічого поганого, але це не означає, що модельєрам доводиться проектувати значні частини база даних, про яку йдеться. З цього моменту, якщо є необхідність, наприклад, надрукувати "повне" замовлення (дуже можливо), ви можете "відтворити" його на вимогу за допомогою декількох підключення до операторів та пунктів WHERE (з огляду на термін дії тощо), можливо, зафіксовано у поданні про майбутнє споживання, надсилаючи відповідний набір результатів відповідним програмам, які, у свою чергу, можуть покращити його форматування за необхідності.

Звичайно, додаткові програми також будуть дуже корисними, коли виконується Замовлення ; наприклад, вікно настільних / мобільних додатків або веб-сторінка можуть:

  • відображати лише Адреса (-и), яку залучений Клієнт визнав "корисною" (через CustomerAddress.IsActive);
  • перерахуйте разом усі адреси, які Клієнт включив для отримання рахунків (через CustomerAddress.IsBilling); і
  • згрупуйте всі адреси , визначені Замовником для послуги доставки (через CustomerAddress.IsShipping);

полегшення таким чином всіх залучених процесів у графічному інтерфейсі (тобто зовнішній рівень абстрагування комп'ютеризованої системи).


Пропоноване читання

Ви попросили (зараз видалені коментарі) деякі вказівки про звукову літературу бази даних; Тому, як і для теоретичного матеріалу, я настійно раджу вам прочитати всю роботу , написану доктором Е. Ф. Кодда , в премії Тьюринга одержувача і, звичайно ж , єдиним винуватцем у реляційної моделі даних (можливо , зараз більш актуальна , ніж коли - або). Цей список містить деякі його надзвичайно впливові статті та статті.

Дві важливі роботи, які не містяться у вищезгаданому списку, - це саме його лекція премії ACM Тюрінга під назвою " Реляційна база даних: Практичний фундамент продуктивності" від 1981 р. Та його книга "Реляційна модель управління базами даних: версія 2" , яка була опублікована в 1990 році.

На фронті концептуальної розробки інтегроване визначення інформаційного моделювання (IDEF1X) - це серйозно рекомендована методика, яка була визначена як стандарт у грудні 1993 р. Національним інститутом стандартів і технологій США (NIST).


1
Вибачте, я знаю, що публікація є старшою, але чому ви посилаєтесь (наприклад, на адресу "REFERENCES Address (AddressId)" в MyOrder? Чому б не Адреса клієнта?
Шадрікс

1
Без хвилювань і приємного улову: Власне кажучи, обидва MyOrder.ShippingAddressIdі MyOrder.BillingAddressIdповинні посилатися на CustomerAddress.AddressId(а не на Address.AddressId); таким чином гарантується, що Замовлення може бути виключно пов’язане з адресою, попередньо пов'язаною із Замовником, який зробив це Замовлення . Діаграма пропонує таке розташування, тому DDL буде більш точним. Дякуємо, що просили пояснення.
MDCCL

2
@Shadrix Я щойно відредагував пост, якщо ви хочете подивитися.
MDCCL

@MDCCL Коли ви сказали, що немає оновлення та видалення на Historyстолі, чи повинно це бути і для Addressтаблиці? Що робити, якщо Клієнт замовляє щось, а потім змінює лише поштовий індекс або місто лише на одне поле. Ми повинні вставити існуючу адресу в, Historyа потім зробити нову вставку в Addressтаблицю, правда?
Майк Росс

1
OTOH, якщо Клієнт хоче змінити одну або кілька відомостей про дану Адресу , необхідно переконатися, що (a) відповідний Addressрядок, який був "присутній" до того, як відбулася модифікація, ВКАЗАНО в AddressHistoryтаблицю, а також, що (b ) відповідний Addressрядок ОНОВЛЕНО з новим значенням. Було б вигідно проводити цей процес як єдину одиницю роботи всередині транзакції.
MDCCL

3

Ця відповідь була складена з коментарів до питання.

Одним з рішень було б використання ФК до адресної таблиці в таблиці замовлень. Це дозволить вам побачити адреси, які були використані для замовлення, і від'єднати адресу від поточної адреси Користувача.

Для того, щоб зробити цю роботу, вам доведеться вставити нову адресу та зв’язати цю нову адресу з таблицею Користувача. Це означає, що адреси пишуться один раз, а редагування є ілюзією для кінцевого користувача. Ви можете ефективно зберігати історію всіх адрес, з якими пов’язаний користувач, перемістивши асоціацію з таблиці "Користувач" в таблицю асоціацій із позначкою часу. Це дасть вам історію редагувань / адрес та підтримує незмінні дані в таблиці адрес.

@MDCCL заявив:

[ви повинні] організувати структуру вашої бази даних, яка має одну таблицю для збереження даних, пов’язаних із замовленням, та іншу таблицю для збереження інформації про адресу. І так, ви, безумовно, можете мати таблицю, яка представляє багато-багато-багато відносин між цими двома типами сутності. Якщо Користувач може змінити свої атрибути Адреса (адреси), вам слід буде відслідковувати такі зміни, таким чином ви повинні включити відповідні AddressHistory. Ця публікація пов'язана з останнім аспектом.

MDCCL також представив огляд того, як знайти поточну адресу для користувача тут:

Для того, щоб схопити останню версію наявної у вас таблиці історії, ви повинні врахувати MAX(AuditedDateTime)відповідну інформацію AddressId. Перший крок - моделювання / проектування найкращих можливих концептуальних та логічних домовленостей, другий крок - пошук правильних способів ВСТАВИТИ, ОНОВЛЮВАТИ, ВИДАЛИТИ та ВИБІРИ свої дані.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.