tabel referensi diri, baik atau buruk? [Tutup]

37

Mewakili lokasi geografis dalam aplikasi, desain model data yang mendasarinya menyarankan dua opsi yang jelas (atau mungkin lebih?).

Satu tabel dengan referensi mandiri kolom parent_id, uk - london (london parent id = UK id)

atau dua tabel, dengan hubungan satu ke banyak menggunakan kunci asing.

Preferensi saya adalah untuk satu tabel rujukan sendiri karena mudah memungkinkan untuk memperluas ke banyak sub wilayah seperti yang diperlukan.

Secara umum apakah orang membelok dari tabel referensi diri, atau apakah mereka baik-baik saja?

NimChimpsky
sumber

Jawaban:

40

Tidak ada yang salah dengan tabel referensi-sendiri.

Ini adalah pola desain database umum untuk hierarki bersarang (tak terhingga?).

Oded
sumber
@NimChimpsky - Seperti konsep rekursi, ide ini sulit bagi sebagian orang.
Oded
2
(Setidaknya) Oracle bahkan memiliki konstruksi SQL khusus, klausa "MULAI DENGAN - HUBUNGI OLEH", untuk menangani tabel referensi-sendiri.
user281377
1
@ user281377 - Dan SQL Server memperkenalkan hierarchyidtipe tersebut.
Oded
gunakan hibernasi sehingga akan memiliki saus spesialnya sendiri
NimChimpsky
4
@NimChimpsky - Pertimbangkan untuk melihat "Nested Set Model" sebagai alternatif dari kolom "parent_id" juga - ini menyediakan fungsionalitas yang sama, tetapi kinerja yang lebih baik dan permintaan yang lebih mudah untuk mengekstrak hierarki. en.wikipedia.org/wiki/Nested_set_model Seri buku Joe Celko "SQL For Smarties" memiliki beberapa contoh SQL yang bagus tentang kumpulan bersarang.
Keith Palmer Jr.
7

Necromancing.
Jawaban yang benar adalah: itu tergantung pada mesin basis data mana dan pada alat manajemen mana.

Mari kita buat contoh:
Kami memiliki tabel laporan,
dan laporan dapat memiliki orang tua (menupoint, seperti kategori),
dan orang tua itu sendiri dapat memiliki orang tua (mis. Pusat laba),
dan seterusnya ad infinitum.

Contoh paling sederhana dari hubungan rekursif standar, seperti dengan entitas / hierarki referensi-diri.

Tabel SQL-Server yang dihasilkan adalah:

IF  EXISTS (SELECT * FROM sys.foreign_keys WHERE object_id = OBJECT_ID(N'dbo.FK_T_FMS_Reports_T_FMS_Reports') AND parent_object_id = OBJECT_ID(N'dbo.T_FMS_Reports'))
ALTER TABLE dbo.T_FMS_Reports DROP CONSTRAINT FK_T_FMS_Reports_T_FMS_Reports
GO

IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'dbo.T_FMS_Reports') AND type in (N'U'))
DROP TABLE dbo.T_FMS_Reports 
GO



CREATE TABLE dbo.T_FMS_Reports 
( 
     RE_UID uniqueidentifier NOT NULL 
    ,RE_RE_UID uniqueidentifier NULL 
    ,RE_Text nvarchar(255) NULL 
    ,RE_Link nvarchar(400) NULL 
    ,RE_Sort int NOT NULL 
    ,RE_Status int NOT NULL 
    ,PRIMARY KEY CLUSTERED ( RE_UID ) 
); 

GO

ALTER TABLE dbo.T_FMS_Reports  WITH CHECK ADD  CONSTRAINT FK_T_FMS_Reports_T_FMS_Reports FOREIGN KEY(RE_RE_UID) 
REFERENCES dbo.T_FMS_Reports (RE_UID) 
-- ON DELETE CASCADE -- here, MS-SQL has a problem 
GO

ALTER TABLE dbo.T_FMS_Reports CHECK CONSTRAINT FK_T_FMS_Reports_T_FMS_Reports 
GO

Tetapi Anda mendapatkan masalah:
Ketika Anda perlu menghapus menupoint dengan semua submenupoints, Anda TIDAK BISA mengatur delete-cascade, karena Microsoft SQL-Server tidak mendukung penghapusan cascaded rekursif (di sisi lain, PostGreSQL melakukannya [tetapi hanya jika grafik bukan cyclic], sedangkan MySQL sama sekali tidak menyukai struktur tabel ini, karena tidak mendukung CTE rekursif).

Jadi Anda agak menghapus integritas / fungsionalitas penghapusan dengan itu, sehingga wajib untuk menerapkan fungsi tersebut dalam kode Anda sendiri, atau dalam prosedur tersimpan (jika RDBMS Anda mendukung prosedur tersimpan).

Tidak diragukan lagi ini akan meledakkan segala jenis impor / ekspor data dinamis sepenuhnya otomatis, karena Anda tidak dapat menjalankan pernyataan penghapusan untuk semua tabel sesuai dengan hubungan asing-kunci (non-referensi sendiri), juga tidak dapat melakukan pemilihan sederhana * dan buat sisipan untuk setiap baris dalam urutan acak.

Misalnya, ketika Anda membuat skrip INSERT menggunakan SSMS, maka SSMS tidak akan mendapatkan kunci asing, dan karenanya memang membuat pernyataan-penyisipan yang akan menyisipkan entri dengan dependensi, sebelum menyisipkan induk dependensi, yang akan gagal dengan kesalahan , karena kunci asing sudah ada.

Namun, pada sistem manajemen database yang tepat (seperti PostgreSQL), dengan tooling yang tepat, ini seharusnya tidak menjadi masalah. Hanya memahami bahwa hanya karena Anda membayar banyak untuk RDBMS Anda (saya sedang melihat Anda, Microsoft; Oracle =?), Dan / atau tool-belt-nya, itu tidak berarti diprogram dengan benar. Dan begitu juga OpenSource (mis. MySQL) membuat Anda kebal terhadap hal-hal kecil yang luar biasa.

Iblis ada dalam perinciannya, seperti kata pepatah lama.

Sekarang, bukan berarti Anda tidak bisa mengatasi masalah seperti itu, tapi saya benar-benar tidak akan merekomendasikan hal itu, jika sistem Anda akan menjadi kompleks (misalnya 200 + tabel).
Plus, dalam pengaturan komersial yang biasa (seperti yang digambarkan oleh Dilbert), Anda tidak akan diberikan waktu itu.

Pendekatan yang jauh lebih baik, meskipun lebih sulit, akan menjadi tabel penutupan.
Itu akan memiliki bonus tambahan yang juga berfungsi di MySQL.
Setelah Anda menerapkan fungsi penutupan-sekali, Anda akan membuatnya berfungsi di tempat-tempat tambahan dalam waktu singkat.

Kebingungan
sumber
3
1 pada membawa tabel penutupan menjadi perhatian saya (setidaknya terminologi, sudah tahu konsepnya). Ini artikel bagus untuk orang lain yang mungkin tertarik. coderwall.com/p/lixing/closure-tables-for-browsing-trees-in-sql
Outfast Source