Kemungkinan manfaat menyimpan banyak nilai dalam satu bidang pada satu baris, bukan sebagai baris terpisah

11

Selama pertemuan mingguan terakhir kami, seseorang yang tidak memiliki pengalaman latar belakang dalam Administrasi Database mengajukan pertanyaan ini:

"Apakah akan ada skenario yang membenarkan penyimpanan data in-line (string) alih-alih beberapa baris?"

Mari kita asumsikan tabel yang disebut countryStatestempat kita ingin menyimpan negara bagian; Saya akan menggunakan USA untuk contoh ini dan tidak akan mencantumkan semua Negara demi kemalasan.

Di sana kita akan memiliki dua kolom; yang satu dipanggil Countrydan yang lainnya dipanggil States. Seperti yang dibahas di sini , dan diusulkan oleh jawaban @ srutzky , PKkode tersebut akan ditentukan oleh ISO 3166-1 alpha-3 .

Meja kami akan terlihat seperti ini:

+---------+-----------------------+-------------------------------------------------------+
| Country | States                | StateName                                             |
+---------+-----------------------+-------------------------------------------------------+
| USA     | AL, CA, FL,OH, NY, WY | Alabama, California, Florida, Ohio, New York, Wyoming |
+---------+-----------------------+-------------------------------------------------------+

Ketika mengajukan pertanyaan yang sama kepada pengembang teman, ia mengatakan bahwa dari sudut pandang ukuran lalu lintas data, ini mungkin berguna, tetapi tidak jika kita perlu memanipulasi data ini. Dalam hal ini harus ada intelijen pada kode aplikasi yang dapat mengubah string ini dalam daftar (misalkan perangkat lunak yang memiliki akses ke tabel ini perlu membuat kotak kombo).

Kami menyimpulkan bahwa model ini tidak terlalu berguna, tetapi saya curiga bahwa mungkin ada cara untuk membuat ini berguna.

Yang ingin saya tanyakan adalah apakah ada di antara Anda yang sudah melihat, mendengar atau melakukan sesuatu seperti ini dengan cara yang benar-benar berfungsi .

Manusia_Setelah Semua
sumber
Sekarang bayangkan Anda memiliki tabel kedua, "penjualan", yang memiliki data untuk setiap penjualan yang terjadi bersama dengan kode negara di mana penjualan terjadi. Bagaimana Anda menulis kueri yang menghasilkan laporan dengan kolom (StateName, TotalSalesAmount)? Sulit kan?
zgguy
Persis. Saya juga tidak setuju dengan model ini. Kami terjebak pada titik mana pun yang kami perlukan untuk memulihkan semua jenis data (atau data yang berguna jika Anda mau).
Human_AfterAll
Skenario yang memungkinkan adalah menyimpan variabel. Toko a;b;c, gunakan ujung depan untuk mengurai string Anda Anda kemudian mendapatkan a, b, cdan membawa pada eksekusi melakukan sesuatu dengan mereka, mungkin ?. Merasa itu mungkin cocok dengan semacam kebutuhan spesifik dengan cara itu ... Setelah dipikir-pikir, tidak. Anda selalu dapat menyimpan ID, Gabung tabel Anda dan buat string gabungan daripada yang dapat mengirim konten ke FE ...
Nelz
Agar adil (bagi saya, setidaknya ;-), saya mengusulkan menggunakan kode negara 2 karakter :-) dalam jawaban lain .
Solomon Rutzky
2
Perhatikan bahwa tidak ada yang ragu untuk menyimpan nilai "Alabama" di kolom daripada memiliki tabel terpisah dengan kolom STATE, N & C untuk "sebutkan nama STATE memiliki karakter ke-C". Karena 1. kita tidak bermaksud untuk menanyakan tentang karakter nama atau 2. kita tidak keberatan memanggil fungsi NTH_CHAR (N, S) mengembalikan "karakter N dari string S" pada setiap baris dengan nama jika kita melakukannya . (Vs BERGABUNG & operator relasional lainnya menghilangkan beberapa baris seperti melalui tabel tambahan.) Ditto untuk bilangan bulat dan NTH_DIGIT (N, I). Itu selalu merupakan panggilan penilaian untuk apa dalam suatu basis data tertentu yang secara atomis berhubungan.
philipxy

Jawaban:

13

Untuk memulainya, judul Pertanyaan saat ini yang mengacu pada "menyimpan data sebagai string, bukan kolom" agak membingungkan. Ketika berbicara tentang menyimpan data sebagai string, bukan sesuatu yang lain, itu biasanya merujuk pada serialisasi segalanya ke format string, bukan tipe data yang benar / kuat (misalnya INTatau DATETIME). Tetapi jika bertanya tentang menyimpan data sebagai beberapa nilai dalam satu bidang sebagai lawan dari baris terpisah, itu sedikit berbeda. Dan agar adil, sementara nilai-nilai gabungan paling mudah dilakukan dengan string, itu juga dapat dilakukan dengan INTdan BINARYmengetik juga, baik dengan sedikit-masking atau dengan cara yang sama memesan posisi tertentu untuk memiliki makna yang berbeda. Karena interpretasi kedua adalah apa yang sebenarnya ditanyakan, berdasarkan pada teks Pertanyaan, mari kita bahas itu.

Dengan kata lain: Tidak. Jika Anda menyimpan poin data aktual maka itu hanya akan menimbulkan rasa sakit (dalam hal kode dan kinerja) karena merupakan komplikasi yang tidak perlu. Jika itu adalah nilai yang hanya akan disimpan sebagai satu unit, diperbarui sebagai satu unit, dan tidak pernah dibongkar dalam basis data, maka itu bisa ok karena secara analog sama dengan menyimpan gambar atau PDF. Jika tidak, setiap upaya untuk mengurai data akan tidak valid menggunakan indeks apa pun (misalnya menggunakan LIKE '%something%', atau CHARINDEX, atau PATINDEX, atau SUBSTRING, dll).

Jika Anda perlu menyimpan nilai terpisah dalam bidang tunggal dari satu baris, maka ada cara yang lebih tepat untuk melakukan itu: XML atau JSON. Ini adalah format yang dapat diuraikan ( XML / JSON ) dan XML bahkan dapat diindeks . Namun idealnya data ini akan disimpan dalam bidang yang diketik dengan benar sehingga dapat benar-benar bermanfaat.

Dan jangan lupa bahwa tujuan RDBMS adalah untuk menyimpan data sedemikian rupa sehingga dapat diambil dan dimanipulasi seefisien mungkin, dalam batasan yang ditentukan oleh kepatuhan ACID . Mengambil nilai-nilai gabungan cukup buruk karena kebutuhan untuk menguraikan nilai-nilai terlebih dahulu, dan itu tidak dapat diindeks. Tetapi memanipulasi sering kali berarti mengganti seluruh gumpalan hanya untuk memperbarui bagiannya (dengan asumsi tidak ada pola yang digunakan dengan REPLACEfungsi). Tipe data XML setidaknya memungkinkan untuk XML DML untuk pembaruan sederhana, meskipun itu masih tidak secepat pembaruan sederhana dari data yang dimodelkan dengan benar.

Juga, mengingat skenario seperti apa yang diperlihatkan dalam Pertanyaan di atas, dengan menggabungkan semua Kode Negara bersama-sama, Anda tidak akan dapat dengan Foreign Key (dalam arah mana pun) nilai-nilai itu.

Dan bagaimana jika persyaratan bisnis berubah seiring waktu dan Anda perlu melacak properti tambahan dari barang-barang ini? Dalam hal "negara bagian", bagaimana dengan ibu kota, atau populasi, atau pengurutan, atau yang lainnya? Disimpan dengan benar sebagai baris, Anda dapat menambahkan lebih banyak kolom untuk properti tambahan. Tentu, Anda dapat memiliki beberapa tingkat data yang dapat diuraikan, seperti |StateCode,Capital,Population |StateCode,Capital,Populate|...tetapi semoga siapa pun dapat melihat masalah tersebut tumbuh secara eksponensial di luar kendali. Tentu saja, masalah khusus ini agak mudah ditangani dengan format XML dan JSON, dan itulah nilainya seperti yang disebutkan di atas. Tetapi Anda masih membutuhkan alasan yang sangat baik untuk menggunakan salah satu dari mereka sebagai sarana awal pemodelan karena tidak akan pernah seefisien menggunakan bidang diskrit dalam baris terpisah.

Solomon Rutzky
sumber
9

Saya sebenarnya menggunakan sesuatu seperti itu untuk tujuan yang sangat terbatas. Kami membuat tabel header untuk file output. Mereka secara khusus dibangun dan sebagian besar hanya judul kolom tetapi tidak cukup. Jadi datanya terlihat seperti

OutputType   OutputHeader
PersonalData Name|Address|City|State|Zip
JobInfo      Name|JobName|JobTitle

Pada dasarnya itu tampak seperti daftar yang dibatasi. Dan dalam satu cara itu. Tetapi untuk tujuan kami, itu adalah string panjang tunggal.

Itulah triknya di sini. Jika Anda tidak pernah berencana untuk menguraikan daftar maka ada baiknya menyimpan daftar. Namun jika Anda akan atau bahkan mungkin perlu menguraikan daftar maka layak ruang ekstra & waktu untuk membaginya dan menyimpannya dalam baris terpisah.

Kenneth Fisher
sumber
1

Saya pernah menggunakannya sekali dengan meja yang agak kecil, misalnya:

CREATE TABLE t1 (
  ID number,
  some_feature   varchar2(100),
  valid_channels  varchar2(100));

CREATE TABLE channel_def (
  channel varchar2(100));

Dan kemudian menyimpan nilai CRM,SMS,SELF-CARE- nilai valid_channel.

Seluruh tabel memiliki sekitar 10 catatan. valid_channelberisi nilai-nilai yang seharusnya berada dalam tabel tautan yang menggambarkan hubungan banyak-ke-banyak. Meja t1tidak akan digunakan secara intensif, jadi kami memutuskan untuk menyusuri jalan ini. Namun, beberapa politik terlibat dalam keputusan ini (lihat di bawah).

Tetapi secara umum saya menghindarinya, ini bukan 3NF.

Tempat saya bekerja saat ini memiliki lusinan kolom seperti itu di semua tempat. Pembenaran mereka adalah bahwa itu membuat kueri mereka lebih mudah: alih-alih bergabung dengan tiga tabel menggunakan tabel penautan mereka bisa langsung menggunakan tabel definisi LIKE. Misalnya

SELECT * 
  FROM t1 
 INNER JOIN channel_def cd
    ON ','||t1.valid_channels||',' LIKE '%,'||cd.channel||',%';

+ Mengerikan pada Oracle menonaktifkan penggunaan indeks karena memulai '%,'.

Robotron
sumber
Mana yang lebih lambat: LIKEatau gabung sederhana?
Human_AfterAll
Yang terbaik adalah memiliki gabungan pada kolom yang diindeks atau setidaknya memiliki batasan referensial (FK) di atasnya. Selain itu, bergabung biasanya dilakukan pada PK dari tabel lain, yang diindeks secara default (setidaknya pada Oracle). Jika Anda bertanya tentang kasus tertentu yang ada (lihat di atas), rencana eksekusi kemungkinan besar akan mengatakan itu sama, karena itu adalah meja kecil.
Robotron
@Human_AfterAll LIKEakan lebih lambat, terutama jika data dimodelkan dengan benar untuk menggunakan TINYINTbidang PK di channel_def. Maka itu hanya perlu membandingkan satu byte antara dua tabel. Di sini ia harus mengurai string, karakter demi karakter (setidaknya sampai kondisi terpenuhi), dan sedang melakukan pencarian case-insensitive (berdasarkan tabel yang diberikan def tidak menunjukkan _BIN2collation sedang digunakan). Ini juga membatalkan indeks pada SQL Server. Saya menjawab ini dengan jawaban saya dengan mengatakan bahwa parsing tidak dapat menggunakan indeks. Saya baru saja memperbarui jawaban saya untuk membuatnya lebih jelas.
Solomon Rutzky
1
@Human_AfterAll Saya akan mengatakan bahwa keputusan pemodelan ini lahir dari kurangnya pengalaman dan pengetahuan (dan terkadang kemalasan). Satu GABUNGAN tambahan adalah semua yang disimpan, tetapi yang dikorbankan adalah kemampuan Foreign Key yang akan mencegah data palsu sepenuhnya masuk (bahkan jika itu tidak cocok dengan LIKEklausa dan menghasilkan hasil yang aneh, itu masih dapat menyebabkan masalah lain atau setidaknya membuat debugging lebih sulit / lebih lama). Itu juga membuat memperbarui valid_channelsbidang lebih rumit. Ini bukan untuk mengatakan bahwa ini tidak bekerja, hanya ada ada yang baik alasan untuk melakukannya.
Solomon Rutzky
"kurang pengalaman" - yang terburuk adalah bahwa keputusan desain khusus ini dipaksakan oleh anggota staf senior ...
Robotron
1

Ini dilakukan di sini di SE. Seperti Marc Gravell menulis :

... Setelah beberapa pemikiran dan pertimbangan, kami menetap di sebuah pipa (bar) representasi alami yang dibatasi, dengan pipa yang mengarah / mengikuti, jadi ".net c #" menjadi sekadar "| .net | c # |". Ini memiliki sifat:

  • sangat mudah diurai
  • pembaruan massal dan penghapusan tag dapat dilakukan dengan penggantian sederhana (termasuk pipa, untuk menghindari penggantian kecocokan tag tengah)
  • ...

"Format baru" ini adalah langkah selanjutnya dari "format lama" yang sedikit berbeda dan dipilih untuk menggunakan fitur Pencarian Penuh Teks SQL Server, sehingga beberapa manfaat tidak relevan jika Anda melakukannya dari awal.

Mereka mungkin tidak sepenuhnya menormalkan hal itu karena jumlah pekerjaan dan alasan kinerja.

Eugene Ryabtsev
sumber
0

Nah, salah satu kemungkinan manfaat utama menggunakan string, dan tipe data lainnya, adalah mengirimkannya dari SQL Server ke C #, C, C ++ (dll) menggunakan SQLCLR ketika kinerja semata mungkin diperlukan. Anda bahkan dapat membuat tampilan atau prosedur tersimpan untuk merepresentasikan data relasional secara non-relasional - seperti Anda menggunakan contoh di atas untuk tujuan ini.

Lihat contoh ini:

http://aboutsqlserver.com/2013/07/22/clr-vs-t-sql-performance-considerations/

per Wikipedia: SQL CLR atau SQLCLR (SQL Common Language Runtime) adalah teknologi untuk hosting mesin runtime bahasa umum Microsoft .NET dalam SQL Server. SQLCLR memungkinkan kode terkelola dihosting oleh, dan dijalankan di, lingkungan Microsoft SQL Server.

Menyengat
sumber
2
Hai yang disana. Bisakah Anda memberikan detail lebih lanjut di sini. Saya tidak yakin bagaimana ini manfaat menyimpan data dengan cara yang tidak tradisional. Jika ada, itu adalah manfaat dari SQLCLR untuk dapat menangani dengan lebih baik format data alternatif jika harus ada. Tapi itu bukan alasan untuk memilih format data alternatif. Karena itu, saya benar-benar tidak berpikir ini menjawab pertanyaan.
Solomon Rutzky
Tautan artikel menjelaskan manfaat dengan pro dan kontra. Juga, saya sebutkan menyimpan data secara relasional, dan untuk tujuan CLR mengubahnya menjadi non-relasional dengan tampilan, atau prosedur tersimpan. Pertanyaan Anda adalah "Apakah akan ada skenario yang membenarkan penyimpanan data in-line (string) alih-alih beberapa baris?" Dan jawaban saya adalah ya, meskipun saya lebih suka tampilan atau prosedur tersimpan untuk tujuan berinteraksi dengan CLR.
Menyengat
0

Dalam pandangan saya, jawabannya adalah tidak. Saya tidak menggunakan pendekatan ini dan akan menghindarinya - saya tidak bisa memikirkan alasan mengapa saya menempuh rute itu. Anda condong ke dunia JSON / NoSQL dengan sebuah array.

Kami memiliki pilihan desain yang serupa dalam peran sebelumnya di mana tim arsitek ingin memiliki bidang "Data" yang dibatasi dan kemudian dikonversi ke biner. Kami tidak turun rute pada akhirnya karena beberapa alasan.

Jika Anda harus bergabung dengan tipe data ini, itu akan menjadi pengalaman yang buruk. Memperbarui elemen tunggal dari string juga tidak menyenangkan.

Clive Strong
sumber