Berbicara secara konseptual, meskipun dalam lingkungan bisnis Anda, Pesanan dan Alamat adalah gagasan yang terkait erat, keduanya pada dasarnya adalah dua jenis entitas yang terpisah, masing-masing dengan sekumpulan properti (atau atribut) yang berlaku sendiri dan kendala.
Oleh karena itu, seperti yang dinyatakan sebelumnya dalam komentar, saya setuju dengan @Erik , dan Anda harus mengatur tata letak logis dari basis data Anda di antara elemen-elemen lain:
- satu meja diskrit untuk menyimpan potongan informasi Alamat ;
- satu meja untuk menyimpan detail khusus Pelanggan ;
- satu tabel untuk menyertakan poin data Pemesanan ; dan
- satu tabel berisi fakta tentang hubungan antara Pelanggan dan Alamat ;
karena saya akan mencontohkan di bawah ini.
Diagram Eksposisi IDEF1X
Sebuah gambar bernilai ribuan kata, jadi saya membuat diagram IDEF1X yang ditunjukkan pada Gambar 1 untuk mengilustrasikan beberapa kemungkinan yang dibuka oleh saran saya:
Pelanggan , Alamat dan asosiasinya
Seperti yang ditunjukkan, saya digambarkan sebuah asosiasi dengan banyak-ke-banyak (M: N) rasio kardinalitas antara jenis entitas Pelanggan yang dan Alamat ; pendekatan ini akan memberikan fleksibilitas di masa depan karena, seperti yang Anda tahu, Pelanggan dapat menyimpan beberapa Alamat dari waktu ke waktu, atau bahkan secara bersamaan, dan Alamat yang sama dapat dibagikan oleh banyak Pelanggan .
Alamat tertentu dapat digunakan dalam beberapa cara oleh Pelanggan satu-ke-banyak (1: M) ; misalnya, dapat didefinisikan sebagai Fisik , dan / atau dapat diatur untuk Pengiriman , dan / atau untuk Penagihan . Mungkin, instance Alamat yang sama dapat melayani masing-masing tujuan yang disebutkan pada saat yang sama, atau mungkin mencakup dua penggunaan sementara kejadian Alamat yang berbeda mencakup yang lainnya.
a Dalam beberapa lingkungan bisnis, Pelanggan dapat berupa Orang atau Organisasi (situasi yang menyiratkan pengaturan yang sedikit berbeda, sebagaimana dirinci dalam jawaban ini tentang struktur subtipe-supertipe) tetapi dengan tujuan memberikan contoh yang disederhanakan, saya memutuskan tidak memasukkan kemungkinan itu di sini. Jika Anda perlu membahas situasi itu dalam basis data Anda, pos tautan sebelumnya menunjukkan metode untuk menyelesaikan persyaratan tersebut.
Peran Pemesanan , Alamat , Alamat Pelanggan , dan Alamat
Umumnya, Pesanan hanya membutuhkan dua jenis Alamat , satu untuk Pengiriman dan satu untuk Penagihan . Dengan cara ini, instance Alamat yang sama dapat mengisi kedua Peran untuk Pesanan individual , tetapi setiap Peran digambarkan oleh masing-masing properti, yaitu, ShippingAddressId atau BillingAddressId .
Pesanan terhubung dengan Alamat melalui jenis entitas asosiatif PelangganAddress dengan bantuan dua KUNCI ASING multi-properti, yaitu,
- ( Nomor Pelanggan , ShippingAddressId ), dan ( Nomor Pelanggan , BillingAddressId ),
keduanya menunjuk ke multi-properti KUNCI PRIMER PelangganAddress ditampilkan sebagai
- ( Nomor Pelanggan , AddressId )
... yang membantu mewakili aturan bisnis yang menetapkan bahwa (a) mesin virtual Order harus dihubungkan secara eksklusif dengan (b) Alamat yang sebelumnya terkait dengan Pelanggan spesifik yang membuat Pesanan itu , dan tidak pernah dengan (c) non- Pelanggan acak - Alamat terkait .
Riwayat untuk (1) Alamat dan untuk (2) asosiasi CustomerAddress
Jika Anda ingin memberikan kemungkinan mengubah informasi kepingan Alamat , maka Anda harus melacak semua perubahan data. Dengan cara ini saya menggambarkan Alamat sebagai tipe entitas yang "dapat diaudit" yang mempertahankan AddressHistory sendiri .
Karena sifat koneksi antara Pelanggan dan Alamat juga dapat mengalami satu atau beberapa modifikasi, saya juga menggambarkan kemungkinan menangani asosiasi seperti yang "dapat diaudit" berdasarkan tipe entitas CustomerAddressHistory .
Dalam hal ini, berbagai faktor dibahas dalam Tanya Jawab no. 1 dan T&J no. 2 , - keduanya tentang mengaktifkan kapabilitas temporal dalam basis data - benar-benar relevan.
Ilustrasi tata letak logis SQL-DDL
Akibatnya, dalam hal diagram yang ditampilkan dan dijelaskan di atas, saya menyatakan pengaturan level logis berikut (yang dapat Anda sesuaikan untuk memenuhi kebutuhan Anda dengan tepat):
-- 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)
);
Jika Anda ingin melihatnya, saya mengujinya di db <> biola yang berjalan pada SQL Server 2017.
The History
tabel
Kutipan dari pertanyaan Anda berikut ini sangat penting:
Yang saya cari adalah bagaimana saya bisa mengatur alamat saya sehingga ketika saya mengeditnya, pesanannya tidak terpengaruh oleh kenyataan bahwa seorang pelanggan memperbarui alamatnya atau pindah.
The AddressHistory
dan CustomerAddressHistory
meja bantuan dalam memastikan bahwa Orde tidak terpengaruh oleh Alamat perubahan, karena semua “sebelumnya” baris harus dipertahankan di masing-masing History
meja dan dapat bertanya bila diperlukan. Operasi UPDATE dan DELETE pada dua tabel ini harus dilarang (mencoba mengubah sejarah bahkan dapat memiliki implikasi hukum negatif).
The Interval mencakup antara nilai tertutup dalam AddressHistory.CreatedDateTime
dan AddressHistory.AuditedDateTime
berdiri untuk seluruh periode di mana sebuah “masa lalu” tertentu Address
berturut-turut dianggap “hadir”, “saat ini” atau “efektif”. Pertimbangan serupa berlaku untuk CustomerAddressHistory
baris.
Kolom CustomerAddress.IsActive
BIT (boolean) dimaksudkan untuk menunjukkan apakah suatu Address
baris tertentu “dapat digunakan” oleh satu Customer
baris atau tidak; mis., jika disetel ke 'false', itu akan menyampaikan fakta bahwa Pelanggan tidak lagi menggunakan Alamat itu dan karenanya tidak dapat digunakan untuk Pesanan baru .
Catatan : Di sisi lain, saya telah melihat beberapa sistem di mana setiap kali Orde baru diberlakukan, informasi Alamat harus dimasukkan (beberapa kali berulang-ulang), dan Alamat yang digunakan untuk Pesanan sebelumnya tidak pernah dihapus (karenanya yang Pesanan tidak terpengaruh oleh Alamat perubahan).
Tindakan ini jelas dapat melibatkan volume redundansi yang substansial, tetapi ada kemungkinan bahwa - tergantung pada persyaratan informasi yang tepat dari domain bisnis Anda - dapat bekerja, jadi Anda mungkin ingin mengevaluasi pro dan kontranya juga.
Penerimaan data
Versi "sekarang", "saat ini" atau "efektif" dari kejadian Alamat harus dimasukkan sebagai baris dalam Address
tabel, tetapi PILIH "status" sebelumnya dari Alamat DARI tabel AddressHistory
(atau dari CustomerAddressHistory
) itu mudah, dan itu mungkin menjadi latihan yang menarik untuk meningkatkan keterampilan coding SQL Anda.
Sehubungan dengan salah satu situasi yang Anda sebutkan dalam komentar, jika Anda ingin mengambil "versi kedua hingga terakhir" dari satu Address
baris dari AddressHistory
, Anda harus mempertimbangkan MAX(AddressHistory.AuditedDateTime)
dan AddressHistory.AddressId
yang cocok dengan Address.AddressId
nilai tertentu yang ada.
Dalam hal ini-setidaknya ketika membangun relasional database-, sangat nyaman untuk pertama menentukan sesuai konseptual skema (berdasarkan berlaku aturan bisnis ) dan setelah itu mendeklarasikan selanjutnya logis pengaturan DDL. Setelah Anda mendapatkan versi yang stabil dan dapat diandalkan dari elemen-elemen fundamental ini (yang, tentu saja, dapat berkembang dari waktu ke waktu), sekarang saatnya untuk menganalisis dan menentukan cara terbaik untuk memanipulasi (melalui INSERT, UPDATE, HAPUS, dan SELECT operasi atau kombinasi daripadanya) tentang data.
Persepsi pengguna akhir, pandangan dan bantuan program aplikasi
Jelas, pada tingkat eksternal abstraksi, informasi alamat dianggap (oleh pengguna akhir) sebagai bagian dari suatu Pesanan , dan tidak ada yang salah dengan itu, tetapi itu tidak berarti bahwa pemodel harus merancang bagian-bagian penting dari database yang dimaksud seperti itu. Pada titik ini, jika perlu, misalnya, mencetak Pesanan "penuh" (sangat layak), Anda dapat "mereproduksi" itu sesuai permintaan dengan bantuan beberapa operator BERGABUNG dan klausa WHERE (mengingat periode validitas yang bersangkutan) , dll.) mungkin diperbaiki dalam pandangan untuk konsumsi di masa mendatang, mengirimkan hasil terkait diatur ke program aplikasi terkait yang, pada gilirannya, dapat meningkatkan formatnya sesuai kebutuhan.
Tentu saja, program aplikasi akan sangat membantu juga ketika Pesanan sedang dilakukan; misalnya, jendela aplikasi desktop / seluler atau halaman web dapat:
- hanya menampilkan Alamat yang telah ditetapkan oleh Pelanggan yang terlibat sebagai "dapat digunakan" (via
CustomerAddress.IsActive
);
- daftar bersama semua Alamat yang telah diaktifkan Pelanggan untuk layanan penagihan (via
CustomerAddress.IsBilling
); dan
- kelompokkan semua Alamat yang telah ditentukan Pelanggan untuk layanan pengiriman (via
CustomerAddress.IsShipping
);
memfasilitasi dengan cara ini semua proses yang terlibat di GUI (yaitu, tingkat eksternal abstraksi sistem komputerisasi).
Bacaan yang disarankan
Anda meminta (dalam komentar yang sekarang dihapus) beberapa petunjuk tentang literatur database suara; Oleh karena itu, seperti untuk teoritis bahan, saya sangat menyarankan bahwa Anda membaca semua karya yang ditulis oleh Dr EF Codd , seorang Turing Award penerima dan, tentu saja, satu-satunya pencetus dari model relasional data (mungkin sekarang lebih relevan daripada sebelumnya). Daftar ini mencakup beberapa artikel dan makalahnya yang sangat berpengaruh.
Dua karya penting yang tidak termasuk dalam daftar tersebut di atas adalah, tepatnya, ACM Turing Award Lecture-nya yang berjudul Relational Database: A Practical Foundation for Productivity , dari 1981, dan bukunya dengan denominasi The Relational Model for Database Management: Versi 2 , yang diterbitkan pada tahun 1990.
Di depan desain konseptual , Definisi Terpadu untuk Pemodelan Informasi (IDEF1X) adalah teknik yang sangat direkomendasikan yang didefinisikan sebagai standar pada bulan Desember 1993 oleh Institut Nasional Standar dan Teknologi (NIST).
MyOrder.ShippingAddressId
danMyOrder.BillingAddressId
harus membuat referensi keCustomerAddress.AddressId
(dan bukan keAddress.AddressId
); dengan cara ini orang memastikan bahwa Pesanan secara eksklusif dapat dikaitkan dengan Alamat yang sebelumnya terhubung dengan Pelanggan yang membuat Pesanan itu . Diagram menyarankan pengaturan ini, sehingga DDL akan lebih akurat. Terima kasih telah meminta klarifikasi itu.History
meja, haruskah itu sama denganAddress
meja? Bagaimana jika Pelanggan memesan sesuatu dan kemudian hanya mengubah kode pos atau kota hanya satu bidang. Kita harus memasukkan alamat yang ada ke dalamHistory
dan kemudian membuat masukkan baru ke dalamAddress
tabel, kan?Address
baris terkait yang "hadir" sampai modifikasi yang terjadi dimasukkan ke dalamAddressHistory
tabel, dan juga bahwa (b )Address
baris yang dimaksud adalah DIPERBARUI dengan nilai baru. Akan menguntungkan untuk melakukan proses ini sebagai satu unit kerja di dalam suatu transaksi.Jawaban ini dikompilasi dari komentar ke pertanyaan.
Salah satu solusinya adalah dengan menggunakan FK ke tabel alamat di tabel pesanan. Itu akan memungkinkan Anda melihat alamat yang digunakan untuk pesanan, dan memisahkan alamat dari alamat Pengguna saat ini.
Untuk membuat ini berfungsi, Anda harus memasukkan alamat baru dan menautkan alamat baru itu ke tabel Pengguna. Ini berarti bahwa alamat ditulis satu kali dan hasil edit adalah ilusi bagi pengguna akhir. Anda dapat secara efektif menyimpan riwayat semua alamat yang dikaitkan dengan pengguna dengan memindahkan asosiasi dari tabel Pengguna ke tabel asosiasi dengan cap waktu. Itu akan memberi Anda sejarah pengeditan / alamat, dan memelihara data yang tidak dapat diubah di tabel Alamat.
@MDCCL menyatakan:
MDCCL juga memberikan ikhtisar tentang cara menemukan alamat saat ini untuk pengguna di sini:
sumber