Menurut uraian Anda tentang lingkungan bisnis yang dipertimbangkan, terdapat struktur subtipe-supertipe yang mencakup Item — jenis-supertipe tersebut - dan masing-masing Kategorinya , yaitu Mobil , Kapal , dan Pesawat (bersama dengan dua lagi yang tidak diketahui) - subtipe—.
Saya akan merinci di bawah metode yang akan saya ikuti untuk mengelola skenario ini.
Peraturan bisnis
Untuk mulai menggambarkan skema konseptual yang relevan , beberapa aturan bisnis paling penting yang ditentukan sejauh ini (membatasi analisis hanya pada tiga Kategori yang diungkapkan , untuk menjaga hal-hal sesingkat mungkin) dapat dirumuskan sebagai berikut:
- Seorang Pengguna memiliki nol-satu-atau-banyak Item
- Suatu Item dimiliki oleh persis-satu Pengguna pada saat tertentu
- Suatu Item dapat dimiliki oleh Pengguna satu-ke-banyak pada waktu yang berbeda
- Suatu Item diklasifikasikan oleh tepat satu Kategori
- Suatu Item , setiap saat,
- baik sebuah mobil
- atau Perahu
- atau sebuah Pesawat
Ilustrasi diagram IDEF1X
Gambar 1 menampilkan diagram IDEF1X 1 yang saya buat untuk mengelompokkan formulasi sebelumnya bersama dengan aturan bisnis lain yang tampak relevan:
Supertype
Di satu sisi, Item , supertype, menyajikan properti † atau atribut yang umum untuk semua Kategori , yaitu,
- CategoryCode — ditetapkan sebagai KUNCI LUAR NEGERI (FK) yang mereferensikan Category.CategoryCode dan berfungsi sebagai diskriminator subtipe , yaitu, menunjukkan Kategori subtipe yang tepat dengan mana Item yang diberikan harus terhubung—,
- OwnerId —ditandai sebagai FK yang menunjuk ke User.UserId , tetapi saya memberinya nama peran 2 untuk mencerminkan implikasi khususnya lebih akurat—,
- Foo ,
- Bar ,
- Baz dan
- CreatedDateTime .
Subtipe
Di sisi lain, properti ‡ yang berkaitan dengan setiap Kategori tertentu , yaitu,
- Qux dan Corge ;
- Grault , Garply dan Plugh ;
- Xyzzy , Thud , Wibble dan Flob ;
ditunjukkan dalam kotak subtipe yang sesuai.
Pengidentifikasi
Kemudian, Item.ItemId PRIMARY KEY (PK) telah bermigrasi 3 ke subtipe dengan nama peran yang berbeda, yaitu,
- CarId ,
- BoatId dan
- PlaneId .
Asosiasi yang saling eksklusif
Seperti yang digambarkan, ada hubungan atau hubungan kardinalitas satu-ke-satu (1: 1) antara (a) setiap kejadian supertipe dan (b) contoh subtipe komplementernya.
The subtipe eksklusif simbol menggambarkan fakta bahwa subtipe saling eksklusif, yaitu, beton Barang terjadinya dapat dilengkapi dengan contoh subtipe tunggal saja: salah satu mobil , atau satu Pesawat , atau satu Boat (tidak pernah oleh dua atau lebih).
† , ‡ Saya menggunakan nama placeholder klasik untuk memberi hak beberapa properti tipe entitas, karena denominasi mereka yang sebenarnya tidak disediakan dalam pertanyaan.
Tata letak level logis ekspositori
Akibatnya, untuk membahas desain logis ekspositori, saya menurunkan pernyataan SQL-DDL berikut berdasarkan diagram IDEF1X yang ditampilkan dan dijelaskan di atas:
-- 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 context.
-- As one would expect, you are free to utilize
-- your preferred (or required) naming conventions.
CREATE TABLE UserProfile (
UserId INT NOT NULL,
FirstName CHAR(30) NOT NULL,
LastName CHAR(30) NOT NULL,
BirthDate DATE NOT NULL,
GenderCode CHAR(3) NOT NULL,
Username CHAR(20) NOT NULL,
CreatedDateTime DATETIME NOT NULL,
--
CONSTRAINT UserProfile_PK PRIMARY KEY (UserId),
CONSTRAINT UserProfile_AK1 UNIQUE ( -- Composite ALTERNATE KEY.
FirstName,
LastName,
GenderCode,
BirthDate
),
CONSTRAINT UserProfile_AK2 UNIQUE (Username) -- ALTERNATE KEY.
);
CREATE TABLE Category (
CategoryCode CHAR(1) NOT NULL, -- Meant to contain meaningful, short and stable values, e.g.; 'C' for 'Car'; 'B' for 'Boat'; 'P' for 'Plane'.
Name CHAR(30) NOT NULL,
--
CONSTRAINT Category_PK PRIMARY KEY (CategoryCode),
CONSTRAINT Category_AK UNIQUE (Name) -- ALTERNATE KEY.
);
CREATE TABLE Item ( -- Stands for the supertype.
ItemId INT NOT NULL,
OwnerId INT NOT NULL,
CategoryCode CHAR(1) NOT NULL, -- Denotes the subtype discriminator.
Foo CHAR(30) NOT NULL,
Bar CHAR(30) NOT NULL,
Baz CHAR(30) NOT NULL,
CreatedDateTime DATETIME NOT NULL,
--
CONSTRAINT Item_PK PRIMARY KEY (ItemId),
CONSTRAINT Item_to_Category_FK FOREIGN KEY (CategoryCode)
REFERENCES Category (CategoryCode),
CONSTRAINT Item_to_User_FK FOREIGN KEY (OwnerId)
REFERENCES UserProfile (UserId)
);
CREATE TABLE Car ( -- Represents one of the subtypes.
CarId INT NOT NULL, -- Must be constrained as (a) the PRIMARY KEY and (b) a FOREIGN KEY.
Qux CHAR(30) NOT NULL,
Corge CHAR(30) NOT NULL,
--
CONSTRAINT Car_PK PRIMARY KEY (CarId),
CONSTRAINT Car_to_Item_FK FOREIGN KEY (CarId)
REFERENCES Item (ItemId)
);
CREATE TABLE Boat ( -- Stands for one of the subtypes.
BoatId INT NOT NULL, -- Must be constrained as (a) the PRIMARY KEY and (b) a FOREIGN KEY.
Grault CHAR(30) NOT NULL,
Garply CHAR(30) NOT NULL,
Plugh CHAR(30) NOT NULL,
--
CONSTRAINT Boat_PK PRIMARY KEY (BoatId),
CONSTRAINT Boat_to_Item_FK FOREIGN KEY (BoatId)
REFERENCES Item (ItemId)
);
CREATE TABLE Plane ( -- Denotes one of the subtypes.
PlaneId INT NOT NULL, -- Must be constrained as (a) the PRIMARY KEY and (b) a FOREIGN KEY.
Xyzzy CHAR(30) NOT NULL,
Thud CHAR(30) NOT NULL,
Wibble CHAR(30) NOT NULL,
Flob CHAR(30) NOT NULL,
--
CONSTRAINT Plane_PK PRIMARY KEY (PlaneId),
CONSTRAINT Plane_to_Item_PK FOREIGN KEY (PlaneId)
REFERENCES Item (ItemId)
);
Seperti yang ditunjukkan, tipe superentitas dan masing-masing tipe subentity diwakili oleh tabel dasar yang sesuai .
Kolom CarId
, BoatId
dan PlaneId
, dibatasi sebagai PK dari tabel yang sesuai, membantu dalam mewakili asosiasi konseptual satu-ke-satu tingkat dengan cara batasan FK § yang menunjuk ke ItemId
kolom, yang dibatasi sebagai PK Item
tabel. Ini menandakan bahwa, dalam "pasangan" yang sebenarnya, baik baris supertipe dan subtipe diidentifikasi oleh nilai PK yang sama; dengan demikian, lebih dari cukup untuk menyebutkan hal itu
- (a) melampirkan kolom tambahan untuk menyimpan nilai pengganti yang dikontrol sistem ‖ ke (b) tabel yang berdiri untuk subtipe adalah (c) sepenuhnya berlebihan .
§ Untuk mencegah masalah dan kesalahan terkait (terutama ASING) definisi kendala KUNCI —situasi yang Anda sebutkan dalam komentar—, sangat penting untuk memperhitungkan ketergantungan-keberadaan yang terjadi di antara berbagai tabel yang ada, seperti dicontohkan dalam urutan deklarasi tabel dalam struktur DDL ekspositori, yang saya berikan dalam SQL Fiddle ini juga.
‖ Misalnya, menambahkan kolom tambahan dengan properti AUTO_INCREMENT ke tabel database yang dibangun di MySQL.
Pertimbangan integritas dan konsistensi
Sangat penting untuk menunjukkan bahwa, dalam lingkungan bisnis Anda, Anda harus (1) memastikan bahwa setiap baris "supertype" setiap saat dilengkapi dengan mitra "subtype" yang sesuai, dan, pada gilirannya, (2) jaminan yang mengatakan Baris "subtype" kompatibel dengan nilai yang terkandung dalam kolom "diskriminator" pada baris "supertype".
Akan sangat elegan untuk menegakkan keadaan seperti itu dengan cara deklaratif tetapi, sayangnya, tidak ada platform SQL utama yang menyediakan mekanisme yang tepat untuk melakukannya, sejauh yang saya tahu. Oleh karena itu, beralih ke kode prosedural dalam TRANSAKSI ASAM sangat nyaman sehingga kondisi ini selalu dipenuhi dalam database Anda. Pilihan lain adalah mempekerjakan PEMICU, tetapi mereka cenderung membuat segala sesuatunya berantakan.
Menyatakan tampilan yang bermanfaat
Memiliki desain logis seperti yang dijelaskan di atas, akan sangat praktis untuk membuat satu tampilan atau lebih, yaitu, tabel turunan yang terdiri dari kolom yang termasuk dua atau lebih dari tabel dasar yang relevan . Dengan cara ini, Anda dapat, misalnya, PILIH langsung dari pandangan tersebut tanpa harus menulis semua GABUNG setiap kali Anda harus mengambil informasi "gabungan".
Contoh data
Dalam hal ini, mari kita katakan bahwa tabel dasar "diisi" dengan data sampel yang ditunjukkan di bawah ini:
--
INSERT INTO UserProfile
(UserId, FirstName, LastName, BirthDate, GenderCode, Username, CreatedDateTime)
VALUES
(1, 'Edgar', 'Codd', '1923-08-19', 'M', 'ted.codd', CURDATE()),
(2, 'Michelangelo', 'Buonarroti', '1475-03-06', 'M', 'michelangelo', CURDATE()),
(3, 'Diego', 'Velázquez', '1599-06-06', 'M', 'd.velazquez', CURDATE());
INSERT INTO Category
(CategoryCode, Name)
VALUES
('C', 'Car'), ('B', 'Boat'), ('P', 'Plane');
-- 1. ‘Full’ Car INSERTion
-- 1.1
INSERT INTO Item
(ItemId, OwnerId, CategoryCode, Foo, Bar, Baz, CreatedDateTime)
VALUES
(1, 1, 'C', 'This datum', 'That datum', 'Other datum', CURDATE());
-- 1.2
INSERT INTO Car
(CarId, Qux, Corge)
VALUES
(1, 'Fantastic Car', 'Powerful engine pre-update!');
-- 2. ‘Full’ Boat INSERTion
-- 2.1
INSERT INTO Item
(ItemId, OwnerId, CategoryCode, Foo, Bar, Baz, CreatedDateTime)
VALUES
(2, 2, 'B', 'This datum', 'That datum', 'Other datum', CURDATE());
-- 2.2
INSERT INTO Boat
(BoatId, Grault, Garply, Plugh)
VALUES
(2, 'Excellent boat', 'Use it to sail', 'Everyday!');
-- 3 ‘Full’ Plane INSERTion
-- 3.1
INSERT INTO Item
(ItemId, OwnerId, CategoryCode, Foo, Bar, Baz, CreatedDateTime)
VALUES
(3, 3, 'P', 'This datum', 'That datum', 'Other datum', CURDATE());
-- 3.2
INSERT INTO Plane
(PlaneId, Xyzzy, Thud, Wibble, Flob)
VALUES
(3, 'Extraordinary plane', 'Traverses the sky', 'Free', 'Like a bird!');
--
Kemudian, tampilan yang menguntungkan adalah yang mengumpulkan kolom dari Item
, Car
dan UserProfile
:
--
CREATE VIEW CarAndOwner AS
SELECT C.CarId,
I.Foo,
I.Bar,
I.Baz,
C.Qux,
C.Corge,
U.FirstName AS OwnerFirstName,
U.LastName AS OwnerLastName
FROM Item I
JOIN Car C
ON C.CarId = I.ItemId
JOIN UserProfile U
ON U.UserId = I.OwnerId;
--
Tentu saja, pendekatan yang sama dapat diikuti sehingga Anda juga dapat memilih "penuh" Boat
dan Plane
informasi langsung dari satu tabel tunggal (yang diturunkan, dalam kasus ini).
Setelah itu -jika Anda tidak keberatan tentang adanya tanda NULL dalam hasil sets- dengan definisi VIEW berikut, Anda dapat, misalnya, “collect” kolom dari tabel Item
, Car
, Boat
, Plane
dan UserProfile
:
--
CREATE VIEW FullItemAndOwner AS
SELECT I.ItemId,
I.Foo, -- Common to all Categories.
I.Bar, -- Common to all Categories.
I.Baz, -- Common to all Categories.
IC.Name AS Category,
C.Qux, -- Applies to Cars only.
C.Corge, -- Applies to Cars only.
--
B.Grault, -- Applies to Boats only.
B.Garply, -- Applies to Boats only.
B.Plugh, -- Applies to Boats only.
--
P.Xyzzy, -- Applies to Planes only.
P.Thud, -- Applies to Planes only.
P.Wibble, -- Applies to Planes only.
P.Flob, -- Applies to Planes only.
U.FirstName AS OwnerFirstName,
U.LastName AS OwnerLastName
FROM Item I
JOIN Category IC
ON I.CategoryCode = IC.CategoryCode
LEFT JOIN Car C
ON C.CarId = I.ItemId
LEFT JOIN Boat B
ON B.BoatId = I.ItemId
LEFT JOIN Plane P
ON P.PlaneId = I.ItemId
JOIN UserProfile U
ON U.UserId = I.OwnerId;
--
Kode tampilan di sini hanya ilustrasi. Tentu saja, melakukan beberapa latihan pengujian dan modifikasi dapat membantu mempercepat eksekusi (fisik) dari pertanyaan yang ada. Selain itu, Anda mungkin perlu menghapus atau menambahkan kolom ke tampilan kata karena kebutuhan bisnis mendikte.
Sampel data dan semua definisi tampilan dimasukkan ke dalam SQL Fiddle ini sehingga mereka dapat diamati “beraksi”.
Manipulasi data: Kode aplikasi dan alias kolom program
Penggunaan kode program aplikasi (jika itu yang Anda maksud dengan "kode spesifik sisi server") dan alias kolom adalah poin penting lainnya yang Anda kemukakan dalam komentar berikutnya:
Saya berhasil mengatasi masalah [suatu GABUNG] dengan kode khusus sisi server, tetapi saya benar-benar tidak ingin melakukan itu -dan- Menambahkan alias ke semua kolom mungkin "menekankan".
Dijelaskan dengan sangat baik, terima kasih banyak. Namun, seperti yang saya duga, saya harus memanipulasi set hasil ketika mendaftar semua data karena kesamaan dengan beberapa kolom, karena saya tidak ingin menggunakan beberapa alias untuk menjaga pernyataan tetap bersih.
Adalah tepat untuk menunjukkan bahwa sementara menggunakan kode program aplikasi adalah sumber daya yang sangat pas untuk menangani fitur presentasi (atau grafis) dari set hasil, menghindari pengambilan data pada basis per baris sangat penting untuk mencegah masalah kecepatan eksekusi. Tujuannya adalah untuk “mengambil” set data terkait dalam toto melalui instrumen manipulasi data yang kuat yang disediakan oleh engine set (tepatnya) dari platform SQL sehingga Anda dapat mengoptimalkan perilaku sistem Anda.
Lebih jauh lagi, menggunakan alias untuk mengganti nama satu atau lebih kolom dalam lingkup tertentu mungkin tampak menekan tetapi, secara pribadi, saya melihat sumber daya tersebut sebagai alat yang sangat kuat yang membantu untuk (i) mengontekstualisasikan dan (ii) mengaburkan makna dan maksud yang dikaitkan dengan hal tersebut. kolom; karenanya, ini adalah aspek yang harus dipertimbangkan dengan seksama sehubungan dengan manipulasi data yang menarik.
Skenario serupa
Anda mungkin juga menemukan bantuan seri posting ini dan grup posting ini yang berisi pendapat saya tentang dua kasus lain yang mencakup asosiasi subtipe-supertipe dengan subtipe yang saling eksklusif.
Saya juga telah mengusulkan solusi untuk lingkungan bisnis yang melibatkan klaster subtipe supertype di mana subtipe tidak saling eksklusif dalam jawaban (yang lebih baru) ini .
Catatan akhir
1 Definisi Integrasi untuk Pemodelan Informasi ( IDEF1X ) adalah teknik pemodelan data yang sangat direkomendasikan yang ditetapkan sebagai standar pada bulan Desember 1993 oleh Institut Nasional Standar dan Teknologi (NIST). Hal ini kokoh didasarkan pada (a) beberapa karya teoritis ditulis oleh satu-satunya pencetus dari model relasional , yaitu, Dr EF Codd ; pada (b) pandangan entitas-hubungan , yang dikembangkan oleh Dr. PP Chen ; dan juga pada (c) Teknik Desain Basis Data Logis, yang dibuat oleh Robert G. Brown.
2 Dalam IDEF1X, nama peran adalah label khusus yang ditetapkan untuk properti FK (atau atribut) untuk mengekspresikan arti yang dipegangnya dalam lingkup jenis entitasnya masing-masing.
3 Standar IDEF1X mendefinisikan migrasi kunci sebagai “Proses pemodelan menempatkan kunci primer dari entitas induk atau generik dalam entitas anak atau kategorinya sebagai kunci asing”.
Item
tabel termasukCategoryCode
kolom. Sebagaimana disebutkan di bagian berjudul “Pertimbangan Integritas dan Konsistensi”:CategoryColumn
pada tabel yang mewakili subtipe (dengan semua implikasi pada logika [misalnya, anomali modifikasi] dan tingkat fisik abstraksi [misalnya, indeks tambahan, struktur lebih besar, dll.)).Mari kita memanggil Produk tabel utama. Ini host atribut yang dibagikan. Lalu katakanlah kita memiliki meja Mobil, meja Pesawat dan meja Perahu. Tiga tabel ini akan memiliki kunci ProductID dengan batasan FK pada baris ID dari tabel Produk. Jika Anda ingin semuanya - bergabunglah dengan mereka. Jika Anda hanya menginginkan mobil, gabung dengan Mobil dengan Produk (atau gabung dengan produk dan mobil, tetapi saya lebih suka untuk selalu menggunakan gabungan sisi kiri).
Ini disebut model data hiearchical. Untuk sejumlah kecil sub-tabel mungkin masuk akal dalam tabel panjang (jutaan produk).
sumber