Apakah menyimpan daftar terbatas dalam kolom basis data benar-benar buruk?

363

Bayangkan formulir web dengan satu set kotak centang (salah satu atau semuanya dapat dipilih). Saya memilih untuk menyimpannya dalam daftar nilai yang dipisahkan koma yang disimpan dalam satu kolom dari tabel database.

Sekarang, saya tahu bahwa solusi yang tepat adalah membuat tabel kedua dan menormalkan database dengan benar. Itu lebih cepat untuk mengimplementasikan solusi yang mudah, dan saya ingin memiliki bukti konsep aplikasi itu dengan cepat dan tanpa harus menghabiskan terlalu banyak waktu untuk itu.

Saya pikir waktu yang dihemat dan kode yang lebih sederhana sepadan dengan situasi saya, apakah ini pilihan desain yang dapat dipertahankan, atau haruskah saya menormalkannya dari awal?

Lebih banyak konteks, ini adalah aplikasi internal kecil yang pada dasarnya menggantikan file Excel yang disimpan pada folder bersama. Saya juga bertanya karena saya sedang berpikir untuk membersihkan program dan membuatnya lebih bisa dikelola. Ada beberapa hal di sana yang tidak sepenuhnya saya sukai, salah satunya adalah topik dari pertanyaan ini.

Ilmuwan gila
sumber
21
dalam hal ini, mengapa mengganggu database ?, menyimpan dalam file akan dilakukan.
thavan
6
Setuju dengan @thavan. Mengapa bahkan menyimpan data untuk bukti konsep? Setelah bukti lengkap, kemudian tambahkan database dengan benar. Baik Anda melakukan ringan untuk bukti konsep, hanya saja jangan membuat hal-hal yang harus Anda lakukan nanti.
Jeff Davis
1
Dalam Postgres, kolom array harus lebih disukai daripada daftar yang dipisahkan koma. Setidaknya memastikan jenis data yang tepat, tidak memiliki masalah dengan membedakan pembatas dari data aktual dan dapat diindeks secara efisien.
a_horse_with_no_name

Jawaban:

568

Selain melanggar Formulir Normal Pertama karena kelompok nilai berulang yang disimpan dalam satu kolom, daftar yang dipisahkan koma memiliki banyak masalah lain yang lebih praktis:

  • Tidak dapat memastikan bahwa setiap nilai adalah tipe data yang benar: tidak ada cara untuk mencegah 1,2,3, pisang, 5
  • Tidak dapat menggunakan batasan kunci asing untuk menautkan nilai ke tabel pencarian; tidak ada cara untuk menegakkan integritas referensial.
  • Tidak dapat menegakkan keunikan: tidak ada cara untuk mencegah 1,2,3,3,3,5
  • Tidak dapat menghapus nilai dari daftar tanpa mengambil seluruh daftar.
  • Tidak dapat menyimpan daftar lebih lama dari yang cocok di kolom string.
  • Sulit untuk mencari semua entitas dengan nilai yang diberikan dalam daftar; Anda harus menggunakan pemindaian tabel yang tidak efisien. Mungkin harus menggunakan ekspresi reguler, misalnya di MySQL:
    idlist REGEXP '[[:<:]]2[[:>:]]'*
  • Sulit menghitung elemen dalam daftar, atau melakukan kueri agregat lainnya.
  • Sulit untuk menggabungkan nilai ke tabel pencarian yang mereka rujuk.
  • Sulit mengambil daftar dalam urutan yang diurutkan.

Untuk mengatasi masalah ini, Anda harus menulis banyak kode aplikasi, menemukan kembali fungsionalitas yang sudah disediakan RDBMS jauh lebih efisien .

Daftar yang dipisahkan oleh koma cukup salah sehingga saya membuat bab pertama dalam buku saya: SQL Antipatterns: Menghindari Jebakan Pemrograman Basis Data .

Ada saat-saat ketika Anda perlu melakukan denasionalisasi , tetapi seperti yang disebutkan oleh @OMG Ponies , ini adalah kasus pengecualian. “Optimalisasi” non-relasional apa pun menguntungkan satu jenis kueri dengan mengorbankan penggunaan data lainnya, jadi pastikan Anda tahu mana dari kueri Anda yang perlu diperlakukan secara khusus sehingga layak dinasionalisasi.


* MySQL 8.0 tidak lagi mendukung sintaks ekspresi kata-batas ini.

Bill Karwin
sumber
8
ARRAY (dari tipe data apa pun) dapat memperbaiki pengecualian, cukup periksa PostgreSQL: postgresql.org/docs/current/static/arrays.html (@Bill: Buku bagus, harus dibaca untuk pengembang atau dba apa pun)
Frank Heikens
4
+1 tagihan Karwin Jawaban bagus! Poin-poin singkat yang indah. Itu terlihat seperti buku yang bagus juga. Love the cover too +1 NullUserException. Saya sedang dalam proses merancang skema untuk database MySQL untuk menggantikan sistem berbasis teks file datar. Saya telah menemukan beberapa dilema sejauh ini. Jadi buku ini layak dibeli.
therobyouknow
2
Situs pragprog.com terlihat bagus juga: gaya yang bagus, tata letak, bersih ramah pengguna. Ini pasti cukup baru, saya belum bisa membeli ebook mereka di masa lalu. PS. Saya tidak bekerja karena mereka memiliki koneksi dengan penulis. Saya suka merayakan produk, layanan, dan bantuan yang baik ketika saya melihatnya.
therobyouknow
2
Di sisi yang serius, saya akan menambahkan ke daftar Anda: Sulit untuk dicari. Katakan Anda ingin semua catatan yang menyertakan "2". Tentu saja Anda tidak bisa hanya mencari foobar = '2' karena itu akan melewatkannya jika ada nilai lain. Anda tidak dapat mencari foobar seperti '% 2%' karena itu akan mendapatkan hit palsu untuk 12 dan 28 dan seterusnya. Anda tidak dapat mencari foobar seperti '%, 2,%' karena 2 mungkin elemen pertama atau terakhir dari daftar dan karenanya hanya memiliki salah satu dari koma itu.
Jay
2
Saya tahu itu tidak direkomendasikan, tetapi bermain sebagai pendukung setan: sebagian besar dapat dilepas jika ada ui yang menangani keunikan dan tipe data (jika tidak akan terjadi kesalahan atau perilaku buruk), ui tetes dan membuatnya, ada tabel driver di mana nilai berasal dari untuk menjadikannya unik, bidang seperti '% P%' dapat digunakan, nilai menjadi P, R, S, T, menghitung tidak penting, dan pengurutan tidak masalah. Bergantung pada ui, nilai dapat dipisah [] misalnya untuk memeriksa kotak centang dalam daftar dari tabel driver dalam skenario paling tidak umum tanpa harus pergi ke tabel lain untuk mendapatkannya.
jmcclure
44

"Salah satu alasannya adalah kemalasan".

Ini membunyikan bel alarm. Satu-satunya alasan Anda harus melakukan sesuatu seperti ini adalah karena Anda tahu bagaimana melakukannya "dengan cara yang benar" tetapi Anda sampai pada kesimpulan bahwa ada alasan nyata untuk tidak melakukannya dengan cara itu.

Setelah mengatakan ini: jika data yang Anda pilih untuk disimpan dengan cara ini adalah data yang Anda tidak akan pernah perlu bertanya, maka mungkin ada kasus untuk menyimpannya dengan cara yang Anda pilih.

(Beberapa pengguna akan membantah pernyataan di paragraf saya sebelumnya, dengan mengatakan bahwa "Anda tidak akan pernah tahu persyaratan apa yang akan ditambahkan di masa depan." Pengguna ini salah arah atau menyatakan keyakinan agama. Kadang-kadang menguntungkan untuk bekerja dengan persyaratan yang Anda inginkan. miliki sebelum Anda.)

Hammerite
sumber
Saya selalu mendengar beberapa orang mengatakan bahwa "desain saya lebih fleksibel daripada milik Anda" ketika saya menghadapi mereka tentang hal-hal seperti tidak membuat batasan kunci asing, atau menyimpan daftar dalam satu bidang. Bagi saya, fleksibilitas (dalam kasus seperti itu) == tanpa disiplin == kemalasan.
foresightyj
41

Ada banyak pertanyaan pada SO yang bertanya:

  • cara mendapatkan hitungan nilai tertentu dari daftar yang dipisahkan koma
  • cara mendapatkan catatan yang hanya memiliki nilai spesifik 2/3 / etc yang sama dari daftar yang dipisahkan koma

Masalah lain dengan daftar yang dipisahkan koma adalah memastikan nilainya konsisten - menyimpan teks berarti kemungkinan kesalahan ketik ...

Ini semua adalah gejala dari data yang didenormalisasi, dan menyoroti mengapa Anda harus selalu membuat model untuk data yang dinormalisasi. Denormalisasi dapat menjadi optimasi permintaan, untuk diterapkan ketika kebutuhan benar-benar muncul dengan sendirinya .

OMG Ponies
sumber
19

Secara umum apa pun bisa dipertahankan jika memenuhi persyaratan proyek Anda. Ini tidak berarti bahwa orang akan setuju atau ingin mempertahankan keputusan Anda ...

Secara umum, menyimpan data dengan cara ini tidak optimal (mis. Lebih sulit untuk melakukan kueri yang efisien) dan dapat menyebabkan masalah pemeliharaan jika Anda memodifikasi item dalam formulir Anda. Mungkin Anda bisa menemukan jalan tengah dan menggunakan integer yang mewakili satu set flag bit sebagai gantinya?

bobbymcr
sumber
10

Ya, saya akan mengatakan bahwa itu benar-benar buruk. Itu pilihan yang bisa dipertahankan, tetapi itu tidak membuatnya benar atau baik.

Ini merusak bentuk normal pertama.

Kritik kedua adalah bahwa menempatkan hasil input mentah langsung ke dalam database, tanpa validasi atau mengikat sama sekali, membuat Anda terbuka untuk serangan injeksi SQL.

Apa yang Anda sebut kemalasan dan kurangnya pengetahuan SQL adalah hal-hal yang membuat orang baru. Saya akan merekomendasikan meluangkan waktu untuk melakukannya dengan benar dan melihatnya sebagai kesempatan untuk belajar.

Atau biarkan apa adanya dan pelajari pelajaran menyakitkan dari serangan injeksi SQL.

Duffymo
sumber
19
Saya tidak melihat apa pun dalam pertanyaan ini yang menunjukkan bahwa ia rentan terhadap injeksi SQL. Injeksi SQL dan normalisasi basis data adalah topik ortogonal, dan penyimpangan Anda pada injeksi tidak relevan dengan pertanyaan.
Hammerite
5
@ Paul: Dan mungkin sikap yang sama akan menyebabkan dia ditabrak bus ketika dia gagal melihat ke dua arah sebelum menyeberang jalan, tetapi Anda belum memperingatkannya tentang hal itu. Sunting: Saya mengira Anda adalah poster jawaban ini, kesalahan saya.
Hammerite
1
@ Hammerite - ekstrapolasi Anda ke bus tidak masuk akal.
duffymo
4
Ya, itu dimaksudkan untuk menjadi konyol. Konyolnya menggambarkan maksud saya, yaitu tidak masuk akal untuk memperingatkannya terhadap sesuatu yang Anda tidak punya alasan untuk berpikir dia perlu diperingatkan.
Hammerite
1
Ya, saya mengerti. Saya pikir saya punya jauh lebih banyak alasan bahwa peringatan Anda tentang bus.
duffymo
7

Yah saya telah menggunakan daftar pasangan kunci / nilai tab dipisahkan dalam kolom NTEXT di SQL Server selama lebih dari 4 tahun sekarang dan berfungsi. Anda kehilangan fleksibilitas membuat kueri tetapi di sisi lain, jika Anda memiliki perpustakaan yang tetap / derpersists pasangan nilai kunci maka itu bukan ide yang buruk.

Raj
sumber
13
Tidak, itu ide yang mengerikan. Anda telah berhasil lolos begitu saja, tetapi biaya beberapa menit waktu pengembangan Anda telah menyebabkan Anda kehilangan kinerja, fleksibilitas, dan pemeliharaan kode yang buruk.
Paul Tomblin
5
Paul, aku setuju. Tetapi seperti yang saya katakan saya gunakan jika untuk tujuan tertentu, dan itu untuk operasi entri data di mana Anda memiliki banyak jenis formulir. Saya merevisi desain sekarang bahwa saya telah belajar NHibernate tetapi saat itu saya membutuhkan fleksibilitas untuk merancang formulir di ASP.NET dan menggunakan id kotak teks sebagai kunci dalam pasangan kunci / nilai.
Raj
28
+1 hanya untuk melawan downvotes. Memberitahu seseorang yang telah mempertahankan aplikasi selama 4 tahun tentang masalah pemeliharaan agak sombong. Ada sangat sedikit ide "mengerikan" dalam pengembangan sw - kebanyakan hanya gagasan dengan penerapan yang sangat terbatas. Adalah masuk akal untuk memperingatkan orang-orang tentang keterbatasan, tetapi menghukum orang-orang yang telah melakukannya dan hidup melaluinya menganggap saya sebagai sikap yang lebih suci daripada yang dapat saya lakukan tanpanya.
Mark Brackett
7

Saya membutuhkan kolom multi-nilai, itu bisa diimplementasikan sebagai bidang xml

Itu dapat dikonversi menjadi koma yang dibatasi seperlunya

meminta daftar XML dalam server sql menggunakan Xquery .

Dengan menjadi bidang xml, beberapa masalah dapat diatasi.

Dengan CSV: Tidak dapat memastikan bahwa setiap nilai adalah tipe data yang benar: tidak ada cara untuk mencegah 1,2,3, pisang, 5

Dengan XML: nilai dalam tag dapat dipaksa menjadi jenis yang benar


Dengan CSV: Tidak dapat menggunakan batasan kunci asing untuk menautkan nilai ke tabel pencarian; tidak ada cara untuk menegakkan integritas referensial.

Dengan XML: masih menjadi masalah


Dengan CSV: Tidak dapat menegakkan keunikan: tidak ada cara untuk mencegah 1,2,3,3,3,5

Dengan XML: masih menjadi masalah


Dengan CSV: Tidak dapat menghapus nilai dari daftar tanpa mengambil seluruh daftar.

Dengan XML: item tunggal dapat dihapus


Dengan CSV: Sulit untuk mencari semua entitas dengan nilai yang diberikan dalam daftar; Anda harus menggunakan pemindaian tabel yang tidak efisien.

Dengan XML: bidang xml dapat diindeks


Dengan CSV: Sulit untuk menghitung elemen dalam daftar, atau melakukan kueri agregat lainnya. **

Dengan XML: tidak terlalu sulit


Dengan CSV: Sulit untuk menggabungkan nilai ke tabel pencarian yang dirujuk. **

Dengan XML: tidak terlalu sulit


Dengan CSV: Sulit untuk mengambil daftar dalam urutan yang diurutkan.

Dengan XML: tidak terlalu sulit


Dengan CSV: Menyimpan bilangan bulat sebagai string membutuhkan ruang sekitar dua kali lebih banyak daripada menyimpan bilangan bulat biner.

Dengan XML: penyimpanan bahkan lebih buruk daripada csv


Dengan CSV: Ditambah banyak karakter koma.

Dengan XML: tag digunakan sebagai ganti koma


Singkatnya, menggunakan XML mengatasi beberapa masalah dengan daftar yang dibatasi DAN dapat dikonversi ke daftar yang dibatasi sesuai kebutuhan

James A Mohler
sumber
6

Ya, itu adalah yang buruk. Pandangan saya adalah bahwa jika Anda tidak suka menggunakan database relasional kemudian mencari alternatif yang cocok untuk Anda, ada banyak proyek "NOSQL" yang menarik di luar sana dengan beberapa fitur yang sangat canggih.

Robin
sumber
0

Saya mungkin akan mengambil jalan tengah: membuat setiap bidang di CSV menjadi kolom terpisah dalam database, tetapi tidak terlalu khawatir tentang normalisasi (setidaknya untuk saat ini). Pada titik tertentu, normalisasi mungkin menjadi menarik, tetapi dengan semua data dimasukkan ke dalam satu kolom, Anda hampir tidak mendapat manfaat dari menggunakan database sama sekali. Anda perlu memisahkan data menjadi bidang / kolom logis / apa pun yang ingin Anda panggil sebelum dapat memanipulasinya secara bermakna.

Jerry Coffin
sumber
Formulir ini berisi beberapa bidang lagi, ini hanya satu bagian dari formulir (yang tidak saya jelaskan dengan baik dalam pertanyaan).
Mad Scientist
0

Jika Anda memiliki jumlah bidang boolean yang tetap, Anda bisa menggunakan INT(1) NOT NULL(atau BIT NOT NULLjika ada) atau CHAR (0)(nullable) untuk masing-masing. Anda juga bisa menggunakan SET(saya lupa sintaks yang tepat).

Solomon Ucko
sumber
1
INT(1)membutuhkan 4 byte; yang (1)tidak berarti.
Rick James