Sınırlandırılmış bir listeyi bir veritabanı sütununda depolamak gerçekten kötü mü?


363

Bir dizi onay kutusu içeren bir web formu düşünün (herhangi biri veya tamamı seçilebilir). Onları veritabanı tablosunun bir sütununda depolanan değerlerin virgülle ayrılmış listesine kaydetmeyi seçtim.

Şimdi, doğru çözümün ikinci bir tablo oluşturmak ve veritabanını düzgün bir şekilde normalleştirmek olacağını biliyorum. Kolay çözümü uygulamak daha hızlıydı ve bu uygulamanın hızlı bir şekilde ve üzerinde çok fazla zaman harcamak zorunda kalmadan bir kavram kanıtına sahip olmak istedim.

Tasarruf edilen zamanın ve daha basit kodun benim durumumda buna değer olduğunu düşündüm, bu savunulabilir bir tasarım seçimi mi, yoksa baştan normalleştirmeliydim mi?

Biraz daha bağlamda, bu, esas olarak paylaşılan bir klasörde depolanan bir Excel dosyasının yerini alan küçük bir iç uygulamadır. Ayrıca soruyorum çünkü programı temizlemeyi ve daha sürdürülebilir hale getirmeyi düşünüyorum. İçinde tamamen memnun olmadığım bazı şeyler var, bunlardan biri bu sorunun konusudur.


21
bu durumda, neden veritabanı rahatsız ?, bir dosyaya kaydetmek yapacak.
thavan

6
@Thavan ile aynı fikirde. Neden bir kavram kanıtı için veri bile kaydediliyor? Kanıtı tamamladıktan sonra, doğru bir veritabanı ekleyin. Konsept kanıtı için ince yaptığınız para cezası, sadece daha sonra yapmak zorunda olduğunuz şeyleri yapmayın.
Jeff Davis

1
Postgres'de, virgülle ayrılmış bir listeye göre bir dizi sütunu tercih edilmelidir. Bu, en azından doğru veri türünü garanti eder, sınırlayıcıyı gerçek verilerden ayırt etmekte sorun yaşamaz ve verimli bir şekilde indekslenebilir.
a_horse_with_no_name

Yanıtlar:


568

Tek bir sütunda depolanan yinelenen değerler grubu nedeniyle İlk Normal Formu ihlal etmenin yanı sıra , virgülle ayrılmış listelerin daha birçok başka pratik sorunu vardır:

  • Her bir değerin doğru veri türü olduğundan emin olamaz: 1,2,3, muz, 5'i önlemenin yolu yok
  • Değerleri arama tablosuna bağlamak için yabancı anahtar kısıtlamaları kullanılamaz; referans bütünlüğünü zorlamanın bir yolu yoktur.
  • Benzersizliği zorlayamıyorum: 1,2,3,3,3,5 önlemenin yolu yok
  • Tüm liste getirilmeden listeden bir değer silinemez.
  • Dize sütununa sığmayacak kadar uzun bir liste saklanamaz.
  • Listede belirli bir değere sahip tüm varlıkları aramak zor; verimsiz bir tablo taraması kullanmanız gerekir. Düzenli ifadelere başvurmanız gerekebilir, örneğin MySQL'de:
    idlist REGEXP '[[:<:]]2[[:>:]]'*
  • Listedeki öğeleri saymak veya başka toplu sorgular yapmak zor.
  • Değerleri başvurdukları arama tablosuna eklemek zor.
  • Listeyi sıralanmış düzende getirmek zor.

Bu sorunları çözmek için, RDBMS'nin zaten çok daha verimli sağladığı işlevselliği yeniden icat ederek tonlarca uygulama kodu yazmanız gerekir .

Virgülle ayrılmış listeler, kitabımdaki ilk bölümü yaptığım kadar yanlış: SQL Antipatterns: Veritabanı Programlamanın Tuzaklarından Kaçınma .

Denormalizasyon kullanmanız gereken zamanlar vardır, ancak @OMG Ponies'in belirttiği gibi , bunlar istisna durumlarıdır. İlişkisel olmayan herhangi bir "optimizasyon", verilerin diğer kullanımları pahasına bir tür sorgudan yararlanır, bu nedenle hangi sorgularınızın denormalizasyonu hak edecek kadar özel olarak ele alınması gerektiğini bildiğinizden emin olun.


* MySQL 8.0 artık bu kelime-sınır ifadesi sözdizimini desteklememektedir.


8
Bir ARRAY (herhangi bir veri türünden ) istisnayı düzeltebilir, sadece PostgreSQL'i kontrol edin: postgresql.org/docs/current/static/arrays.html (@Bill: Harika bir kitap, herhangi bir geliştirici veya dba için okunması gerekir)
Frank Heikens

4
+1 fatura Karwin Harika cevap! Güzel özlü kurşun noktaları. Bu harika bir kitap gibi görünüyor. Kapağı da çok seviyorum +1 NullUserException. Düz dosya metin tabanlı sistemi değiştirmek için bir MySQL veritabanı için şema tasarlama sürecindeyim. Şimdiye kadar birkaç ikilemle karşılaştım. Yani bu kitap satın almaya değer.
therobyouknow

2
Pragprog.com sitesi de iyi görünüyor: güzel stil, düzen, kullanıcı dostu temiz. Bu oldukça yeni olmalı, geçmişte e-kitaplarını alamıyordum. PS. Onlarla çalışmıyorum, yazarlarla hiçbir bağlantısı yok. İyi ürünleri, hizmetleri kutlamayı ve gördüğümde yardım etmeyi seviyorum.
therobyouknow

2
Ciddi tarafta, listenize eklemek isterim: Araması zor. "2" içeren tüm kayıtları istediğinizi varsayalım. Tabii ki sadece foobar = '2' yi arayamazsınız çünkü başka değerler olsaydı kaçırırdı. Foobar'ı '% 2%' gibi arayamazsınız, çünkü bu 12 ve 28 için yanlış isabetler alır vb. Foobar'ı '%, 2,%' gibi arayamazsınız, çünkü 2 listenin ilk veya son öğesi olabilir ve bu nedenle yalnızca bu virgüllerden birine sahip olabilirsiniz.
Jay

2
Tavsiye edilmediğini biliyorum, ama şeytanlar savunucusu oynamak: tekliği ve veri türlerini (aksi halde hata veya yanlış davranır) işleyen bir kullanıcı arayüzü varsa, ui düşer ve yine de oluşturur, burada bir sürücü tablosu var. değerleri benzersiz kılmak için gelir, '% P%' gibi alan kullanılabilir, değerler P, R, S, T, sayma önemli değildir ve sıralama önemli değildir. Kullanıcı arabirimine bağlı olarak, değerler bölünebilir [] örn. Bir tablodaki onay kutularını sürücü tablosundan en az ortak senaryoda onaylamak için başka bir tabloya gitmeye gerek kalmadan.
jmcclure

44

"Bir sebep tembellikti".

Alarm zilleri çalar. Bunun gibi bir şey yapmanızın tek nedeni, bunu "doğru yolla" nasıl yapacağınızı bilmenizdir, ancak bunu bu şekilde yapmamak için somut bir neden olduğu sonucuna vardınız.

Bunu söyledikten sonra: Bu şekilde saklamayı seçtiğiniz veriler asla sorgulamanız gerekmeyecek verilerse, seçtiğiniz şekilde saklamak için bir durum olabilir.

(Bazı kullanıcılar önceki paragrafımdaki ifadeye "gelecekte ne gibi gereksinimlerin ekleneceğini asla bilemezsiniz" diyerek itiraz edebilirler. Bu kullanıcılar yanlış yönlendirilmiş veya dini bir inanç belirtmişlerdir. Bazen gereksinimlerinize göre çalışmak avantajlıdır önünüzde olsun.)


Bazı insanların, yabancı anahtar kısıtlamaları koymamak veya listeleri tek bir alanda saklamak gibi şeylerle karşılaştığımda "tasarımım sizinkinden daha esnek" dediğini duyuyorum. Bana göre, esneklik (bu gibi durumlarda) == disiplin yok == tembellik.
foresightyj

41

SO sorma konusunda çok sayıda soru var:

  • virgülle ayrılmış listeden belirli değerlerin sayısı nasıl alınır
  • virgülle ayrılmış listeden yalnızca aynı 2/3 / etc'ye özgü değere sahip kayıtlar nasıl alınır

Virgülle ayrılmış listeyle ilgili bir başka sorun da değerlerin tutarlı olmasını sağlamaktır - metin depolamak yazım hataları ...

Bunların tümü denormalize verilerin belirtileridir ve neden normalleştirilmiş veriler için her zaman modelleme yapmanız gerektiğini vurgular. Denormalizasyon , ihtiyaç gerçekten kendini gösterdiğinde uygulanacak bir sorgu optimizasyonu olabilir .


19

Genel olarak, projenizin gereksinimlerini karşılarsa her şey savunulabilir. Bu, insanların kararınızı kabul edeceği veya savunmak isteyeceği anlamına gelmez ...

Genel olarak, verileri bu şekilde depolamak yetersizdir (örneğin, etkin sorgular yapmak daha zordur) ve formunuzdaki öğeleri değiştirirseniz bakım sorunlarına neden olabilir. Belki bir orta yol bulabilir ve bunun yerine bir bit bayrağı kümesini temsil eden bir tam sayı kullanabilirsiniz?


10

Evet, gerçekten bu kadar kötü olduğunu söyleyebilirim. Savunabilir bir seçim, ancak bu doğru ya da iyi yapmıyor.

İlk normal formu kırar.

İkinci bir eleştiri, ham girdi sonuçlarını herhangi bir doğrulama veya bağlama olmaksızın doğrudan bir veritabanına koymanın sizi SQL enjeksiyon saldırılarına açık bırakmasıdır.

Tembellik ve SQL bilgisi eksikliği olarak adlandırdığınız şey, neofitlerin yapıldığı şeydir. Bunu yapmak için zaman ayırmanızı ve bir öğrenme fırsatı olarak görmenizi tavsiye ederim.

Ya da olduğu gibi bırakın ve bir SQL enjeksiyon saldırısının acı verici dersini öğrenin.


19
Bu soruda SQL enjeksiyonuna karşı savunmasız olduğunu gösteren hiçbir şey görmüyorum. SQL enjeksiyonu ve veritabanı normalizasyonu dikey konulardır ve enjeksiyonla ilgili kazmanız soru ile ilgisizdir.
Hammerite

5
@Paul: Ve belki de aynı tutum, caddeyi geçmeden önce her iki yöne bakamadığında bir otobüse çarpmasına neden olacak, ama onu bu konuda uyarmadın. Edit: Ben bu cevabın posteri olduğunu düşünmüştüm, benim hatam.
Hammerite

1
@Hammerite - otobüslere ekstrapolasyonunuz çok saçma.
duffymo

4
Evet, saçma olması amaçlanmıştı. Onun gülünçlüğü, dikkatimi çektiğim düşünmek için hiçbir nedeniniz olmayan bir şeye karşı onu uyarmanın hiçbir anlamı olmadığı anlamına geliyor.
Hammerite

1
Evet görüyorum. Sanırım otobüsler hakkındaki uyarınızın çok daha fazla sebebi vardı.
duffymo

7

Peki 4 yıldan fazla bir süredir SQL Server'da bir NTEXT sütununda bir anahtar / değer çifti sekmesi ayrılmış liste kullanıyorum ve çalışıyor. Sorgu yapma esnekliğini yitirirsiniz, ancak öte yandan, anahtar değer çiftinde kalıcı / derpersistler olan bir kütüphaneniz varsa o kadar da kötü bir fikir değildir.


13
Hayır, bu korkunç bir fikir. Bundan kurtulmayı başardınız, ancak birkaç dakikalık geliştirme sürenizin maliyeti, sorgu performansını, esnekliğini ve kodunuzun sürdürülebilirliğini maliyetlendiriyor.
Paul Tomblin

5
Paul, katılıyorum. Ama dediğim gibi eğer belirli bir amaç için kullandım ve bu birçok formun olduğu bir veri giriş işlemi içindir. NHibernate öğrendim şimdi tasarım revize ama o zaman ASP.NET form tasarımı ve anahtar / değer çift anahtar olarak metin kutusu kimlikleri kullanmak için esneklik gerekli.
Raj

28
Sadece aşağı oylara karşı + 1'leyin. Uygulamayı bakım endişeleri hakkında 4 yıldır sürdüren birine söylemek biraz küstahtır. Yazılım geliştirmede çok az "korkunç" fikir vardır - çoğunlukla sadece çok sınırlı uygulanabilirliğe sahip fikirlerdir. İnsanları sınırlamalar konusunda uyarmak mantıklıdır, ancak bunu yapmış ve yaşamış olanları azarlamak bana, onsuz yapabileceğimden daha kutsal bir tutum olarak dikkat çekiyor.
Mark Brackett

7

Çok değerli bir sütuna ihtiyacım vardı, bir xml alanı olarak uygulanabilir

Gerektiğinde sınırlandırılmış virgüle dönüştürülebilir

Xquery kullanarak sql sunucusunda bir XML listesi sorgulama .

Bir xml alanı olarak, bazı endişeler giderilebilir.

CSV ile: Her değerin doğru veri türü olduğundan emin olamaz: 1,2,3, muz, 5'i önlemenin yolu yok

XML ile: bir etiketteki değerler doğru tür olmaya zorlanabilir


CSV ile: Değerleri arama tablosuna bağlamak için yabancı anahtar kısıtlamaları kullanılamaz; referans bütünlüğünü zorlamanın bir yolu yoktur.

XML ile: hala bir sorun


CSV ile: Benzersizliği zorlayamıyorum: 1,2,3,3,3,5 önlemenin yolu yok

XML ile: hala bir sorun


CSV ile: Tüm listeyi getirmeden listeden bir değer silinemez.

XML ile: tek öğeler kaldırılabilir


CSV ile: Listede belirli bir değere sahip tüm varlıkları aramak zor; verimsiz bir tablo taraması kullanmanız gerekir.

XML ile: xml alanı dizine eklenebilir


CSV ile: Listedeki öğeleri saymak veya başka toplu sorgular yapmak zor. **

XML ile: özellikle zor değil


CSV ile: Referans verdikleri arama tablosuna değerleri birleştirmek zor. **

XML ile: özellikle zor değil


CSV ile: Listeyi sıralı olarak getirmek zor.

XML ile: özellikle zor değil


CSV ile: Tam sayıları dize olarak saklamak, ikili tam sayıları saklamaktan yaklaşık iki kat daha fazla alan gerektirir.

XML ile: depolama bir csv'den bile daha kötü


CSV ile: Artı bir çok virgül karakteri.

XML ile: virgül yerine etiketler kullanılır


Kısacası, XML kullanmak sınırlandırılmış listeyle ilgili bazı sorunların üstesinden gelir VE gerektiği gibi sınırlandırılmış bir listeye dönüştürülebilir


6

Evet, olduğu kadar da kötü. Benim görüşüme göre ilişkisel veritabanlarını kullanmaktan hoşlanmıyorsanız, o zaman size daha uygun bir alternatif arayın, orada bazı gerçekten gelişmiş özelliklerle ilginç "NOSQL" projeleri var.


0

Muhtemelen orta yolu alacağım: CSV'deki her alanı veritabanında ayrı bir sütuna dönüştürün, ancak normalleştirme hakkında çok fazla endişelenmeyin (en azından şimdilik). Bir noktada, normalizasyon olabilir ilginç hale, ancak tek bir sütuna itti tüm verilerle tüm bir veritabanı kullanarak neredeyse hiçbir yarar kazanıyor ediyoruz. Anlamlı bir şekilde manipüle etmeden önce verileri mantıksal alanlara / sütunlara / çağırmak istediğiniz her şeye ayırmanız gerekir.


Form daha fazla alan içeriyor, bu formun sadece bir kısmı (soruda iyi açıklamamıştım).
Mad Scientist

0

Sabit sayıda boole alanınız varsa, her biri için bir INT(1) NOT NULL(veya BIT NOT NULLvarsa) veya CHAR (0)(boş bırakılabilir) kullanabilirsiniz. Ayrıca SET(Tam sözdizimini unutuyorum) kullanabilirsiniz.


1
INT(1)4 bayt alır; (1)anlamsız.
Rick James
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.