Özel alanlara sahip bir kullanıcı veritabanını nasıl tasarlarsınız


19

Bu soru, bir veritabanını nasıl tasarlamalıyım, daha iyi bir çözümün ne olacağına bağlı olarak ilişkisel / nosql veritabanları olabilir.


"Şirket" ve "Kullanıcı" yı izlemek için bir veritabanı içeren bir sistem oluşturmanız gerektiğinde bir gereklilik verilmiştir. Tek bir kullanıcı her zaman yalnızca bir şirkete aittir

  • Bir kullanıcı yalnızca bir şirkete ait olabilir
  • Bir şirketin birçok kullanıcısı olabilir

"Şirket" tablosunun tasarımı oldukça basittir. Şirket aşağıdaki özelliklere / sütunlara sahip olacaktır: (basit tutalım)

ID, COMPANY_NAME, CREATED_ON

İlk senaryo

Basit ve basit, kullanıcıların hepsi aynı özniteliğe sahiptir, bu nedenle ilişkisel tarzda, kullanıcı tablosunda kolayca yapılabilir:

ID, COMPANY_ID, FIRST_NAME, LAST_NAME, EMAIL, CREATED_ON

İkinci senaryo

Farklı şirketler kullanıcıları için farklı profil özniteliği depolamak isterse ne olur? Her şirket, o şirketin tüm kullanıcıları için geçerli olacak tanımlanmış bir öznitelik kümesine sahip olacaktır.

Örneğin:

  • A şirketi depolamak istiyor: LIKE_MOVIE (boolean), LIKE_MUSIC (boolean)
  • B Şirketi saklamak istiyor: FAV_CUISINE (String)
  • C Şirketi depolamak istiyor: OWN_DOG (boolean), DOG_COUNT (int)

Yaklaşım 1

kaba kuvvet yolu, kullanıcı için tek bir şemaya sahip olmak ve şirkete ait olmadıklarında boş değerlere sahip olmaktır:

ID, COMPANY_ID, FIRST_NAME, LAST_NAME, EMAIL, LIKE_MOVIE, LIKE_MUSIC, FAV_CUISINE, OWN_DOG, DOG_COUNT, CREATED_ON

Bu çok kötü çünkü bir sürü NULLS ve kendileriyle alakasız sütunlara sahip kullanıcı satırları elde edeceksiniz (yani A Şirketine ait tüm kullanıcıların FAV_CUISINE, OWN_DOG, DOG_COUNT) için NULL değerleri var

Yaklaşım 2

ikinci bir yaklaşım, "serbest form alanı" na sahip olmaktır:

ID, COMPANY_ID, FIRST_NAME, LAST_NAME, EMAIL, CUSTOM_1, CUSTOM_2, CUSTOM_3, CREATED_ON

Özel alanların ne olduğu hakkında hiçbir fikriniz olmadığı için kendi başına kötü olurdu, veri türü depolanan değerleri yansıtmayacaktır (örn. İnt değerini VARCHAR olarak depolayacağız).

Yaklaşım 3

PostgreSQL JSON alanına baktım, bu durumda:

ID, COMPANY_ID, FIRST_NAME, LAST_NAME, EMAIL, CUSTOM_PROFILE_JSON, CREATED_ON

Bu durumda, bir kullanıcıya farklı şemalar nasıl uygulayabilirsiniz? A Şirketine sahip bir kullanıcının şemasına benzemesi gerekir

 {"LIKE_MOVIE":"boolean", "LIKE_MUSIC": "boolean"}

C Şirketine sahip bir kullanıcının farklı bir şeması olurken:

 {"OWN_DOG ":"boolean", "DOG_COUNT": "int"}

Bu sorunu nasıl çözmeliyim? Tek bir "nesne" (Kullanıcı) için sahip oldukları ilişkiye (Şirket) dayalı olarak bu esnek şemaya izin vermek için veritabanını nasıl düzgün bir şekilde tasarlayabilirim?

ilişkisel çözüm? nosql çözümü?


Düzenleme: Ayrıca kullanıcı özniteliklerini sütunlar yerine satırlarda depolayacak bir "CUSTOM_PROFILE" tablosu düşündüm.

Bu yaklaşımla ilgili 2 sorun vardır:

1) Kullanıcı başına veriler sütunlar yerine satırlar olarak büyür - ve bu, kullanıcının tam bir resmini elde etmek için çok fazla birleştirme yapılması, farklı özel özelliklerde "özel profil" tablosuna birden çok ekleme yapılması anlamına gelir

2) Verilerin tamsayı veya boole vb. Olması gerektiğini bilsek bile, veri değeri genel olarak VARCHAR olarak saklanır.


3
Farklı şirketlerin her bir müşteri üzerinde farklı, çok değerli veri kümeleri varsa, mutlaka bir COMPANY_CUSTOMER bağlantı tablosuna ihtiyacınız vardır. Diğer her şey çok yakında büyük acıya neden olur.
Kilian Foth

Bir bağlantı tablosu özel verilerle nasıl yardımcı olur? sütunların yine de farklı olması gerekir
noobcser

1
"ŞİRKET: IKEA, MÜŞTERİ: Kilian, ATTRIBUTE: şifre, VALUE: kedi yavrusu" gibi bir tuple ile "Kilian'ın IKEA için şifresi 'kedi yavrusu'" gerçeğini temsil etmelisiniz. Daha basit bir şey işi yapmaz.
Kilian Foth

3
Şema, tanımı gereği sabit bir şeydir; ihtiyacınız olan alanların ne olduğunu bilmiyorsanız bir tane ayarlayamazsınız. İlişkisel bir veritabanında çözülme eğilimi gibi tek yönlü problemler için Entity-Attribute-Value'e bir göz atın .
Mason Wheeler

Yanıtlar:


14

Lütfen bunu bir alternatif olarak düşünün. Önceki iki örnek, uygulamanın kapsamı büyüdükçe şemada değişiklik yapmanızı ve "custom_column" çözümünün genişletilmesi ve sürdürülmesi zor olmasını gerektirecektir. Sonunda Custom_510 ile sonuçlanacak ve daha sonra bu tablonun çalışmak için ne kadar korkunç olacağını hayal edeceksiniz.

Önce Şirketler şemanızı kullanalım.

[Companies] ComnpanyId, COMPANY_NAME, CREATED_ON

Daha sonra, tüm şirketler tarafından kullanılacak / paylaşılacak en üst düzey gerekli özellikler için Kullanıcılar şemanızı kullanacağız.

[Users] UserId, COMPANY_ID, FIRST_NAME, LAST_NAME, EMAIL, CREATED_ON

Daha sonra, her şirketin özel kullanıcı özelliklerine özgü dinamik özelliklerimizi tanımlayacağımız bir tablo oluşturuyoruz. Yani burada Öznitelik sütununun örnek değeri "LikeMusic" olacaktır:

[UserAttributeDefinition] UserAttributeDefinitionId, CompanyId, Attribute

Ardından, kullanıcı özellik değerlerini tutacak bir UserAttributes tablosu tanımlarız

[UserAttributes] UserAttributeDefinitionId, UserId, Value

Bu, performans için daha iyi olacak şekilde birçok şekilde değiştirilebilir. UserAttributes için, her birini Değer'de depolanan veri türüne özgü hale getiren birden çok tablo kullanabilir veya bunu bir VarChar olarak bırakabilir ve onunla bir anahtar değer deposu olarak çalışabilirsiniz.

Ayrıca, CompanyId öğesini UserAttributeDefiniton tablosunun dışına ve gelecekteki yazım denetimleri için çapraz referans tablosuna taşımak isteyebilirsiniz.


teşekkürler - Ben böyle bir yaklaşım olsa - lütfen düzenleme bakınız. 2 problem: 1) Veriler satırlar halinde büyür, yani bir kullanıcının tam resmini elde etmek için çok fazla birleştirme yapmanız gerekir. 2) "değer", değer aslında int veya boolean vb.
Olsa

1
Tablo kimlikleri için int / bigint kullanırsanız ve bunlara katılırsanız, çok sayıda satıra kadar performans sorunları yaşamayacaksınız. Öznitelik değerlerine dayalı olarak aramaya başlarsanız, çok sayıda kayıt almaya başlarsanız bu bir sorun oluşturabilir. Bu durumda, oluşturulabilecek dizinler veya belki de bu tür aramaları hızlandırabilecek dizinlenmiş bir görünüm olup olmadığını belirlemek için bir DBA ile çalışacağım. Benzer bir şema kullandım ve hiçbir performans sorunu olmadan yılda 100 milyon kayıt alıyor, bu nedenle temel tasarım oldukça iyi çalışıyor IMO
P. Roe

Raporlama, filtreleme, sorgulama gerekiyorsa ve farklı özellikler farklı veri kümelerine ait olabilir. Bu yaklaşım NoSQL'den daha iyi olur mu? Performans farkını anlamaya çalışıyorum. Benzer bir durum yalnızca kullanıcı tarafından tanımlanan alanları içeren raporları tanımlayabilir.
kos

Yukarıdaki yaklaşımda, arama şeyi fark olarak nasıl uygularız. şirketler, kullanıcı alanları da dahil olmak üzere kendi alanlarında arama yapmak istemektedir. Bunun üstünde ölçeklenebilir arama sağlamak için doğru yaklaşım nedir
techagrammer

Normalde çok sayıda birleştirmeyle arama yapabilirsiniz. Aramak istediğiniz verileri ayıklamak ve daha denormalize bir yapıya yerleştirmek için bir ETL komut dosyası kullanabilirsiniz. Son olarak, dizine alınmış görünümleri arama yöntemi olarak kullanmaya çalışabilirsiniz. Şahsen araması kolay denormalize yapılar oluşturmak için ETL yöntemini öneririm.
P. Roe

7

NoSQL veritabanı kullanın. Şirket ve kullanıcı belgeleri olacaktır. Kullanıcılar, şemalarının bir kısmını bir kullanıcı şablonuna (o şirkete ait alanları / türleri belirten metin) dinamik olarak oluşturulur.

\Company\<uniqueidentifier>
    - Name: <Name>
    - CreatedOn: <datetime>
    - UserTemplate: <Text>

\User\<uniqueidentifier>
    - COMPANY_ID: <ID>
    - FIRST_NAME: <Text>
    - LAST_NAME: <Text>
    - EMAIL: <Text>
    - CREATED_ON: <datetime>
    - * Dynamically created fields per company

Firebase.com gibi bir şeye böyle görünebilir. Seçtiğiniz her şeyde nasıl yapılacağını öğrenmek zorunda kalacaksınız.


bu ya da belki JSON sütunları hakkında düşünüyorum. PRoe tarafından önerilen çözümle karşılaştırıldığında, sorgulama, filtreleme raporlamasındaki performans nasıl?
kos

1
Verileri her zaman json veya xml'ye sıkıştırır ve sonra bir sütuna atarsanız, arama yapmak çok yavaş olacaktır. Yukarıdaki cevabımda sunulan verileri aramanız gerekirse, verileri almak için dizine alınmış görünümleri kullanmanızı öneririm. Bu çözüm ideal değilse, verileri kolayca aranabilen ve raporlanabilen bir yapıya kopyalamak için ETL kullanmanızı öneririm.
P. Roe

Yukarıdaki yaklaşımda, arama şeyi fark olarak nasıl uygularız. şirketler, kullanıcı alanları da dahil olmak üzere kendi alanlarında arama yapmak istemektedir. Bunun üstünde ölçeklenebilir arama sağlamak için doğru yaklaşım nedir
techagrammer

Nosql veritabanlarında gereksiz verileriniz olabilir, ancak bunlar aranabilir olacak şekilde yapılandırılmıştır. Yukarıda gösterilen, benzersiz tanımlayıcıdır. Bir diğeri \ Şirket \ Adı olabilir. Birden çok dizin oluşturmaya benzer.
JeffO

3

Sık sık özel alan istekleriyle karşılaşacaksanız, aslında veritabanına oldukça benzer bir şekilde modellenirim. Her özel alan, CompanyCustomField (kime ait olduğu, veri türü vb.) Hakkındaki meta verileri ve CustomerId, FieldId ve değeri içeren başka bir şirket CompanyCustomFieldValues ​​tablosu içeren bir tablo oluşturun. Microsoft Sql Server gibi bir şey kullanıyorsanız, değer sütununun sql_variant veri türü olması gerekir.

Elbette bu kolay değildir çünkü yöneticilerin her müşteri için özel alanlar tanımlamasına izin veren bir arayüze ve alan değerlerini toplamak için bir kullanıcı arayüzü oluşturmak için bu meta verileri kullanan başka bir arayüze ihtiyacınız olacaktır. Ve alanların birlikte gruplanması veya bir seçim listesi tür alanı yapılması gibi başka gereksinimleriniz varsa, bunu daha fazla meta veri / diğer tablolarla (örneğin, CompanyCustomFieldPickListOptions) kabul etmeniz gerekir.

Bu önemsiz değildir, ancak her yeni özel alan için veritabanı değişikliği / kod değişikliği gerektirmeme avantajına sahiptir. Özel alanların diğer özelliklerinin de kodlanması gerekir (örneğin, bir dize değerini düzenli olarak doğrulamak veya yalnızca belirli aralıklar arasındaki tarihlere izin vermek istiyorsanız veya başka bir özel alan değerine dayalı olarak bir özel alanı etkinleştirmeniz gerekiyorsa ).


teşekkürler - Ben böyle bir yaklaşım olsa - lütfen düzenleme bakınız. 2 problem: 1) Veriler satırlar halinde büyür, yani bir kullanıcının tam resmini elde etmek için çok fazla birleştirme yapmanız gerekir. 2) "değer", değer aslında int veya boolean vb.
Olsa

1
@noobcser Tüm veritabanları satırlar ve birleşimler etrafında tasarlandıktan sonra, satır olarak büyüyen veriler gerçekten önemli değildir. Her durumda, bu tür şeyler için oldukça iyi olan Ortak Tablo İfadelerini kullanmanız daha olasıdır. Eğer sql_variant, hangi tür sopa içine değeri depolar değer sütun için veri türü olarak kullanabileceğinizi söylediğim kısmı kaçırdıysanız emin değilim. MS SQL sunucusu özellik adlarını adlandırırken, diğer olgun DBMS'nin benzer özelliklere sahip olmasını beklerdim.
Andy

1
@noobcser FYI Aslında kariyerimde bu gerekliliklerle oldukça sık karşılaştım ve önerilen çözümlerin her biri ile deneyime sahibim, bu yüzden deneyimlerimde en iyi şekilde çalışanı öneriyorum. Bu tür bir şey için xml veri türlerinin kullanımı kısmen neden bu MS yerel veri türü olarak xml ekleyerek nefret ediyorum.
Andy

1

Diğer cevaplara bir alternatif şema uygulamanız tarafından tamamen yönetilen profile_attrib veya benzeri bir tabloya sahip olmaktır.

Özel öznitelikler eklendikçe, ALTER TABLE profile_attrib ADD COLUMN like_movie TINYINT(1)bunları silmeyi yasaklayabilirsiniz. Bu, esneklik sağlarken katılımınızı en aza indirir.

Biraz değiş tokuş uygulaması artık veritabanına tablo ayrıcalıklarını değiştirmek gerekir ve sütun adlarını dezenfekte konusunda akıllı olmak zorunda sanırım.


Düzenli ifade [^\w-]+, bunu iyi bir şekilde yapmalı, olmayan bir şeye izin vermemeli - 0-9A-Za-z_-ama evet, sanitizasyon, kötü niyetliğe veya aptallığa karşı korumak için burada şarttır.
Düzenli Joe

0

Sorunuzun birçok potansiyel çözümü var. Bir çözüm, ek özellikleri XML olarak saklamaktır. XML metin olarak veya XML türlerini XML (SQL Server) olarak destekleyen bir veritabanı kullanıyorsanız saklanabilir. Metin olarak saklamak, sorgulama yeteneğinizi sınırlar (özel bir öznitelikte arama yapmak gibi), ancak depolama ve alma tüm gereksiniminizse iyi bir çözümdür. Sorgulanması gerekiyorsa, XML'i XML türü olarak depolamak daha iyi bir seçenek olacaktır (bu daha satıcıya özgü olsa da).

Bu, müşteri tablosuna yalnızca bir ekleme sütunu ekleyerek müşteriye herhangi bir sayıda özelliği depolayabilme olanağı verecektir. Öznitelikler bir karma veya sözlük olarak saklanabilir, her şey başlamak için bir dize olacağından tür güvenliğini kaybeder, ancak tarihler, sayılar, booleans için standart biçim dizgisini zorlarsa, sorun çözmez.

Daha fazla bilgi için:

https://msdn.microsoft.com/en-us/library/hh403385.aspx

@ WalterMitty'nin cevabı da geçerlidir, ancak farklı özelliklere sahip çok sayıda müşteri varsa, kalıtım modelini takip ederse birçok tabloyla sonuçlanabilir. Bu, müşteriler arasında kaç adet özel özelliğin paylaşıldığına bağlıdır.


Bu da işe yarayabilir, ancak aslında XML / JSON alanında depolanan verilere karşı bir şeyler yapmanız gerektiğinde sınırlı hissediyorum.
Andy

@Andy - Doğru, başka bir katman daha var. DB'yi sorgula ve XML'i sadece DB'yi sorgulamak yerine ayrıştır. Sınırlayıcı diyebilir miyim bilmiyorum, sadece daha hantal. Ancak, özel özelliklerin yoğun bir şekilde kullanılıp kullanılmadığı dikkate alınacak bir şey olurdu.
Jon Raynor

T-SQL'de XML / JSON sütunundaki içeriği bir ad alanına karşı tanımlamak ve özel verilerdeki öğelere karşı sorgulamak mümkündür. Zor değil
Stephen York

-1

Veritabanınızı, her farklı şirket profili türü için 3 farklı tabloya sahip olacak şekilde normalleştirmelisiniz. Örneğin, sütun içeren tablolarınız olacaktır:

USER_ID, LIKE_MOVIE, LIKE_MUSIC

USER_ID, FAVORITE_CUISINE

USER_ID, OWN_DOG, DOG_COUNT

Bu yaklaşım, bir şirketin önceden depolamak istediği bilginin şeklini bileceğinizi ve bunun sık sık değişmeyeceğini varsayar. Verilerin şekli tasarım zamanında bilinmiyorsa, bu JSON alanına veya nosql veritabanına gitmek daha iyi olur.


-1

Bir nedenden ötürü, veritabanları, iç platform etkisinin en sık görüldüğü alandır. Bu, anti-desen patlamasının başka bir örneğidir.

Bu durumda, doğal ve doğru çözümle savaşmaya çalışıyorsunuz. A şirketinin kullanıcıları B şirketinin kullanıcıları değildir ve kendi alanları için kendi tabloları olmalıdır.

Veritabanı satıcınız tablodan ücret almaz ve tablonun iki katı için iki kez disk alanına ihtiyacınız yoktur (aslında, iki tablonun olması daha verimlidir, çünkü A'nın B kullanıcıları için özniteliklerini depolamazsınız. yer kaplar).

Tabii ki, yeterli ortak alan varsa, bunları paylaşılan bir Kullanıcılar tablosuna dahil edebilir ve şirkete özgü kullanıcı tablolarının her birinde bir yabancı anahtar kullanabilirsiniz. Bu o kadar basit bir yapı ki, hiçbir veritabanı sorgusu iyileştiricisi onunla mücadele etmez. Gerekli herhangi bir JOIN önemsizdir.


3
Ve binlerce müşteriniz varsa, her biri için bir tablo hızlı bir şekilde sürdürülemez hale gelebilir, her müşterinin özel alanları için özel koda ihtiyacınız olduğunu belirtmiyoruz.
Andy

@Andy: Bil bakalım ne oldu? Bin farklı şemayı tek bir masaya karıştırırsanız durum daha da sürdürülemez! Ve evet, muhtemelen özel alanlar için özel koda ihtiyacınız vardır. Her müşterinin temiz ve ayrı bir masası varsa, bu daha basit, daha zor değil. X şirketinin alanlarını bin kişiden almaya çalışmak kanlı bir karışıklıktır.
MSalters

Cevabımdan mı yoksa OP'lerin fazladan sütunları müşteri tablosuna yapıştırma fikrinden mi bahsediyorsunuz?
Andy

2
Buradaki amaç, sürdürülebilir ve ölçeklenebilir bir çözüm bulmaktır. Müşteri başına bir masa oluşturmak kesinlikle bunun tam tersidir. Yeni bir müşteriye her katıldığınızda, gerçekçi değildir: bir tablo oluşturma komut dosyası çalıştırmak, kodunuzu güncellemek (Varlık nesneleri) ve yeniden dağıtmak.
tsOverflow 13:15

Tüm müşteriler için paylaşılan tablolar kullanma fikrinin tamamı ayrı bir SaaS mimarisi tartışmasıdır ve müşterileri farklı tablolarda (hatta farklı veritabanlarında tutmak, müşteri başına yedekleme / geri yükleme ve ölçeklendirme sağlamak) için bazı iyi nedenler vardır. Bu senaryoda, ana tabloda cusotm sütunları oluşturmak no-brainer. Kaldırdım ve merak ediyorum ki insanlar neden bu yaklaşımı sevmedikleri için bunu önemsiz düşürüyorlar. İç platform etkisi bir gerçektir: Bir EVA modeli kullanarak sorgulamanız daha zor, daha zor, bütünlük daha zor, vb.
Olacaktır

-1

Çözümüm, bu sorguyu bir programdan çağırdığınızı ve işlem sonrası gerçekleştirebildiğinizi varsayar. Aşağıdaki sütunlara sahip olabilirsiniz:

ID, COMPANY_ID, FIRST_NAME, LAST_NAME, EMAIL, CUSTOM_VALUES

CUSTOM_VALUES, dize depolama anahtarı ve değer çifti türünde olacaktır. anahtar sütun adı ve değer sütun değeri, ör.

LIKE_MOVIE;yes;LIKE_MUSIC;no;FAV_CUISINE;rice

bu CUSTOM_VALUES içinde yalnızca var olan bilgileri kaydedeceksiniz. Programdan sorgu yaptığınızda bu dizeyi bölebilir ve kullanabilirsiniz.

Bu mantığı kullanıyorum ve iyi çalışıyor, sadece sorgulamada kodda filtreleme mantığı uygulamak zorunda kalacaksınız.

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.