Listeleri ilişkisel bir veritabanında kullanmak hiç sorun değil mi?


94

Proje konseptine uygun bir veritabanı tasarlamaya çalışıyorum ve çok tartışılan bir konu gibi göründüm. Birkaç makale okudum ve bazı Stack Overflow cevapları bir alanda bir ID listesi veya benzeri bir şey saklamanın asla (veya neredeyse hiç) tamam olmadığını belirtir - tüm veriler ilişkisel olmalıdır, vb.

Karşılaştığım sorun olsa da, bir görevli görevli yapmaya çalışıyorum. İnsanlar görevler yaratacak, onları birden fazla kişiye atayacak ve veritabanına kaydedecektir.

Tabii ki, bu görevleri "Kişi" bölümünde ayrı ayrı kaydedersem, onlarca kukla "TaskID" sütununa sahip olmalıyım ve bunları mikro-yönetmem gerekecek, çünkü bir kişiye atanmış 0 ila 100 görev olabilir.

Sonra tekrar, görevleri "Görevler" tablosuna kaydedersem, onlarca sahte "PersonID" sütununa sahip olmalıyım ve bunları daha önce olduğu gibi mikro yönetmeliyim.

Böyle bir problem için, bir form ya da diğerini alarak bir kimlik listesini kaydetmem uygun mudur, yoksa bunun ilkeleri ihlal etmeden elde edilebilecek başka bir yol düşünmüyor muyum?


22
Bu etiketli "ilişkisel veritabanı" bu yüzden sadece bir yorum değil bir cevap olarak bırakacağız farkındayım, ama veritabanları başka tür o yapar listelerini depolamak için mantıklı. Cassandra birleşmediği için akla geliyor.
Kaptan Adam

12
Araştırmada ve sonra burada sormada iyi iş! Nitekim, 1. normal formu asla ihlal etmemek için 'tavsiye' sizin için gerçekten iyi oldu, çünkü gerçekten, başka bir ilişkisel yaklaşımla, yani içinde standart bir kalıbın olduğu "çoktan çoğa" bir ilişki bulmalısınız. kullanılması gereken ilişkisel veritabanları.
JimmyB

6
“Her şey yolunda mı?” Evet .... ne olursa olsun, cevabı evet. Geçerli bir nedeniniz olduğu sürece. Sizi her zaman en iyi uygulamaları ihlal etmeye zorlayan bir kullanım durumu vardır çünkü bunu yapmak mantıklıdır. (Senin durumunda olsa da, kesinlikle yapmamalıydın)
xyious

3
Şu anda bir etiket listesi saklamak için bir dizi ( ayrılmış bir dize değil - a VARCHAR ARRAY) kullanıyorum. Muhtemelen bu, satırın ilerleyen bölümlerinde depolanmalarına neden olmaz, ancak listelerde prototip oluşturma aşamasında, işaret edecek başka hiçbir şeye sahip olmadığınız ve yapmadan önce tüm veritabanı şemasını oluşturmak istemediğiniz durumlarda listeler çok yararlı olabilir. başka bir şey yap.
Nic Hartley,

3
@Ben " (bunlar dizine eklenebilir olmayacak olsa da) " - Postgres, (Kontrol etmedim ama muhtemelen XML) JSON sütunları karşı çeşitli sorgular olan dizine eklenebilir.
Nic Hartley

Yanıtlar:


249

Araştırmanız gereken anahtar kelime ve anahtar kavram veritabanı normalleşmesidir .

Yapacağınız şey, kişiye veya görev tablolarına atamalar hakkında bilgi eklemek yerine, ilgili atama bilgileriyle ilgili ilişkilerle birlikte yeni bir tablo eklemenizdir.

Örneğin, aşağıdaki tablolara sahipsiniz:

Kişiler:

+ ---- + ----------- +
| Kimlik | İsim |
+ ==== + =========== +
| 1 | Alfred |
| 2 | Jebediah |
| 3 | Jacob |
| 4 | Ezekiel |
+ ---- + ----------- +

Görevler:

+ ---- + -------------------- +
| Kimlik | İsim |
+ ==== + ==================== +
| 1 | Tavukları Besle |
| 2 | Pulluk |
| 3 | Sağım İnekleri |
| 4 | Bir ahır kaldırın |
+ ---- + -------------------- +

Daha sonra Atamalar ile üçüncü bir tablo oluşturun. Bu tablo, insanlar ve görevler arasındaki ilişkiyi modelleyecektir:

+ ---- + ----------- + --------- +
| Kimlik | PersonId | Görev Kimliği |
+ ==== + =========== + ========= +
| 1 | 1 | 3 |
| 2 | 3 | 2 |
| 3 | 2 | 1 |
| 4 | 1 | 4 |
+ ---- + ----------- + --------- +

Daha sonra, bir Yabancı Anahtar kısıtlamasına sahip olacağız, böylece veritabanı, Kişisel Kimlik ve Görev Kimliği'nin bu yabancı öğeler için geçerli kimlikler olması gerektiğini zorunlu kılar. İlk satır için görebiliriz PersonId is 1böylece, Alfred , atanan TaskId 3, Sağım inekler .

Burada görmeniz gereken şey, görev başına ya da kişi başına istediğiniz kadar az ya da daha fazla ödev verebileceğinizdir. Bu örnekte, Ezekiel'e herhangi bir görev verilmez ve Alfred , 2'ye SELECT PersonId from Assignments WHERE TaskId=<whatever>;atanır. Şunları yapabilirsiniz WHEREPersonId o kişiye atanmış tüm görevleri bulmak için.

Kimlikleri Adlar ve görevlerle değiştiren sorguları iade etmek istiyorsanız, tablolara nasıl katılacağını öğrenirsiniz.


86
Daha fazlasını öğrenmek aramak istediğiniz anahtar kelime "dir çok-çok ilişkisi "
Danny Pflughoeft - BlueRaja

34
Thierrys'in yorumunu biraz detaylandırmak için: Normalize etmeniz gerekmediğini düşünebilirsiniz, çünkü sadece X'e ihtiyacım var ve kimlik listesini saklamak çok basit , ancak daha sonra genişletilebilecek herhangi bir sistem için normalleştirmemiş olduğunuz için pişman olacaksınız. daha erken. Her zaman normalleştirmek ; tek soru normal formun
Jan Doggen

8
@Jan ile anlaşmaya varıldı - daha iyi bir karar vermeme karşı ekibimin bir süre önce bir tasarım kısayolu almalarına izin verdim, bunun yerine JSON'u "uzatılması gerekmeyecek" bir şey için sakladı. FML gibi altı ay sürdü. Ardından geliştiricimiz, JSON'u başlamamız gereken programa geçirmek için ellerinde iğrenç bir kavga geçirdi. Gerçekten daha iyisini bilmeliydim.
Orbit'te Hafiflik Yarışları

13
@Deduplicator: Bahçe çeşitliliği, otomatik artış tamsayı birincil anahtar sütununun bir temsilidir. Oldukça tipik şeyler.
whatsisname,

8
@whatsisname Kişiler veya Görevler tablosunda sizinle aynı fikirdeyim. Tek amacın halihazırda vekil anahtarlara sahip diğer iki tablo arasındaki çok-çok ilişkisini temsil etmek olduğu bir köprü masasında? İyi bir sebep olmadan bir tane eklemem. Sorgularda veya ilişkilerde asla kullanılmayacağından, sadece ek yük.
jpmc26

35

Burada iki soru soruyorsun.

İlk önce, bir sütunda serileştirilmiş listeleri saklamak için uygun olup olmadığını sorun. Evet bu iyi. Projeniz bunun için çağırırsa. Örnek olarak, her bir içeriği ayrı ayrı takip etmek istemediğiniz bir katalog sayfası için ürün bileşenleri olabilir.

Maalesef ikinci sorunuz daha ilişkisel bir yaklaşım tercih etmeniz gereken bir senaryoyu açıklar. 3 masaya ihtiyacınız olacak. Biri halk için, biri görevler için, diğeri ise hangi görevin hangi kişilere atandığını listeler. Bu sonuncusu dikey olacak, kişi / görev kombinasyonuna göre bir satır, birincil anahtarınız için sütunlar, görev kimliği ve kişi kimliği.


9
Referans verdiğiniz içerik örneği yüzeyde doğru; ancak bu durumda düz metin olur. Programlama anlamında bir liste değildir (dizginin açıkça yapmadığınız bir karakter listesi olduğu anlamına gelmiyorsa). Verilerini "kimlik listesi" (veya sadece "[[]]" listesi "olarak tanımlayan) OP, bu verileri tek tek nesneler olarak işleyen bir noktada olduklarını belirtir.
Flast

10
@Flater: Ama bu bir liste. Öğelerin bir web sayfasında, düz metin belgesinde, cep telefonunda (çeşitli şekillerde) düzgün bir şekilde görüntülenmesini sağlamak için bir HTML listesi, bir Markdown listesi, JSON listesi, vb. Biçiminde yeniden biçimlendirebilmeniz gerekir. app ... ve gerçekten düz metinle yapamazsınız.
Kevin

12
@Kevin Amacınız buysa, malzemeleri bir tabloda depolayarak çok daha kolay ve kolayca elde edilebilir! Oh, dilediğine, diyelim ki, bilmiyorum sonra, insanlar ... olurdu eğer cabası tavsiye yerine kullanılan veya elde bakmak gibi aptalca bir şey olmadan tüm tarifler herhangi fıstık veya glüten veya hayvansal proteinler ...
Dan Bron

10
@DanBron: YAGNI. Şu anda sadece bir liste kullanıyoruz çünkü kullanıcı arayüzü mantığını kolaylaştırıyor. İhtiyacımız veya iş mantığı tabakasında liste benzeri davranış ihtiyaç olacaksa, o zaman ayrı bir tabloya normalize edilmelidir. Tablolar ve birleştirmeler mutlaka pahalı değildir, ancak ücretsiz değillerdir ve eleman sıralaması ("Malzemelerin sırasını önemsiyor muyuz?") Ve daha da normalleşme ("3 yumurtayı" açacak mısınız?) içine ('yumurtalar', 3)? 'Tuz, tadı', peki ('tuz', NULL)? ").
Kevin

7
@Kevin: YAGNI burada oldukça yanlış. Siz, listeyi birçok şekilde (HTML, markdown, JSON) dönüştürmenin gerekliliğini savundunuz ve bu nedenle listenin tek tek öğelerine ihtiyacınız olduğunu savunuyorsunuz . Veri depolama ve "liste kullanımı" uygulamaları bağımsız olarak geliştirilen iki uygulama olmadıkça (ve ayrı uygulama katmanlarının! = Ayrı uygulamaların olduğunu unutmayın), veri tabanı yapısı her zaman verileri hazır bir şekilde saklayacak bir biçimde saklanmalıdır. - Ek ayrıştırma / dönüştürme mantığından kaçınırken.
Flater

22

Ne tarif ettiğin arasında durumunda, ilişki bir "Birçok birçok" olarak bilinir Personve Task. Genellikle, bazen "link" veya "çapraz referans" tablosu olarak adlandırılan üçüncü bir tablo kullanılarak uygulanır. Örneğin:

create table person (
    person_id integer primary key,
    ...
);

create table task (
    task_id integer primary key,
    ...
);

create table person_task_xref (
    person_id integer not null,
    task_id integer not null,
    primary key (person_id, task_id),
    foreign key (person_id) references person (person_id),
    foreign key (task_id) references task (task_id)
);

2
task_idGörev tarafından filtrelenen sorgular yapıyorsanız, önce bir dizin eklemek de isteyebilirsiniz .
jpmc26 16:18

1
Ayrıca köprü masası olarak da bilinir. Ayrıca, her sütunda bir indeks tavsiye etmeme rağmen, kimlik sütunu bulundurmadığınız için size ekstra bir artı verebilmeyi diliyorum.
jmoreno

13

... bir alanda bir kimlik veya benzeri listesini saklamak asla (veya neredeyse hiç) tamam değildir

Eğer sadece zaman olabilir o alan olduğunda tek bir alandaki birden fazla veri öğesi depolamak olduğunu sadece şimdiye kadar tek bir varlık olarak da kullanılır; asla bu küçük elemanların yapılmış olarak değerlendirmiştir. Bir örnek, bir BLOB alanında depolanan bir görüntü olabilir. Çok fazla ve çok sayıda küçük elemandan (bayt) oluşur, ancak bunlar veritabanında hiçbir şey ifade etmez ve yalnızca birlikte kullanılabilir (ve bir Son Kullanıcıya hoş görünürler).

Bir "liste", tanımı gereği, daha küçük elemanlardan (öğeler) oluştuğundan, buradaki durum bu değildir ve verileri normalleştirmelisiniz.

... bu görevleri tek tek "Kişi" de kaydedersem, onlarca sahte "TaskID" sütununa sahip olmak zorunda kalacağım ...

Hayır . Kişi ve Görev arasında bir Kavşak Tablosunda (aka Zayıf Varlık) birkaç satır olacak. Veritabanları birçok satırla çalışmakta gerçekten iyidir; aslında birçok [tekrarlanan] sütunla çalışmakta oldukça berbatlar.

Whatsisname tarafından verilen net net bir örnek.


4
Gerçek hayat sistemleri oluştururken "asla asla" deme, yaşanması çok iyi bir kuraldır.
lbb0

1
Çoğu durumda, bir listeyi normalleştirilmiş biçimde tutmanın veya almanın öğe başına maliyeti, listenin her bir öğesinin içinde bulunduğu ana öğenin kimliğini elinde bulundurması gerektiğinden, öğeleri bir blok olarak tutma maliyetini büyük ölçüde aşabilir Gerçek verilere ek olarak listedeki konumu ile ilişkilendirilir. Kodun, tüm listeyi güncellemeden bazı liste öğelerini güncelleyebilmesinden fayda sağlayabileceği durumlarda bile, her şeyi bir blob olarak saklamak ve bir şeyi yeniden yazmak zorunda kaldığında her şeyi yeniden yazmak daha ucuz olabilir.
supercat

4

Bazı önceden hesaplanmış alanlarda yasal olabilir.

Sorgularınızdan bazıları pahalıysa ve veritabanı tetikleyicileri kullanılarak otomatik olarak güncellenen önceden hesaplanmış alanları seçmeye karar verirseniz, listeleri bir sütun içinde tutmak meşru olabilir.

Örneğin, kullanıcı arayüzünde, her listenin çift tıkladıktan sonra tam ayrıntıları (tam listelerle) açabileceği, ızgara görünümünü kullanarak bu listeyi göstermek istiyorsunuz:

REGISTERED USER LIST
+------------------+----------------------------------------------------+
|Name              |Top 3 most visited tags                             |
+==================+====================================================+
|Peter             |Design, Fitness, Gifts                              |
+------------------+----------------------------------------------------+
|Lucy              |Fashion, Gifts, Lifestyle                           |
+------------------+----------------------------------------------------+

Müşteri yeni makaleyi ziyaret ettiğinde veya zamanlanmış görevde tetikleyici tarafından ikinci sütunu güncel tutmaya devam edersiniz.

Böyle bir alanı arama için bile uygun hale getirebilirsiniz (normal metin olarak).

Bu gibi durumlarda, liste tutmak meşrudur. Sadece maksimum alan uzunluğunu aşma ihtimalini göz önünde bulundurmanız gerekir.


Ayrıca, Microsoft Access kullanıyorsanız, sunulan çok değerli alanlar başka bir özel kullanım durumudur. Listelerinizi otomatik olarak bir alanda yönetir.

Ancak, diğer cevaplarda gösterilen standart normalleştirilmiş forma her zaman geri dönebilirsiniz.


Özet: Normal veri tabanı formları , veri modellemenin önemli yönlerini anlamak için gerekli olan teorik modeldir. Ancak elbette normalleştirme, performansı veya verileri almanın diğer maliyetlerini hesaba katmaz. Bu teorik modelin kapsamı dışında. Ancak, liste veya diğer önceden hesaplanmış (ve kontrol edilen) kopyaları saklamak pratik uygulama için genellikle gereklidir.

Yukarıdakilerin ışığında, pratik uygulamada, mükemmel normal forma dayanan sorguyu ve 20 saniye çalışmasını mı yoksa 0.08 s süren önceden hesaplanmış değerlere dayanan eşdeğer sorguyu mu tercih edeceğiz? Hiç kimse yazılım ürününü yavaşlıkla suçlanmaktan hoşlanmaz.


1
Önceden hesaplanmış şeyler olmadan bile meşru olabilir. Verilerin doğru şekilde depolandığı birkaç kez yaptım, ancak performans nedeniyle ana kayıtlarda birkaç önbelleklenmiş sonuç elde etmek faydalıdır.
Loren Pechtel

@LorenPechtel - Evet, teşekkürler, önceden hesaplanan terimi kullandığımda, gerektiğinde saklanan önbellek değerleri vakalarını da içeririm . Karmaşık bağımlılığa sahip sistemlerde, performansı normal tutmanın yolu bunlar. Ve eğer yeterli teknik bilgi ile programlanmışsa, bu değerler güvenilir ve daima senkronizedir. Sadece cevabı basit ve güvenli tutmak için cevabın önbelleğe alınma durumunu eklemek istemedim. Yine de indirildi. :)
miroxlav

@LorenPechtel Aslında, bu hala kötü bir sebep olurdu ... önbellek verileri bir ara önbellek deposunda tutulmalı ve önbellek hala geçerliyken, bu sorgu ana DB'ye hiç girmemelidir.
Tezra

1
@Tezra Hayır, bazen ikincil bir tablodan bir veri parçasının ana kopyanın bir kopyasını çıkarmanın mantıklı olması için yeterince gerekli olduğunu söylüyorum. (Yaptığım örnek - çalışan tablosu son giriş ve son çıkış süresini içerir. Yalnızca görüntüleme amacıyla kullanılırlar, gerçek hesaplamaların hepsi giriş / çıkış kayıtlarını içeren tablodan gelir.)
Loren Pechtel

0

İki tablo verildi; Onlara Kişi ve Görev adını vereceğiz, her biri kendi kimliğine (Kişi Kimliği, Görev Kimliği) sahip ... temel fikir, onları birbirine bağlamak için üçüncü bir tablo oluşturmaktır. Bu tabloya PersonToTask adını vereceğiz. En azından kendi kimliğine ve diğer ikisine sahip olmalıdır. Yani birisini bir göreve atamak söz konusu olduğunda; artık Person tablosunu güncellemenize gerek kalmayacak, PersonToTaskTable'a yeni bir satır eklemek yeterlidir. Ve bakım daha kolay hale gelir; bir görevi silmeye ihtiyaç duyar, TaskID'ye dayalı bir SİLME olur, Kişi tablosunu daha fazla güncellemeye gerek kalmaz

CREATE TABLE dbo.PersonToTask (
    pttID INT IDENTITY(1,1) NOT NULL,
    PersonID INT NULL,
    TaskID   INT NULL
)

CREATE PROCEDURE dbo.Task_Assigned (@PersonID INT, @TaskID INT)
AS
BEGIN
    INSERT PersonToTask (PersonID, TaskID)
    VALUES (@PersonID, @TaskID)
END

CREATE PROCEDURE dbo.Task_Deleted (@TaskID INT)
AS
BEGIN
    DELETE PersonToTask  WHERE TaskID = @TaskID
    DELETE Task          WHERE TaskID = @TaskID
END

Basit bir rapora veya bir göreve atananlara ne dersiniz?

CREATE PROCEDURE dbo.Task_CurrentAssigned (@TaskID INT)
AS
BEGIN
    SELECT PersonName
    FROM   dbo.Person
    WHERE  PersonID IN (SELECT PersonID FROM dbo.PersonToTask WHERE TaskID = @TaskID)
END

Elbette daha fazlasını yapabilirsin; TaskAssigned ve TaskCompleted için DateTime alanları eklediyseniz bir TimeReport yapılabilir. Her şey sana bağlı


0

İnsan tarafından okunabilen Birincil anahtarlara sahipseniz ve bir görev yapısının bir tablo yapısının dikey doğası ile uğraşmadan bir görev listesi istemeniz gerektiğini söyleyebilir. yani ilk tabloyu okumak çok daha kolay.

------------------------  
Employee Name | Task 
Jack          |  1,2,5
Jill          |  4,6,7
------------------------

------------------------  
Employee Name | Task 
Jack          |  1
Jack          |  2
Jack          |  5
Jill          |  4
Jill          |  6
Jill          |  7
------------------------

O zaman soru şu olurdu: görev listesi büyük ölçüde aşağıdaki gibi gereksinimlere bağlı olacak şekilde talep üzerine depolanmalı veya oluşturulmalı mı: listenin ne sıklıkta gerektiği, kaç veri satırının mevcut olduğu, verilerin nasıl kullanılacağı vs. .. bundan sonra kullanıcı deneyimine karşı ticaretin analiz edilmesi ve ihtiyaçların karşılanması gerekir.

Örneğin, 2 sırayı oluşturan bir sorgu çalıştırarak vs 2 sırayı hatırlamak için gereken süreyi karşılaştırarak. Uzun sürerse ve kullanıcı en güncel listeye ihtiyaç duymuyorsa (* günde 1'den az değişiklik bekliyor) o zaman saklanabilir.

Veya kullanıcı, kendisine atanmış görevlerin geçmiş kaydına ihtiyaç duyuyorsa, listenin kaydedilip kaydedilmediğini de anlamalıdır. Bu gerçekten ne yaptığınıza bağlı, asla asla deme.


Söylediğiniz gibi, bu verilerin nasıl alınacağına bağlıdır. Siz / sadece / hiç bu tabloyu Kullanıcı Adı ile sorgularsanız, "liste" alanı tamamen yeterlidir. Bununla birlikte, Görev # 1234567'de kimin çalıştığını bulmak ve nasıl bir performans sergilemek için böyle bir tabloyu nasıl sorgulayabilirsiniz? Hemen hemen her tür "X alanın herhangi bir yerinde bulunan X" işlevi, böyle bir sorgunun / Tablo Taraması / 'a yol açmasına neden olur. Düzgün bir şekilde normalize edilmiş, doğru bir şekilde endekslenmiş verilerle bu gerçekleşmez.
Phill W.

0

Başka bir masa olması gereken şeyi alıyor, 90 derece döndürüyor ve başka bir masaya atıyorsunuz.

İtemProdcode1, itemQuantity1, itemPrice1 ... itemProdcode37, itemQuantity37, itemPrice37 öğelerinin bulunduğu bir sipariş tablosuna sahip olmak gibidir. Programlı bir şekilde ele almak garip olmaktan başka, yarın birisinin 38 şey sipariş etmek isteyeceğini garanti edebilirsiniz.

'Liste' gerçekten bir liste değilse, yani bir bütün olarak durduğu ve her bir satır öğesinin açık ve bağımsız bir varlığa atıfta bulunmadığı durumlarda, kendi yolunuzla yapacağım. Bu durumda hepsini, yeterince büyük olan bazı veri türlerinde doldurun.

Bu yüzden bir düzen bir listedir, bir malzeme listesi bir listedir (veya "yana doğru" uygulamak için daha fazla bir kabus olacak bir liste listesidir). Ancak bir not / yorum ve bir şiir değildir.


0

"Tamam" değilse, her Wordpress sitesinin bir satırda wp_capabilities, bir satırdaki dismissed_wp_pointers listesi ve diğerlerinin bir listesi olması oldukça kötü.

Aslında bu gibi durumlarda , neredeyse her zaman listeyi istediğiniz gibi hız için daha iyi olabilir . Ancak Wordpress'in en iyi uygulamaların mükemmel bir örneği olduğu bilinmemektedir.

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.