Apa praktik terbaik untuk memodelkan pewarisan dalam basis data?
Apa trade-off (mis queriability)?
(Saya paling tertarik dengan SQL Server dan .NET, tetapi saya juga ingin memahami bagaimana platform lain mengatasi masalah ini.)
Apa praktik terbaik untuk memodelkan pewarisan dalam basis data?
Apa trade-off (mis queriability)?
(Saya paling tertarik dengan SQL Server dan .NET, tetapi saya juga ingin memahami bagaimana platform lain mengatasi masalah ini.)
Jawaban:
Ada beberapa cara untuk memodelkan pewarisan dalam database. Yang Anda pilih tergantung pada kebutuhan Anda. Berikut ini beberapa opsi:
Table-Per-Type (TPT)
Setiap kelas memiliki meja sendiri. Kelas dasar memiliki semua elemen kelas dasar di dalamnya, dan setiap kelas yang berasal darinya memiliki tabel sendiri, dengan kunci utama yang juga merupakan kunci asing ke tabel kelas dasar; kelas tabel turunan hanya berisi elemen yang berbeda.
Jadi misalnya:
class Person {
public int ID;
public string FirstName;
public string LastName;
}
class Employee : Person {
public DateTime StartDate;
}
Akan menghasilkan tabel seperti:
table Person
------------
int id (PK)
string firstname
string lastname
table Employee
--------------
int id (PK, FK)
datetime startdate
Table-Per-Hierarchy (TPH)
Ada tabel tunggal yang mewakili semua hierarki warisan, yang berarti beberapa kolom mungkin akan jarang. Kolom diskriminator ditambahkan yang memberi tahu sistem apa jenis baris ini.
Dengan kelas-kelas di atas, Anda berakhir dengan tabel ini:
table Person
------------
int id (PK)
int rowtype (0 = "Person", 1 = "Employee")
string firstname
string lastname
datetime startdate
Untuk setiap baris yang bertipe 0 (Orang), tanggal mulai akan selalu nol.
Table-Per-Beton (TPC)
Setiap kelas memiliki tabel yang sepenuhnya terbentuk sendiri tanpa referensi ke tabel lain.
Dengan kelas-kelas di atas, Anda berakhir dengan tabel ini:
table Person
------------
int id (PK)
string firstname
string lastname
table Employee
--------------
int id (PK)
string firstname
string lastname
datetime startdate
Desain database yang tepat tidak seperti desain objek yang tepat.
Jika Anda berencana untuk menggunakan database untuk apa pun selain hanya membuat serial objek Anda (seperti laporan, kueri, penggunaan multi-aplikasi, intelijen bisnis, dll.) Maka saya tidak merekomendasikan segala jenis pemetaan sederhana dari objek ke tabel.
Banyak orang menganggap sebuah baris dalam tabel database sebagai entitas (saya menghabiskan waktu bertahun-tahun memikirkan istilah-istilah itu), tetapi sebuah baris bukanlah entitas. Itu adalah proposisi. Relasi basis data (yaitu, tabel) mewakili beberapa pernyataan fakta tentang dunia. Kehadiran baris mengindikasikan fakta itu benar (dan sebaliknya, ketidakhadirannya menunjukkan fakta itu salah).
Dengan pemahaman ini, Anda dapat melihat bahwa satu tipe dalam program berorientasi objek dapat disimpan di selusin hubungan yang berbeda. Dan berbagai jenis (disatukan oleh warisan, asosiasi, agregasi, atau sama sekali tidak terafiliasi) dapat sebagian disimpan dalam satu hubungan.
Yang terbaik adalah bertanya pada diri sendiri, fakta apa yang ingin Anda simpan, pertanyaan apa yang ingin Anda jawab, laporan apa yang ingin Anda hasilkan.
Setelah desain DB yang tepat dibuat, maka itu adalah masalah sederhana untuk membuat kueri / tampilan yang memungkinkan Anda untuk membuat serial objek Anda ke relasi tersebut.
Contoh:
Dalam sistem pemesanan hotel, Anda mungkin perlu menyimpan fakta bahwa Jane Doe memiliki pemesanan kamar di Seaview Inn untuk 10-12 April. Apakah itu atribut entitas pelanggan? Apakah itu atribut entitas hotel? Apakah ini entitas reservasi dengan properti yang mencakup pelanggan dan hotel? Bisa jadi salah satu atau semua hal itu dalam sistem berorientasi objek. Dalam database, itu bukan hal-hal itu. Ini hanyalah fakta yang tidak jelas.
Untuk melihat perbedaannya, pertimbangkan dua pertanyaan berikut. (1) Berapa banyak pemesanan hotel yang dimiliki Jane Doe untuk tahun depan? (2) Berapa banyak kamar yang dipesan untuk 10 April di Seaview Inn?
Dalam sistem berorientasi objek, kueri (1) adalah atribut entitas pelanggan, dan kueri (2) adalah atribut entitas hotel. Itu adalah objek yang akan mengekspos properti itu di API mereka. (Padahal, jelas mekanisme internal yang digunakan nilai-nilai tersebut dapat melibatkan referensi ke objek lain.)
Dalam sistem basis data relasional, kedua kueri akan memeriksa relasi reservasi untuk mendapatkan nomor mereka, dan secara konseptual tidak perlu repot dengan "entitas" lainnya.
Jadi, dengan mencoba menyimpan fakta tentang dunia — alih-alih mencoba menyimpan entitas dengan atribut — maka basis data relasional yang tepat dibangun. Dan begitu itu dirancang dengan baik, maka pertanyaan berguna yang tidak terbayangkan selama fase desain dapat dengan mudah dibangun, karena semua fakta yang diperlukan untuk memenuhi pertanyaan tersebut ada di tempat yang tepat.
Employment
meja, yang mengumpulkan semua pekerjaan dengan tanggal mulai mereka. Jadi, jika mengetahui tanggal mulai kerja seorang saat Employer
ini adalah penting, itu bisa menjadi kasus penggunaan yang tepat untuk a View
, yang mencakup properti itu dengan menanyakan? (catatan: tampaknya karena '-' tepat setelah nama panggilan saya, saya tidak mendapat pemberitahuan tentang komentar Anda)
Jawaban singkat: Anda tidak.
Jika Anda perlu membuat serial objek Anda, gunakan ORM, atau bahkan sesuatu yang lebih baik seperti activerecord atau prevaylence.
Jika Anda perlu menyimpan data, simpanlah dengan cara yang relasional (berhati-hatilah dengan apa yang Anda simpan, dan perhatikan apa yang dikatakan Jeffrey L Whitledge), tidak ada yang terpengaruh oleh desain objek Anda.
Pola TPT, TPH, dan TPC adalah cara yang Anda gunakan, seperti yang disebutkan oleh Brad Wilson. Tetapi beberapa catatan:
kelas anak yang diwarisi dari kelas dasar dapat dilihat sebagai entitas yang lemah terhadap definisi kelas dasar dalam database, yang berarti mereka bergantung pada kelas dasar mereka dan tidak dapat ada tanpa kelas itu. Saya telah melihat beberapa kali, bahwa ID unik disimpan untuk masing-masing dan setiap tabel anak sambil juga menjaga FK ke tabel induk. Satu FK cukup dan bahkan lebih baik untuk memiliki kaskade on-delete memungkinkan untuk hubungan-FK antara anak dan tabel dasar.
Di TPT, dengan hanya melihat catatan tabel dasar, Anda tidak dapat menemukan kelas anak mana yang diwakili catatan tersebut. Ini kadang-kadang diperlukan, ketika Anda ingin memuat daftar semua catatan (tanpa melakukan select
pada setiap tabel anak). Salah satu cara untuk menangani ini, adalah dengan memiliki satu kolom yang mewakili jenis kelas anak (mirip dengan bidang rowType di TPH), jadi bagaimanapun juga, mencampur TPT dan TPH.
Katakanlah kita ingin mendesain database yang berisi diagram bentuk kelas berikut:
public class Shape {
int id;
Color color;
Thickness thickness;
//other fields
}
public class Rectangle : Shape {
Point topLeft;
Point bottomRight;
}
public class Circle : Shape {
Point center;
int radius;
}
Desain database untuk kelas-kelas di atas bisa seperti ini:
table Shape
-----------
int id; (PK)
int color;
int thichkness;
int rowType; (0 = Rectangle, 1 = Circle, 2 = ...)
table Rectangle
----------
int ShapeID; (FK on delete cascade)
int topLeftX;
int topLeftY;
int bottomRightX;
int bottomRightY;
table Circle
----------
int ShapeID; (FK on delete cascade)
int centerX;
int center;
int radius;
Ada dua jenis warisan yang bisa Anda siapkan dalam DB, tabel per entitas dan tabel per Hierarki.
Tabel per entitas adalah tempat Anda memiliki tabel entitas dasar yang memiliki properti bersama dari semua kelas anak. Anda kemudian memiliki per kelas anak tabel lain masing-masing dengan hanya properti yang berlaku untuk kelas itu. Mereka dihubungkan 1: 1 oleh PK mereka
Tabel per hierarki adalah tempat semua kelas berbagi tabel, dan properti opsional dapat dibatalkan. Mereka juga merupakan bidang diskriminator yang merupakan angka yang menunjukkan jenis yang dimiliki catatan saat ini
SessionTypeID adalah diskriminator
Target per hierarki lebih cepat dicari karena Anda tidak perlu bergabung (hanya nilai diskriminator), sedangkan target per entitas yang perlu Anda lakukan bergabung kompleks untuk mendeteksi apa jenis sesuatu serta retreiuve semua datanya ..
Sunting: Gambar yang saya perlihatkan di sini adalah cuplikan layar dari proyek yang sedang saya kerjakan. Gambar Aset tidak lengkap, karena itu kekosongan itu, tetapi itu terutama untuk menunjukkan bagaimana pengaturannya, bukan apa yang harus dimasukkan ke dalam tabel Anda. Itu terserah anda ;). Tabel sesi berisi informasi sesi kolaborasi virtual, dan dapat terdiri dari beberapa jenis sesi tergantung pada jenis kolaborasi yang terlibat.
Anda akan menormalkan database Anda dan itu sebenarnya akan mencerminkan warisan Anda. Itu mungkin memiliki penurunan kinerja, tapi begitulah dengan normalisasi. Anda mungkin harus menggunakan akal sehat untuk menemukan keseimbangan.
ulangi jawaban utas yang serupa
dalam pemetaan ATAU, pewarisan memetakan ke tabel induk di mana tabel induk dan anak menggunakan pengidentifikasi yang sama
sebagai contoh
create table Object (
Id int NOT NULL --primary key, auto-increment
Name varchar(32)
)
create table SubObject (
Id int NOT NULL --primary key and also foreign key to Object
Description varchar(32)
)
SubObject memiliki hubungan foreign-key dengan Object. saat Anda membuat baris SubObject, Anda harus terlebih dahulu membuat baris Objek dan menggunakan Id di kedua baris
EDIT: jika Anda mencari untuk memodelkan perilaku juga, Anda akan membutuhkan tabel tipe yang mencantumkan hubungan warisan antara tabel, dan menentukan perakitan dan nama kelas yang menerapkan perilaku setiap tabel
sepertinya berlebihan, tetapi itu semua tergantung pada apa Anda ingin menggunakannya!
Menggunakan SQL Alkimia (Python ORM), Anda dapat melakukan dua jenis warisan.
Pengalaman yang pernah saya alami adalah menggunakan meja makan, dan memiliki kolom diskriminan. Sebagai contoh, database Sheep (jangan bercanda!) Menyimpan semua Sheep dalam satu tabel, dan Rams dan Ewes ditangani menggunakan kolom gender dalam tabel itu.
Dengan demikian, Anda dapat meminta semua Domba, dan mendapatkan semua Domba. Atau Anda dapat meminta hanya dengan Ram, dan itu hanya akan mendapatkan Rams. Anda juga dapat melakukan hal-hal seperti memiliki hubungan yang hanya dapat menjadi Ram (yaitu, Sire of a Sheep), dan sebagainya.
Perhatikan bahwa beberapa mesin basis data sudah menyediakan mekanisme pewarisan asli seperti Postgres . Lihatlah dokumentasinya .
Sebagai contoh, Anda akan meminta sistem Person / Karyawan yang dijelaskan dalam respons di atas seperti ini:
/ * Ini menunjukkan nama depan semua orang atau karyawan * / SELECT firstname FROM Person; / * Ini menunjukkan tanggal mulai semua karyawan saja * / SELECT mulai tanggal DARI Karyawan;
Dalam hal itu adalah pilihan basis data Anda, Anda tidak perlu menjadi sangat pintar!