Fatura Adresinin En İyi Uygulamasını Siparişler Tablosunda Saklama


10

Birisi bu kullanıcının CustomerLocation tablosu için cevabını anlamama yardımcı olabilir . Adresler tablosunda adresleri depolamak için gerçekten iyi bir yöntem istiyorum.

Aradığım şey, adreslerimi nasıl ayarlayabileceğim, böylece onları düzenlediğimde, sipariş bir müşterinin adresini güncellemesi veya yerini değiştirmesi gerçeğinden etkilenmez.

Durduğunda şemam şuna benzer:

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

Yanıtlar:


16

Kavramsal olarak konuşursak, iş ortamınızda Düzen ve Adres , yakından ilişkili fikirler olsa da, aslında her biri kendi uygulanabilir özellik (veya öznitelikler) ve kısıtlamalara sahip iki ayrı varlık türüdür.

Bu nedenle, daha önce yorumlarda belirtildiği gibi, @Erik ile aynı fikirdeyim ve veritabanınızın mantıksal düzenini diğer öğeler arasında bildirerek düzenlemelisiniz:

  • Adres bilgilerini tutmak için bir ayrı tablo ;
  • Müşteriye özgü ayrıntıları korumak için bir tablo ;
  • Sipariş veri noktalarını kapsayan bir tablo ; ve
  • Müşteriler ve Adresler arasındaki ilişkiler hakkında gerçekleri içeren bir tablo ;

Aşağıda örnek olacağım gibi.

Açıklayıcı IDEF1X diyagramı

Bir resim bin kelimeye bedeldir, bu yüzden önerim tarafından açılan bazı olasılıkları göstermek için Şekil 1'de gösterilen IDEF1X diyagramını oluşturdum :

Şekil 1 - Müşteriler, Siparişler ve Adresler Açıklayıcı IDEF1X Şeması

Müşteri , Adres ve ilişkilendirmeleri

Gösterildiği gibi, Müşteri a ve Adres varlık türleri arasında çoktan çoğa (M: N) bir kardinalite oranı ile bir ilişki tasvir ettim ; bu yaklaşım gelecekteki esnekliği sağlayacaktır çünkü bildiğiniz gibi bir Müşteri zaman içinde, hatta aynı anda birden fazla Adres tutabilir ve aynı Adres birden fazla Müşteri tarafından paylaşılabilir .

Belirli bir Adres , bire çok (1: M) Müşteriler tarafından çeşitli şekillerde kullanılabilir ; örneğin, Fiziksel olarak tanımlanabilir ve / veya Nakliye ve / veya Faturalandırma için ayarlanabilir . Belki de aynı Adres örneği, yukarıda belirtilen amaçların her birine aynı anda hizmet edebilir veya farklı bir Adres oluşumu geri kalanını kapsarken iki kullanımı kapsayabilir .

a Bazı iş ortamlarında, Müşteri ya bir Kişi ya da bir Kuruluş olabilir (bir süper tip-alt yapı hakkında bu cevapta ayrıntılı olarak açıklandığı gibi biraz farklı bir düzenleme anlamına gelir), ancak basitleştirilmiş bir örnek sunmak amacıyla karar verdim. bu olasılığı buraya dahil etmemek. Veritabanınızdaki bu durumu kapsamanız gerektiğinde, önceki bağlantının gönderisi söz konusu gereksinimi çözme yöntemini gösterir.

Sipariş , Adres , Müşteri Adresi ve Adres Rolleri

Genellikle, bir Sipariş yalnızca biri Nakliye ve diğeri Faturalandırma için olmak üzere iki tür Adres gerektirir . Bu şekilde, aynı Adres örneği ayrı bir Sipariş için her iki Rolü de doldurabilir , ancak her bir Rol ilgili özellik, yani ShippingAddressId veya BillingAddressId tarafından resmedilir .

Sipariş ile bağlı Adres aracılığı CustomerAddress , yani iki çoklu mülkiyet yabancı anahtarlar, yardımıyla ilişkisel varlık türü

  • ( CustomerNumber , ShippingAddressId ) ve ( CustomerNumber , BillingAddressId ),

her ikisi de gösterilen CustomerAddress çok özellikli PRIMARY KEY

  • ( CustomerNumber , AddressId )

... öngörmektedir ki (a) bir bir iş kuralını temsil etmek yardımcı olur Sipariş örneği (b) ile münhasıran bağlantılı olmalıdır Adres oluşumları önceden belirli ilişkili Müşteri yaptığı Sipariş ve asla rastgele olmayan (c) Müşteri - ilgili Adres .

Geçmiş (1) için adres ve (2) için CustomerAddress birlikte

Adres bilgilerinin değiştirilme olasılığını sağlamak istiyorsanız, tüm veri değişikliklerini takip etmeniz gerekir. Bu şekilde, Adres'i kendi AddressHistory özelliğini koruyan “denetlenebilir” varlık türü olarak tasvir ettim .

Bir Müşteri ile Adres arasındaki bağlantının niteliği de bir veya daha fazla değişikliğe uğrayabileceğinden, CustomerAddressHistory varlık türü sayesinde “denetlenebilir” bir ilişkiyi yönetme olasılığını da gösterdim .

Bu bağlamda, Soru-Cevap no. 1 ve Soru-Cevap no. 2 , —Bir veritabanında zamansal yeteneklerin etkinleştirilmesine dair her ikisi de - gerçekten önemlidir.

Açıklayıcı SQL-DDL mantıksal düzeni

Sonuç olarak, yukarıda gösterilen ve açıklanan diyagram açısından, aşağıdaki mantıksal seviye düzenlemesini beyan ettim (ihtiyaçlarınızı tam olarak karşılayacak şekilde uyarlayabilirsiniz):

-- 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)
);

Bir göz atmak istiyorsanız , SQL Server 2017'de çalışan bu db <> kemanında test ettim .

Historytablolar

Sorunuzdan aşağıdaki alıntı çok önemlidir:

Aradığım şey, adreslerimi nasıl ayarlayabileceğim, böylece onları düzenlediğimde, sipariş bir müşterinin adresini güncellemesi veya yerini değiştirmesinden etkilenmez.

AddressHistoryVe CustomerAddressHistorybir sağlamada tabloları yardım al etkilenmez Adres bütün “önceki” satırları, ilgili saklanacaktır gerektiği gibi, değişiklikleri Historymasaya ve gerektiğinde sorular sorulabilir. Bu iki tablodaki GÜNCELLEME ve SİL işlemlerinin yasaklanması gerekir (geçmişi değiştirmeye çalışmanın olumsuz yasal sonuçları bile olabilir).

Aralık içinde yer alan değerleri arasında kapsanan AddressHistory.CreatedDateTimeve AddressHistory.AuditedDateTimetüm temsil Dönem belirli bir “son” sırasında Addresssatır, “geçerli” veya “etkili” “mevcut” kabul edildi. Benzer düşünceler CustomerAddressHistorysatırlar için de geçerlidir .

CustomerAddress.IsActiveBİT (boolean) sütunu belirli bir olmadığını işaret etmek anlamına gelir Addresssatır tarafından “kullanılabilir” bir Customersatır ya da; örneğin, 'yanlış' olarak ayarlanırsa, bir Müşterinin bu Adresi artık kullanmadığını ve dolayısıyla yeni Siparişler için kullanılamayacağını bildirir .


Not : Öte yandan, her yeni Sipariş verildiğinde Adres bilgilerinin girilmesi gereken (bazen tekrar tekrar) ve geçmiş Siparişler için kullanılan Adres (ler) in asla silinmediği bazı sistemler gördüm. Siparişleri etkilenmez Adres ) değişiklikleri.

Bu eylem süreci kesinlikle önemli miktarda yedeklilik içerebilir, ancak iş alanınızın kesin bilgi gereksinimlerine bağlı olarak işe yarayabilir, bu nedenle artılarını ve eksilerini de değerlendirmek isteyebilirsiniz.


Veri alma

Bir arasında “mevcut”, “geçerli” veya “etkili” versiyonu Adres bir oluşum içinde, bir satır olarak bulunması gereken Addresstablo, ancak bir önceki “durumları”, seçme Adres DAN AddressHistory(veya CustomerAddressHistory) Tablo kolaydır, ve bunu, SQL kodlama becerilerinizi geliştirmek için ilginç bir egzersiz yapın.

Eğer bir birey “son sürümüne ikinci” almak istiyorsanız, yorum olarak bahsedilen bu durumlardan biri ile ilgili olarak Addressonun GELEN satırda AddressHistory, dikkate almak zorunda MAX(AddressHistory.AuditedDateTime)ve AddressHistory.AddressIdözellikle aynı olduğundan Address.AddressIdel altında değer.

Bu bakımdan - en azından ilişkisel bir veritabanı oluşturulurken - önce ilgili kavramsal şemayı tanımlamak (geçerli iş kurallarına dayalı olarak ) ve daha sonra sonraki mantıksal DDL düzenlemesini beyan etmek oldukça uygundur . Bu temel öğelerin kararlı ve güvenilir sürümlerini elde ettikten sonra (elbette zamanla gelişebilir), manipüle etmenin en iyi yollarını analiz etme ve belirleme zamanıdır (INSERT, UPDATE, DELETE ve SELECT işlemleri veya bunların kombinasyonları aracılığıyla) ilgili veriler.

Son kullanıcıların algılama, görüntüleme ve uygulama programı yardımı

Açıkça görüldüğü gibi , dış soyutlama düzeyinde, Adres bilgileri (son kullanıcılar tarafından) bir Siparişin parçası olarak algılanır ve bununla ilgili yanlış bir şey yoktur, ancak bu, modelcilerin söz konusu veritabanı. Bu noktada, örneğin, “tam” bir Sipariş (çok uygun) yazdırmaya ihtiyaç varsa, birkaç JOIN operatörünün ve WHERE yan tümcelerinin yardımıyla (talep geçerlilik süresi göz önünde bulundurularak) talep üzerine “yeniden” oluşturabilirsiniz. , vb.) gelecekteki tüketim görünümlerinde düzeltilebilir , ilgili sonuç kümesini ilgili uygulama programlarına gönderir ve bu da biçimlendirmesini gerektiği gibi geliştirebilir.

Tabii ki, uygulama programı (ler) bir çok zaman çok yararlı olacaktır al yürürlüğe ediliyor; örneğin, bir masaüstü / mobil uygulama penceresi veya bir web sayfası şunları yapabilir:

  • yalnızca ilgili Müşterinin “kullanılabilir” (üzerinden ) olarak belirlediği Adresleri görüntüler ;CustomerAddress.IsActive
  • Liste hep birlikte Adresleri o Müşteri (yoluyla fatura servisi için sağladı CustomerAddress.IsBilling); ve
  • grup hepsi Adresleri o Müşteri (aracılığıyla hizmet nakliye için tanımladığı CustomerAddress.IsShipping);

bu şekilde GUI'deki tüm ilgili süreçleri kolaylaştırmak (yani bilgisayarlı bir sistemin dış soyutlama seviyesi).


Önerilen Okuma

Sağlam veritabanı literatürü hakkında bazı işaretçiler istediniz (şimdi kaldırıldı yorumlarında); bu nedenle, teorik materyale gelince , Turing Ödülü sahibi Dr. EF Codd ve elbette ilişkisel veri modelinin tek yaratıcısı olan (belki şimdi her zamankinden daha alakalı) Dr. Bu liste , muazzam derecede etkili makalelerini ve makalelerini içermektedir.

Yukarıda adı geçen listede yer almayan iki önemli eser, tam olarak, İlişkisel Veritabanı başlıklı ACM Turing Ödülü Konferansı : 1981'den itibaren Verimlilik için Pratik Bir Temel ve kitabının Veri Tabanı Yönetimi için İlişkisel Model: Sürüm 2 olarak adlandırdığı 1990 yılında.

Açık kavramsal tasarım ön, Bilgi Sistemi için Entegre Tanımı (IDEF1X) ABD tarafından Aralık 1993 yılında bir standart olarak tanımlandı ciddi tavsiye tekniktir Ulusal Standartlar ve Teknoloji Enstitüsü (NIST).


1
Maalesef, gönderinin daha eski olduğunu biliyorum, ancak neden MyOrder'da (örneğin: REFERENCES Address (AddressId)) başvuruyorsunuz? Neden MüşteriAdresi değil?
Shadrix

1
Endişeye gerek yok ve güzel yakalama: Aslında, hem MyOrder.ShippingAddressIdve hem de MyOrder.BillingAddressIdbir referans yapmalı CustomerAddress.AddressId(ve olmamalıdır Address.AddressId); bu şekilde bir Sipariş'in münhasıran daha önce bu Siparişi veren Müşteri ile bağlantılı Adres (ler) ile ilişkilendirilebilmesini sağlar . Diyagram bu düzenlemeyi önerdiğinden, DDL daha doğru olacaktır. Bu açıklamayı talep ettiğiniz için teşekkür ederiz.
MDCCL

2
@Shadrix Bir göz atmak istemeniz durumunda yazıyı yeni düzenledim.
MDCCL

@MDCCL Tabloda GÜNCELLEME ve SİLME dediğinizde, Historytablo için aynı Addressmı olmalı ? Müşteri bir şey sipariş edip yalnızca posta kodunu veya şehri yalnızca bir alanı değiştirirse ne olur. Mevcut adresi tabloya Historyeklemeli ve sonra Addresstabloya yeni bir sayfa eklemeliyiz , değil mi?
Mike Ross

1
OTOH, bir Müşteri belirli bir Adres hakkında bir veya daha fazla bilgiyi değiştirmek isterse , (a) Addressdeğişiklik yapılana kadar “mevcut” ilgili satırın AddressHistorytabloya eklenmesini ve ayrıca (b ) söz konusu Addresssatır yeni değer (ler) ile GÜNCELLENDİR. Bu işlemi bir işlem içinde tek bir iş birimi olarak gerçekleştirmek avantajlı olacaktır.
MDCCL

3

Bu cevap, yorumdan soruya derlendi.

Bir çözüm, sipariş tablosundaki adres tablosuna bir FK kullanmak olacaktır. Bu, sipariş için kullanılan adresleri görmenizi sağlar ve adresi Kullanıcının geçerli adresinden ayırır.

Bu işi yapmak için yeni bir adres girmeniz ve bu yeni adresi Kullanıcı tablosuna bağlamanız gerekir. Bu, adreslerin bir kez yazıldığı ve düzenlemenin son kullanıcı için bir yanılsama olduğu anlamına gelir. İlişkilendirmeyi Kullanıcı tablosundan zaman damgalı bir ilişkilendirme tablosuna taşıyarak, kullanıcının ilişkili olduğu tüm adreslerin geçmişini etkili bir şekilde depolayabilirsiniz. Bu size düzenlemelerin / adreslerin bir geçmişini verir ve Adres tablosundaki değişmez verileri korur.

@MDCCL şunları söyledi:

[ilgili] Siparişle ilgili verileri tutmak için bir tablo ve Adres bilgilerini tutmak için başka bir tablo içeren veritabanı yapınızı düzenlemelisiniz. Ve evet, kesinlikle bu iki varlık türü arasında çoktan çoğa ilişkiyi temsil eden bir tablonuz olabilir. Bir Kullanıcı Adres (ler) in özelliklerini değiştirebiliyorsa, bu tür değişiklikleri takip etmeniz gerekir, bu nedenle karşılık gelenleri etkinleştirmeniz gerekir AddressHistory. Bu gönderi ikinci yönü ile ilgilidir.

MDCCL ayrıca bir kullanıcı için mevcut adresi nasıl bulacağınıza dair bir genel bakış verdi:

Sahip olduğunuz bir Geçmiş tablosunun en son sürümünü almak için MAX(AuditedDateTime), karşılık gelenleri dikkate almanız gerekir AddressId. İlk adım, mümkün olan en iyi kavramsal ve mantıksal düzenlemelerinizi modellemek / tasarlamaktır, ikinci adım ise verilerinizi INSERT, UPDATE, DELETE ve SELECT için uygun yolları bulmaktır.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.