Mengapa model relasional untuk masalah basis data?

61

Saya mendekati proyek di mana saya harus mengimplementasikan database dengan bos saya; kami adalah permulaan yang sangat kecil sehingga lingkungan kerja sangat pribadi.

Dia telah memberi saya salah satu database perusahaan sebelumnya dan itu benar-benar bertentangan dengan apa yang saya diajarkan (dan baca tentang) di sekolah untuk RDBMS. Misalnya, ada seluruh basis data di sini yang terdiri dari satu tabel (per basis data independen). Salah satu tabel itu adalah 20+ kolom dan untuk konteks, berikut adalah beberapa nama kolom dari satu tabel:

lngStoreID | vrStoreName | lngPerusahaanID | vrCompanyName | lngProductID | vrProductName

Intinya adalah bahwa di mana ia harus memiliki tabel individual yang menyimpan data entitas (nama, ukuran, tanggal pembelian, dll.) Ia menyodorkan semuanya dalam satu tabel besar per basis data.

Saya ingin meningkatkan desain ini, tetapi saya tidak yakin mengapa model data yang dinormalisasi dan tersegmentasi dengan baik akan benar-benar meningkatkan produk ini. Sementara saya terbiasa dengan desain database dari perguruan tinggi dan saya mengerti bagaimana melakukannya, saya tidak yakin mengapa ini benar-benar meningkatkan database.

Mengapa skema relasional yang baik meningkatkan database?

8 proton
sumber
33
Satu kata: Normalisasi.
Robert Harvey
9
Tutup pemilih - benarkan diri Anda! :-)
Robbie Dee
6
Merupakan hal yang biasa bagi karyawan baru untuk mengkritik prosedur yang ada tanpa memahami alasan di baliknya, bahkan jika alasan tersebut tidak masuk akal secara teknis. Pertama cari tahu mengapa atasan Anda membangunnya seperti itu. Dia mungkin sangat tahu bahwa itu bukan desain yang baik, tetapi tidak memiliki pengetahuan (atau lebih mungkin, waktu) untuk melakukannya dengan lebih baik. Setiap perubahan yang Anda usulkan mungkin akan diterima lebih positif jika Anda dengan hormat mengakui alasan desain saat ini.
Pedro
5
He [the boss] had given me one of his databases before and it completely went against what I was taught (and read about) in school for RDBMS<- Selamat datang di dunia nyata!
Möoz
5
Saya teringat akan kutipan basis data relasional favorit saya: "Normalisasi sampai sakit, denormalkan sampai berfungsi"
Jake

Jawaban:

70

Argumen kinerja biasanya merupakan argumen yang paling intuitif. Anda terutama ingin menunjukkan bagaimana akan sulit untuk menambahkan indeks yang baik dalam database yang dinormalisasi secara tidak benar (catatan: ada kasus tepi di mana denormalisasi sebenarnya dapat meningkatkan kinerja, tetapi ketika Anda berdua tidak berpengalaman dengan database relasional Anda kemungkinan besar tidak akan dengan mudah lihat kasus ini).

Lainnya adalah argumen ukuran penyimpanan. Tabel denormalized dengan banyak redudansi akan membutuhkan penyimpanan jauh lebih banyak. Ini juga berperan dalam aspek kinerja: semakin banyak data yang Anda miliki, semakin lambat kueri Anda.

Ada juga argumen yang sedikit lebih sulit untuk dipahami, tetapi sebenarnya lebih penting karena Anda tidak dapat menyelesaikannya dengan melemparkan lebih banyak perangkat keras padanya. Itulah masalah konsistensi data. Basis data yang dinormalisasi dengan benar akan menangani dengan sendirinya bahwa produk dengan ID tertentu selalu memiliki nama yang sama. Tetapi dalam database denormalized, inkonsistensi seperti itu mungkin, jadi perhatian khusus harus diambil ketika datang untuk menghindari inkonsistensi, yang akan memakan waktu pemrograman untuk memperbaikinya dan masih akan menyebabkan bug yang akan membebani Anda dengan kepuasan pelanggan.

Philipp
sumber
19
Satu kasus tepi utama untuk denormalisasi adalah pergudangan data , khususnya, jika Anda memiliki sejumlah besar data yang dijamin tidak akan pernah berubah dan Anda ingin menanyakannya lebih cepat dan efisien dengan mengorbankan ruang penyimpanan. Jawaban yang bagus, ini hanya FYI untuk pemula SQL yang tidak yakin mengapa selain 3NF diinginkan.
11
Saya tidak yakin mengapa argumen konsistensi "lebih sulit untuk dipahami." Tampaknya lebih sederhana bagi saya: jika suatu nilai berubah, maka semua salinan dari nilai itu harus diperbarui. Memperbarui satu salinan jauh lebih rentan kesalahan daripada memperbarui ratusan atau ribuan salinan dari data yang sama. Ini berlaku sama baiknya untuk hubungan antar data. (Jika saya memiliki hubungan yang disimpan dua cara, saya harus memperbarui kedua salinan hubungan.) Ini adalah masalah yang sangat umum dalam DB yang didenormalkan; itu sangat sulit untuk mencegah korupsi ini dalam praktek (eksepsi terwujud penggunaan jenis tampilan).
jpmc26
4
Paragraf terakhir itu harus disorot dengan huruf tebal. :-) Tanpa normalisasi, tidak mungkin untuk menjamin integritas data. Mengontrol input hanya pada lapisan Logika Bisnis adalah tugas bodoh karena setiap database non-normal akhirnya menunjukkan beberapa jenis anomali data.
DanK
2
@IsmaelMiguel Praktik biasa adalah bahwa data master seperti ini tidak pernah dihapus dengan keras dari database. Anda hanya menghapusnya dengan menetapkan bendera di atasnya yang menyatakan tidak lagi tersedia. Dalam kasus khusus ini akan menjadi ide bagus untuk memiliki hubungan kunci asing antara produk dan pesanan yang berarti bahwa database akan membuat kesalahan ketika Anda mencoba untuk menghapus produk yang dirujuk oleh pesanan apa pun.
Philipp
24

Saya harus menerapkan database dengan bos saya ...

Menggunakan perangkat lunak Manajemen Basis Data khusus mungkin jauh lebih mudah (maaf; tidak bisa menolak).

lngStoreID | vrStoreName | lngPerusahaanID | vrCompanyName | lngProductID | vrProductName

Jika basis data ini hanya peduli tentang "pencatatan" produk mana yang dijual di mana, kapan dan oleh siapa, maka Anda mungkin dapat meregangkan Definisi "basis data OK" cukup jauh untuk membahasnya. Jika data ini digunakan untuk hal lain, maka itu sangat buruk.

Tapi ...

Apakah aplikasi / pertanyaan yang menggunakan data ini merespons dengan buruk / lambat? Jika tidak, maka tidak ada masalah nyata untuk dipecahkan. Tentu, itu terlihat dan terasa jelek, tetapi jika berhasil maka Anda tidak akan mendapatkan "poin" untuk menyarankannya "bisa" lebih baik.

Jika Anda dapat menemukan gejala yang pasti (yaitu masalah) yang sepertinya disebabkan oleh pemodelan data yang buruk, maka buat prototipe solusi yang lebih baik. Ambil salinan salah satu dari "basis data" ini, normalkan data dan lihat apakah solusi Anda berjalan lebih baik. Jika jauh lebih baik (dan saya sepenuhnya berharap bahwa setiap operasi pembaruan pada data ini akan ditingkatkan secara besar - besaran ) maka kembalilah ke atasan Anda dan tunjukkan pada mereka peningkatannya.

Sangat mungkin untuk membuat kembali "tampilan tabel tunggal" dari data dengan .. well .. Views.

Phill W.
sumber
11
Resistansi terhadap tabel tunggal weltanschauung sering datang dari mereka yang tidak berpengalaman dengan SQL yang tidak mengerti gabungan - terutama yang berkaitan dengan data yang hilang yaitu gabungan luar.
Robbie Dee
6
@RobbieDee Lebih umum, itu dari orang-orang yang telah melihat data yang didenormalkan menjadi rusak dengan menjadi tidak konsisten. Saya adalah orang yang demikian. Saya hanya akan mempertimbangkan struktur seperti ini dalam situasi yang disarankan Phill: ini adalah semacam tabel logging / pelaporan di mana data tidak akan pernah diperbarui atau hanya diperbarui dengan dibersihkan dan sepenuhnya diturunkan dari sumber lain.
jpmc26
2
Bahkan jika aplikasi berkinerja baik dengan database seperti ini, itu masih tidak fleksibel seperti database yang dinormalisasi dengan benar. Jika nama toko atau nama perusahaan berubah, itu harus diperbarui di mana-mana, bukan hanya di meja toko atau perusahaan. Dalam beberapa kasus, itu mungkin sebenarnya yang Anda inginkan (seperti jika data terutama dikumpulkan untuk keperluan arsip), tetapi kami perlu tahu lebih banyak tentang aplikasi spesifik.
Zach Lipton
1
@ Zak: setuju, itu sebabnya log penjualan berpotensi menjadi kasus yang dapat diterima untuk ini. Andaikata Anda ingin setiap penjualan dikaitkan dengan apa pun nama toko itu pada saat penjualan dilakukan, bukan "nama toko saat ini", maka upaya "menormalkan" ini menimbulkan beberapa kerumitan yang cukup besar (karena tabel pencatatan nama toko akan perlu seri dari waktu ke waktu, bukan hanya satu nilai per storeid)
Steve Jessop
Mungkin aturan praktis adalah bahwa jika satu-satunya kompleksitas diperkenalkan oleh normalisasi yang diusulkan adalah bahwa beberapa pertanyaan sekarang perlu bergabung di dalamnya untuk mengambil semua kolom yang mereka perlu laporkan, maka Anda harus menjalankan tidak berjalan untuk membuat perubahan itu: - )
Steve Jessop
14

Mengapa skema relasional yang baik meningkatkan database?

Jawabannya adalah: itu tidak selalu meningkatkan database. Anda harus menyadari bahwa apa yang Anda mungkin ajarkan disebut Bentuk Normal Ketiga .

Formulir lain valid dalam beberapa situasi, yang merupakan kunci untuk menjawab pertanyaan Anda. Contoh Anda terlihat seperti Bentuk Normal Pertama , jika itu membantu Anda merasa lebih baik tentang keadaan saat ini.

Aturan 3NF membangun hubungan di antara data yang "meningkatkan" database:

  1. Mencegah data yang tidak valid masuk ke sistem Anda (jika suatu hubungan 1-ke-1 itu memaksa kesalahan meskipun kode tertulis di atasnya). Jika data Anda konsisten dalam basis data, kecil kemungkinannya akan menghasilkan inkonsistensi di luar basis data Anda.

  2. Ini memberikan cara untuk memvalidasi kode (misalnya hubungan banyak ke satu adalah sinyal untuk membatasi properti / perilaku objek). Saat menulis kode untuk menggunakan database, terkadang programmer memperhatikan struktur data sebagai indikator bagaimana kode mereka seharusnya bekerja. Atau mereka dapat memberikan umpan balik yang bermanfaat jika database tidak cocok dengan kode mereka. (Sayangnya, ini lebih seperti angan-angan.)

  3. Berikan aturan yang dapat secara signifikan membantu Anda mengurangi kesalahan saat membangun database, sehingga Anda tidak membangunnya berdasarkan persyaratan sewenang-wenang yang mungkin datang kapan saja selama masa database. Sebaliknya, Anda secara sistematis mengevaluasi informasi untuk mencapai tujuan tertentu.

  4. Struktur basis data yang tepat mengarah pada peningkatan kinerja dengan menghubungkan data dengan cara yang meminimalkan penyimpanan data, meminimalkan panggilan penyimpanan untuk mengambil data, memaksimalkan sumber daya dalam memori dan / atau meminimalkan penyortiran / manipulasi data untuk dataset tertentu yang Anda miliki, dibandingkan dengan permintaan Anda mengeksekusinya. Tetapi struktur "benar" tergantung pada jumlah data, sifat data, jenis kueri, sumber daya sistem, dll. Dengan normalisasi Anda dapat membuat kinerja lebih buruk (yaitu jika Anda memuat semua data sebagai 1 tabel - bergabung dengan dapat memperlambat sebuah permintaan). Pemrosesan transaksi (OLTP) vs intelijen bisnis (data warehouse) sangat berbeda.

Di perusahaan kecil dengan kumpulan data kecil, Anda mungkin menemukan bahwa tidak ada yang salah dengan keadaannya sekarang. Kecuali, jika Anda tumbuh, akan sulit untuk "memperbaikinya" nanti, karena saat tabel bertambah besar, sistem yang menggunakannya kemungkinan akan semakin lambat.

Biasanya Anda akan ingin menekankan transaksi cepat saat perusahaan tumbuh. Namun, jika Anda menghabiskan waktu untuk proyek ini sekarang alih-alih hal-hal lain yang mungkin perlu perusahaan lebih mendesak, Anda mungkin tidak pernah memiliki masalah itu karena perusahaan Anda tidak pernah benar-benar tumbuh. Itulah "tantangan pra-optimasi" - tempat untuk menghabiskan waktu berharga Anda sekarang.

Semoga berhasil!

Jim
sumber
4
Tidak disebutkan tetapi saya pikir poin penting bagi programmer adalah bahwa mengedit satu "hal" memerlukan pengeditan hanya satu baris daripada harus mengulang seluruh database untuk menemukan dan mengganti satu hal itu.
slebetman
@slebetman Anda seharusnya tidak pernah memiliki loop sisi kode untuk memperbarui beberapa baris dalam satu tabel, terlepas dari apakah itu dinormalisasi. Gunakan WHEREklausa. Tentu saja, ini masih bisa salah, tetapi kecil kemungkinannya dalam situasi normal karena Anda hanya perlu mencocokkan satu baris melalui kunci primer.
jpmc26
@ jpmc26: Dengan mengulang-ulang database yang saya maksud, membangun kueri untuk memperbarui semua baris yang terpengaruh. Terkadang satu WHERE saja cukup. Tetapi saya telah melihat struktur tidak suci yang membutuhkan subselect ke dalam tabel yang sama untuk mendapatkan semua baris yang terpengaruh tanpa mempengaruhi baris yang seharusnya tidak berubah. Saya bahkan telah melihat struktur di mana permintaan tunggal tidak dapat melakukan pekerjaan (entitas yang membutuhkan perubahan berada di kolom yang berbeda tergantung pada baris)
slebetman
Banyak jawaban bagus untuk pertanyaan ini, dan ini tidak terkecuali.
Mike Chamberlain
11

Ada beberapa alasan mengapa menggunakan satu "meja dewa" besar itu buruk. Saya akan mencoba dan mengilustrasikan masalah dengan database contoh yang dibuat. Anggaplah Anda mencoba membuat model acara olahraga. Kami akan mengatakan Anda ingin membuat model game dan tim yang bermain di game-game itu. Desain dengan beberapa tabel mungkin terlihat seperti ini (ini sengaja disederhanakan jadi jangan terjebak di tempat-tempat di mana lebih banyak normalisasi dapat diterapkan):

Teams
Id | Name | HomeCity

Games
Id | StartsAt | HomeTeamId | AwayTeamId | Location

dan database tabel tunggal akan terlihat seperti ini

TeamsAndGames
Id | TeamName | TeamHomeCity | GameStartsAt | GameHomeTeamId | GameAwayTeamId | Location

Pertama, mari kita lihat membuat indeks pada tabel tersebut. Jika saya membutuhkan indeks di kota asal untuk tim, saya bisa menambahkannya ke Teamstabel atau TeamsAndGamestabel dengan mudah. Ingat bahwa setiap kali Anda membuat indeks, yang perlu disimpan di disk di suatu tempat dan diperbarui saat baris ditambahkan ke tabel. Dalam hal Teamstabel ini cukup mudah. Saya memasukkan tim baru, basis data memperbarui indeks. Tapi untuk apa TeamsAndGames? Yah, hal yang sama berlaku dariTeamscontoh. Saya menambahkan tim, indeks diperbarui. Tapi itu juga terjadi ketika saya menambahkan game! Meskipun bidang itu akan menjadi nol untuk game, indeks tetap harus diperbarui dan disimpan di disk untuk game itu. Untuk satu indeks, ini kedengarannya tidak terlalu buruk. Tetapi ketika Anda membutuhkan banyak indeks untuk beberapa entitas yang dijejalkan ke dalam tabel ini, Anda membuang banyak ruang untuk menyimpan indeks dan banyak waktu prosesor memutakhirkannya untuk hal-hal yang tidak berlaku.

Kedua, konsistensi data. Dalam hal menggunakan dua tabel terpisah, saya bisa menggunakan kunci asing dari Gamestabel ke Teamstabel untuk menentukan tim mana yang bermain dalam permainan. Dan dengan asumsi saya membuat HomeTeamIddan AwayTeamIdkolom tidak dapat dibatalkan, basis data akan memastikan bahwa setiap permainan yang saya masukkan memiliki 2 tim dan bahwa tim-tim itu ada di basis data saya. Tapi bagaimana dengan skenario tabel tunggal? Nah, karena ada banyak entitas dalam tabel ini, kolom-kolom itu harus nullable (Anda bisa membuatnya tidak nullable dan mendorong data sampah di sana, tapi itu hanya ide yang mengerikan). Jika kolom tersebut tidak dapat dibatalkan, basis data tidak dapat lagi menjamin bahwa ketika Anda memasukkan game, ia memiliki dua tim.

Tetapi bagaimana jika Anda memutuskan untuk tetap melakukannya? Anda mengatur kunci asing sedemikian rupa sehingga bidang-bidang itu menunjuk kembali ke entitas lain dalam tabel yang sama. Tetapi sekarang database hanya akan memastikan bahwa entitas-entitas itu ada dalam tabel, bukan bahwa mereka adalah tipe yang benar. Anda bisa dengan mudah mengatur GameHomeTeamIdke ID game lain dan database tidak akan mengeluh sama sekali. Jika Anda mencobanya dalam skenario beberapa tabel, basis data akan cocok.

Anda dapat mencoba mengurangi masalah ini dengan mengatakan "baiklah, kami hanya akan memastikan bahwa kami tidak pernah melakukannya dalam kode". Jika Anda yakin dengan kemampuan Anda untuk menulis kode bebas bug pertama kali dan dalam kemampuan Anda untuk memperhitungkan setiap kombinasi aneh dari hal-hal yang mungkin dicoba pengguna, silakan saja. Saya pribadi tidak yakin dengan kemampuan saya untuk melakukan hal-hal itu, jadi saya akan membiarkan database memberi saya jaring pengaman ekstra.

(Ini menjadi lebih buruk jika desain Anda adalah di mana Anda menyalin semua data yang relevan di antara baris daripada menggunakan kunci asing. Setiap ejaan / ketidakkonsistenan data lainnya akan sulit untuk diselesaikan. Bagaimana Anda bisa tahu apakah "Jon" adalah salah mengeja dari "John" "Atau jika itu disengaja (karena mereka adalah dua orang yang terpisah)?)

Ketiga, hampir setiap kolom perlu dibatalkan atau harus diisi dengan data yang disalin atau sampah. Gim tidak perlu TeamNameatau TeamHomeCity. Jadi setiap game membutuhkan semacam placeholder di sana atau perlu dibatalkan. Dan jika itu nullable, database akan dengan senang hati mengambil game tanpa TeamName. Ini juga akan membutuhkan tim tanpa nama, bahkan jika logika bisnis Anda mengatakan itu seharusnya tidak pernah terjadi.

Ada beberapa alasan lain mengapa Anda ingin tabel terpisah (termasuk menjaga kewarasan pengembang). Bahkan ada beberapa alasan mengapa tabel yang lebih besar mungkin lebih baik (denormalisasi kadang-kadang meningkatkan kinerja). Skenario-skenario itu sedikit dan jarang (dan biasanya paling baik ditangani ketika Anda memiliki metrik kinerja untuk menunjukkan bahwa itu benar-benar masalah, bukan indeks yang hilang atau yang lainnya).

Akhirnya, kembangkan sesuatu yang mudah dipelihara. Hanya karena "berfungsi" tidak berarti tidak apa-apa. Mencoba mempertahankan meja dewa (seperti kelas dewa) adalah mimpi buruk. Anda hanya menyiapkan diri Anda untuk kesakitan nanti.

Becuzz
sumber
1
"Tim: Id | Nama | HomeCity". Pastikan skema data Anda tidak membuat aplikasi Anda salah mengklaim bahwa Super Bowl XXXIV dimenangkan oleh LA Rams. Sedangkan SB XXXIV akan muncul dalam permintaan untuk semua kejuaraan yang dimenangkan oleh tim yang saat ini dikenal sebagai LA Rams. Ada "tabel dewa" yang lebih baik dan lebih buruk, dan Anda tentu saja menyajikan yang buruk. Yang lebih baik adalah "ID permainan | nama tim tuan rumah | kota tim tuan rumah | nama tim tandang | tim kota tandang | permainan dimulai dari | dll ...". Yang muncul sebagai upaya pertama untuk memodelkan informasi seperti "New Orleans Saints @ Chicago Bears 1p Eastern".
Steve Jessop
6

Kutipan hari itu: " Teori dan praktik harus sama ... dalam teori "

Tabel dinormalisasi

Tabel hold-it-all unik Anda berisi data yang redundan memiliki satu keuntungan: membuat pelaporan pada barisnya sangat mudah dikodekan dan cepat dijalankan karena Anda tidak harus melakukan penggabungan. Tetapi ini dengan biaya tinggi:

  • Itu memegang salinan hubungan yang berlebihan (misalnya IngCompanyIDdan vrCompanyName). Memperbarui data master mungkin perlu memperbarui lebih banyak baris daripada dalam skema yang dinormalisasi.
  • Ini mencampur semuanya. Anda tidak dapat memastikan kontrol akses yang mudah di tingkat basis data, mis. Memastikan bahwa pengguna A hanya dapat memperbarui informasi perusahaan, dan info produk hanya pengguna B.
  • Anda tidak dapat memastikan aturan konsistensi di tingkat basis data (mis. Kunci utama untuk memastikan bahwa hanya ada satu nama perusahaan untuk id perusahaan).
  • Anda tidak sepenuhnya mendapat manfaat dari pengoptimal DB yang dapat mengidentifikasi strategi akses optimal untuk kueri yang kompleks, mengambil keuntungan dari ukuran tabel yang dinormalisasi dan statistik dari beberapa indeks. Ini mungkin dengan cepat mengimbangi manfaat terbatas menghindari bergabung.

Meja dinormalisasi

Kerugian di atas adalah keuntungan untuk skema yang dinormalisasi. Tentu saja, pertanyaannya mungkin sedikit lebih rumit untuk ditulis.

Singkatnya, skema yang dinormalisasi mengekspresikan struktur dan hubungan yang lebih baik antara data Anda. Saya akan menjadi provokatif dan mengatakan itu adalah perbedaan yang sama dari antara disiplin yang diperlukan untuk menggunakan satu set laci kantor yang dipesan dan kemudahan penggunaan tempat sampah.

Christophe
sumber
5

Saya pikir setidaknya ada dua bagian dari pertanyaan Anda:

1. Mengapa entitas dari tipe yang berbeda tidak disimpan dalam tabel yang sama?

Jawaban paling penting di sini adalah keterbacaan dan kecepatan kode. A SELECT name FROM companies WHERE id = ?jauh lebih mudah dibaca daripada a SELECT companyName FROM masterTable WHERE companyId = ?dan Anda cenderung untuk secara tidak sengaja meminta omong kosong (mis. SELECT companyName FROM masterTable WHERE employeeId = ?Tidak akan mungkin ketika perusahaan dan karyawan disimpan dalam tabel yang berbeda). Adapun kecepatan, data dari tabel database diambil baik dengan membaca tabel penuh secara berurutan, atau dengan membaca dari indeks. Keduanya lebih cepat jika tabel / indeks mengandung lebih sedikit data, dan itu terjadi jika data disimpan dalam tabel yang berbeda (dan Anda hanya perlu membaca salah satu tabel / indeks).

2. Mengapa entitas dari tipe tunggal dipecah menjadi sub-entitas yang disimpan dalam tabel yang berbeda?

Di sini, alasannya adalah sebagian besar untuk mencegah inkonsistensi data. Dengan pendekatan tabel tunggal, untuk sistem manajemen pesanan Anda dapat menyimpan nama pelanggan, alamat pelanggan, dan ID produk dari produk yang dipesan oleh pelanggan sebagai satu kesatuan. Jika pelanggan memesan beberapa produk, Anda akan memiliki beberapa contoh nama dan alamat pelanggan dalam database Anda. Dalam kasus terbaik, Anda baru saja mendapatkan data duplikat di database Anda, yang mungkin memperlambatnya sedikit. Tetapi kasus yang lebih buruk adalah seseorang (atau beberapa kode) melakukan kesalahan ketika data dimasukkan sehingga perusahaan berakhir dengan alamat berbeda di basis data Anda. Ini saja sudah cukup buruk. Tetapi jika Anda menanyakan alamat perusahaan berdasarkan namanya (misSELECT companyAddress FROM orders WHERE companyName = ? LIMIT 1) Anda hanya akan secara sewenang-wenang mendapatkan salah satu dari dua alamat kembali dan bahkan tidak akan menyadari bahwa ada ketidakkonsistenan. Tetapi setiap kali Anda menjalankan kueri, Anda mungkin benar-benar mendapatkan alamat yang berbeda, tergantung pada bagaimana kueri Anda diselesaikan secara internal oleh DBMS. Ini kemungkinan akan merusak aplikasi Anda di tempat lain, dan akar penyebab kerusakan itu akan sangat sulit ditemukan.

Dengan pendekatan multi-tabel, Anda akan menyadari bahwa ada ketergantungan fungsional dari nama perusahaan ke alamat perusahaan (jika perusahaan hanya dapat memiliki satu alamat), Anda akan menyimpan tuple (nama perusahaan, alamat perusahaan) dalam satu tabel (mis. company), dan (productId, companyName) tuple di tabel lain (misalnya order). Sebuah UNIQUEkendala pada companytabel maka bisa menegakkan bahwa setiap perusahaan hanya memiliki satu alamat dalam database Anda sehingga tidak ada inkonsistensi untuk alamat perusahaan yang bisa timbul.

Catatan: dalam praktiknya, untuk alasan kinerja Anda mungkin akan menghasilkan companyId unik untuk setiap perusahaan dan menggunakannya sebagai kunci asing alih-alih menggunakan nama perusahaan secara langsung. Tetapi pendekatan umum tetap sama.

Pengkhayal
sumber
3

TL; DR - Mereka merancang basis data berdasarkan bagaimana mereka diajarkan ketika mereka masih di sekolah.

Saya bisa menulis pertanyaan ini 10 tahun yang lalu. Butuh beberapa waktu untuk memahami mengapa pendahulu saya mendesain database mereka seperti yang mereka lakukan. Anda bekerja dengan seseorang yang:

  1. Memperoleh sebagian besar keterampilan desain basis data mereka menggunakan Excel sebagai basis data atau
  2. Mereka menggunakan praktik terbaik sejak mereka keluar dari sekolah.

Saya tidak menduga itu # 1 karena Anda benar-benar memiliki nomor ID di meja Anda, jadi saya akan menganggap # 2.

Setelah saya keluar dari sekolah, saya bekerja di sebuah toko yang menggunakan AS / 400 (alias IBM i). Saya menemukan beberapa hal aneh dalam cara mereka mendesain database mereka, dan mulai menganjurkan kita membuat perubahan untuk mengikuti bagaimana saya diajarkan bagaimana merancang database. (Saya bodoh saat itu)

Butuh programmer yang lebih tua dan sabar untuk menjelaskan kepada saya mengapa semuanya dilakukan seperti itu. Mereka tidak mengubah skema karena itu akan menyebabkan program yang lebih tua dari saya rusak. Secara harfiah, kode sumber untuk satu program memiliki tanggal pembuatan tahun sebelum saya lahir. Pada sistem yang kami kerjakan, program mereka harus menerapkan semua logika dan operasi yang ditangani oleh perencana kueri basis data Anda. (Anda dapat melihatnya dengan menjalankan EXPLAIN di salah satu pertanyaan Anda)

Dia up-to-date tentang teknik yang saya coba terapkan, tetapi menjaga sistem berjalan lebih penting daripada membuat perubahan "karena itu bertentangan dengan apa yang diajarkan kepada saya". Setiap proyek baru kami mulai memanfaatkan model relasional yang kami bisa. Sayangnya, programmer / konsultan lain dari waktu itu masih mendesain basis data mereka seolah-olah mereka bekerja dengan batasan-batasan sebelumnya dari sistem itu.


Beberapa contoh dari apa yang saya temui yang tidak sesuai dengan model relasional:

  • Tanggal disimpan sebagai angka hari Julian yang mengharuskan bergabung ke tabel tanggal untuk mendapatkan tanggal yang sebenarnya.
  • Tabel yang didenormalkan dengan kolom berurutan dengan tipe yang sama (mis. code1,code2, ..., code20)
  • Kolom CHAR panjang NxM mewakili array N string panjang M.

Alasan saya diberikan untuk keputusan desain itu semua didasarkan pada kendala sistem ketika database pertama kali dirancang.

Tanggal - saya diberitahu bahwa butuh lebih banyak waktu pemrosesan untuk menggunakan fungsi tanggal (yang bulan atau hari atau hari kerja) untuk memproses tanggal daripada yang dibuat untuk membuat tabel setiap tanggal yang mungkin dengan semua informasi itu.

Kolom berurutan dengan jenis yang sama - Lingkungan pemrogramannya memungkinkan program membuat variabel array di atas bagian baris. Dan itu adalah cara yang lebih mudah untuk mengurangi jumlah operasi baca.

NxM Panjang CHAR kolom - Lebih mudah untuk mendorong nilai konfigurasi menjadi satu kolom untuk mengurangi operasi membaca file.

Contoh kurang dipahami dalam C setara dengan mencerminkan lingkungan pemrograman yang mereka miliki:

#define COURSE_LENGTH 4
#define NUM_COURSES 4
#define PERIOD_LENGTH 2

struct mytable {
    int id;
    char periodNames[NUM_COURSES * PERIOD_LENGTH];  // NxM CHAR Column
    char course1[COURSE_LENGTH];
    char course2[COURSE_LENGTH];
    char course3[COURSE_LENGTH];
    char course4[COURSE_LENGTH];
};

...

// Example row
struct mytable row = {.id= 1, .periodNames="HRP1P2P8", .course1="MATH", .course2="ENGL", .course3 = "SCI ", .course4 = "READ"};

char *courses; // Pointer used to access the sequential columns
courses = (char *)&row.course1;


for(int i = 0; i < NUM_COURSES; i++) {

    printf("%d: %.*s -> %.*s\n",i+1, PERIOD_LENGTH, &row.periodNames[PERIOD_LENGTH * i], COURSE_LENGTH,&courses[COURSE_LENGTH*i]);
}

Keluaran

1: SDM -> MATEMATIKA
2: P1 -> ENGL
3: P2 -> SCI
4: P8 -> BACA

Menurut apa yang saya katakan, beberapa di antaranya dianggap praktik terbaik pada saat itu.

Core.B
sumber