Birden çok değeri ayrı satırlar yerine bir satırın bir alanında depolamanın olası faydaları


11

Son haftalık toplantımızda, Veritabanı Yönetimi'nde arka plan deneyimi olmayan bir kişi şu soruyu gündeme getirdi:

"Verileri birkaç satır yerine satır içi (dize) depolamayı haklı kılacak bir senaryo var mı?"

countryStatesBir ülkenin eyaletlerini depolamak istediğimiz yerde bir tablo varsayalım ; Bu örnek için ABD'yi kullanacağım ve tembellik uğruna tüm Devletleri listelemeyeceğim.

Orada iki sütunumuz olacaktı; biri aradı Country, diğeri aradı States. Tartışıldığı gibi burada ve @ srutzky en önerdiği yanıt , PKtarafından tanımlanan kod olacak ISO 3166-1 alfa-3 .

Masamız şöyle görünür:

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

Aynı soruyu bir arkadaş geliştiriciye sorduğunda, veri trafiği boyutu açısından, bunun yararlı olabileceğini, ancak bu verileri manipüle etmemiz gerekiyorsa değil dedi. Bu durumda, uygulama kodunda bu dizeyi bir listede dönüştürebilecek bir zeka olması gerekir (diyelim ki bu tabloya erişimi olan yazılımın birleşik giriş kutusu oluşturması gerekir).

Bu modelin çok kullanışlı olmadığı sonucuna vardık, ancak bunu faydalı hale getirmenin bir yolu olabileceğinden şüphelendim.

Sormak istediğim, herhangi birinizin böyle bir şeyi gerçekten işe yarayacak bir şekilde görmüş, duymuş veya yapmış olup olmadığıdır .


Şimdi, satışın gerçekleştiği eyalet koduyla birlikte gerçekleşen her satış için verilere sahip ikinci bir tablonuz olan "satışlar" olduğunu düşünün. Sütunlar içeren bir rapor oluşturan bir sorguyu nasıl yazarsınız (StateName, TotalSalesAmount)? Zor, değil mi?
zgguy

Kesinlikle. Ben de bu modele katılmıyorum. Herhangi bir veri türünü (veya isterseniz yararlı verileri) kurtarmamız gereken herhangi bir noktada takılırız.
Human_AfterAll

Olası bir senaryo değişkenleri depolamak olabilir. Mağaza a;b;c, sonra dize sizi ayrıştırmak almak için kullanıcı arabirimi kullanmasını a, b, cbelki onlarla bir şeyler yapıyor yürütme ve carry ?. Bu tarzda bir tür özel ihtiyaca uygun olabileceğini hissedin ... İkinci düşüncede hayır. Her zaman kimlikleri depolayabilir, tablolarınıza katılabilir ve
FE'ye

Adil olmak gerekirse (benim için, en azından ;-), diğer cevapta 2 karakterlik ülke kodlarını :-) kullanmayı önerdim .
Solomon Rutzky

2
Hiç kimsenin STATE, N & C sütunları için ayrı bir tabloya sahip olmak yerine "Alabama" değerini bir sütunda saklamak konusunda bir sorunu olmadığına dikkat edin. Çünkü 1. ya da isimlerin karakterleri hakkında sorgulama niyetinde değiliz ya da 2. yaparsak, bir satırda her isimde "S dizesinin N karakteri . (Vs JOIN ve diğer ilişkisel işleçler bu gibi bazı satırları ekstra tablo aracılığıyla ortadan kaldırır.) Tamsayılar ve NTH_DIGIT için Ditto (N, I). Belirli bir veritabanında neyin ilişkisel olarak atomik olduğuna dair her zaman bir karar çağrısıdır.
philipxy

Yanıtlar:


13

Başlangıç ​​olarak, "sütun yerine verileri dize olarak saklamak" ile ilgili mevcut Soru başlığı biraz kafa karıştırıcıdır. Verileri başka bir şey yerine dize olarak depolamaktan bahsederken, bu genellikle her şeyi uygun / güçlü bir veri türü (örneğin INTveya DATETIME) yerine bir dize biçimine serileştirmeyi ifade eder . Ancak, verileri ayrı satırların aksine tek bir alanda birden çok değer olarak depolamak istiyorsanız, bu biraz farklıdır. Ve değerlerini birleştirerek en kolay dizeleri ile yapılmakla birlikte, aynı zamanda birlikte yapılabilir, adil olmak INTve BINARYsıra, her iki bit-maskeleyerek veya benzer farklı anlamlara belirli mevkileri türleri. İkinci yorum aslında sorulan şey olduğundan, Sorunun metnine dayanarak, bunu ele alalım.

Tek kelimeyle: Hayır. Gerçek veri noktalarını saklıyorsanız, gereksiz bir komplikasyon olduğu için sadece ağrı (kod ve performans açısından) getirecektir. Yalnızca tek bir birim olarak saklanacak, tek bir birim olarak güncellenecek ve asla veritabanında sökülmeyecek bir değerse, bir görüntüyü veya PDF'yi depolamaya kabaca benzer olduğu için bu uygun olabilir. Aksi takdirde, verileri ayrıştırma girişimleri herhangi bir dizin kullanarak geçersiz kılar (ör. LIKE '%something%', Veya CHARINDEX, veya PATINDEX, veya SUBSTRINGvb. Kullanarak ).

Tek bir satırın tek bir alanında ayrı değerler depolamanız gerekiyorsa, bunu yapmanın daha uygun yolları vardır: XML veya JSON. Bunlar ayrıştırılabilir biçimlerdir ( XML / JSON ) ve XML bile dizine eklenebilir . Ancak ideal olarak bu veriler, gerçekten yararlı olabilmesi için düzgün bir şekilde yazılan alanlarda saklanır.

Ve lütfen bir RDBMS'nin amacının, verileri ACID uyumlu olması nedeniyle getirilen kısıtlamalar dahilinde olabildiğince verimli bir şekilde alınabileceği ve işlenebileceği şekilde saklamak olduğunu unutmayın . Birleştirilmiş değerleri almak, önce değerleri ayrıştırma ihtiyacı nedeniyle yeterince kötüdür ve bu endekslenemez. Ancak manipüle etmek, çoğu zaman sadece bir kısmını güncellemek için tüm bloğu değiştirmek anlamına gelir (bir işlevle kullanılacak bir modelin olmadığı varsayılarak ). XML veri türü en azından basit güncellemeler için XML DML'ye izin verir , ancak bunlar uygun şekilde modellenmiş verilerin basit bir güncellemesi kadar hızlı değildir.REPLACE

Ayrıca, yukarıdaki Soru'da gösterilenler gibi bir senaryo verildiğinde, tüm StateCodes'ları birleştirerek bu değerleri Yabancı Anahtarlara (her iki yönde) koyamazsınız.

İş gereksinimleri zaman içinde değişirse ve bu öğelerin ek özelliklerini izlemeniz gerekiyorsa ne olur? "Devletler" açısından, başkentler, nüfus ya da bir sıralama düzeni ya da başka bir şey ne olacak? Satırlar olarak düzgün bir şekilde saklandığında ek özellikler için daha fazla sütun ekleyebilirsiniz. Elbette, birden fazla ayrıştırılabilir veri düzeyine sahip olabilirsiniz, |StateCode,Capital,Population |StateCode,Capital,Populate|...ancak umarım herkes sorunun üstel olarak kontrolden çıktığını görebilir. Tabii ki, bu özel sorun XML ve JSON formatlarıyla oldukça kolay bir şekilde ele alınır ve bu yukarıda belirtildiği gibi değerlerdir. Ancak , her ikisini de ayrı satırlarda ayrı alanları kullanmak kadar verimli olmayacağından, bunlardan birini ilk modelleme aracı olarak kullanmak için hala çok iyi bir nedene ihtiyacınız olacaktır.


9

Aslında böyle bir şeyi çok sınırlı bir amaç için kullandım. Çıktı dosyaları için bir başlık tablosu oluşturduk. Özel olarak inşa edilmişler ve çoğunlukla sadece sütun başlıklarıydı, ancak tam olarak değillerdi. Veriler şöyle görünüyordu:

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

Aslında sınırlı bir liste gibi görünüyordu. Ve bir bakıma öyleydi. Ama bizim amacımız için tek bir uzun ipti.

Buradaki hile bu. Eğer varsa asla o zaman yetmeyecek listesi tasarruf listesi ayrıştırma planlıyoruz. Bununla birlikte, listeyi ayrıştırmanız gerekecek veya hatta ayrılmanız gerekebiliyorsa, bölmek ve ayrı satırlara kaydetmek için ekstra alan ve zaman ayırmaya değer.


1

Bir kez oldukça küçük bir tabloyla kullandım, örneğin:

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

CREATE TABLE channel_def (
  channel varchar2(100));

Ve sonra değerleri CRM,SMS,SELF-CAREiçine saklayın valid_channel.

Tüm tablonun 10 kaydı var. valid_channelaslında bir çoktan çoğa ilişkiyi gösteren bir bağlantı tablosunda olması gereken değerleri içerir. Masa t1yoğun olarak kullanılmayacak, bu yüzden bu yolda gitmeye karar verdik. Bununla birlikte, bazı politikalar bu karara dahil olmuştur (aşağıya bakınız).

Ama genel olarak bundan kaçınırım, 3NF değil.

Şu anda çalıştığım yerde her yerde düzinelerce sütun var. Onların gerekçeleri, sorgularını kolaylaştırmasıdır: bağlantı tablosunu kullanarak üç tabloya katılmak yerine, tanım tablosunu kullanarak doğrudan gidebilirler LIKE. Örneğin

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

Oracle üzerindeki Horrible +, başlangıç ​​nedeniyle dizin kullanımını devre dışı bırakır '%,'.


Hangisi daha yavaş olur: LIKEveya basit bir birleşim?
Human_AfterAll

Dizine alınan veya en azından üzerinde bir referans kısıtlaması (FK) olan bir sütuna katılmak en iyisidir. Ek olarak, birleştirmeler genellikle varsayılan olarak dizine eklenen (en azından Oracle'da) diğer tablonun PK'sinde yapılır. Eldeki belirli bir durumu soruyorsanız (yukarıya bakın), yürütme planı büyük olasılıkla aynı olduğunu söyleyecektir, çünkü küçük bir tablodur.
Robotron

@Human_AfterTüm LIKEveriler, özellikle veriler bir TINYINTPK alanını kullanmak için uygun şekilde modellenmişse daha yavaş olur channel_def. O zaman sadece iki tablo arasında tek bir bayt karşılaştırmak gerekir. Burada, dizeyi karakter karakter ayrıştırmak zorundadır (en azından koşul karşılanana kadar) ve büyük / küçük harfe duyarlı olmayan bir arama yapıyor (verilen tablo def'nin _BIN2kullanılmakta olan bir harmanlamayı göstermemesine dayanarak ). Bu, SQL Server üzerindeki dizinleri de geçersiz kılar. Yanıtımda, ayrıştırma işleminin dizin kullanamayacağını söyleyerek değindim. Cevabımı daha açık hale getirmek için güncelledim.
Solomon Rutzky

1
@Human_AfterTüm bu modelleme kararının deneyim ve bilgi eksikliğinden (ve bazen tembellikten) kaynaklandığını söyleyebilirim . Ek bir birleşim kaydedilen tek şeydir, ancak feda edilen şey, tamamen sahte verilerin içeri girmesini engelleyecek Yabancı Anahtar yeteneğidir ( LIKEmadde ile eşleşmeyip garip sonuçlar üretse bile, yine de başka sorunlara neden olabilir veya en azından hata ayıklamayı daha sert / daha uzun yapın). Ayrıca valid_channelsalanın güncellenmesini daha karmaşık hale getirir . Bu, bunun işe yaramadığı anlamına gelmez, bunu yapmak için iyi bir neden yoktur .
Solomon Rutzky

"deneyim eksikliği" - en kötüsü, bu özel tasarım kararının kıdemli bir personel tarafından verildiğidir ...
Robotron

1

Bu burada SE'de yapıldı. Marc Gravell'in yazdığı gibi :

... Bazı düşünce ve değerlendirmelerden sonra, önde gelen / sondaki borularla sınırlandırılmış doğal temsili bir boruya (bar) yerleştik, böylece “.net c #” basitçe “| .net | c # |” olur. Bunun erdemleri var:

  • ayrıştırmak çok basit
  • toplu güncelleme ve etiketlerin kaldırılması basit bir değiştirme ile yapılabilir (orta etiket eşleşmelerinin değiştirilmesini önlemek için borular dahil)
  • ...

Bu "yeni biçim", biraz farklı olan ve SQL Server Tam Metin Arama özelliğini kullanmak için seçilen "eski biçim" in bir sonraki adımıydı, bu yüzden sıfırdan yaparsanız bazı avantajlar ilgili değildir.

Muhtemelen işi hem performans hem de performans nedenleriyle tamamen normalleştirmediler.


0

Dizeleri ve diğer veri türlerini kullanmanın olası bir birincil yararı, bunları SQL Server'dan CQ, C, C ++ (vb.) Düzeyine ihtiyaç duyulduğunda SQLCLR kullanarak göndermektir. İlişkisel verileri ilişkisel olmayan bir şekilde temsil etmek için bir görünüm veya saklı yordam bile oluşturabilirsiniz - bu amaçla yukarıdaki örneğinizde olduğu gibi.

Bu örneğe bakın:

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

Wikipedia başına: SQL CLR veya SQLCLR (SQL Ortak Dil Çalışma Zamanı), Microsoft .NET ortak dil çalışma zamanı motorunun SQL Server içinde barındırılması için kullanılan bir teknolojidir. SQLCLR, yönetilen kodun Microsoft SQL Server ortamı tarafından barındırılmasını ve çalıştırılmasını sağlar.


2
Merhaba. Burada daha ayrıntılı bilgi verebilir misiniz? Bunun geleneksel olmayan yollarla veri depolamanın bir yararı olduğundan emin değilim. Eğer varsa, alternatif veri formatlarıyla daha iyi başa çıkabilmek SQLCLR'nin bir yararıdır. Ancak bu, alternatif bir veri formatını tercih etmenin bir nedeni değildir. Bu nedenle, bunun soruyu cevapladığını gerçekten sanmıyorum.
Solomon Rutzky

Makale bağlantısı artıları ve eksileri ile faydalarını açıklar. Ayrıca, verileri ilişkisel olarak saklamayı ve CLR'nin bir görünüm veya ilişkisiz yordamla ilişkisel olmayan duruma dönüştürmesini amaçladım. Sorunuz "Verilerin birkaç satır yerine satır içi (dize) depolanmasını haklı kılan bir senaryo var mı?" Ve cevabım evetti, ancak CLR ile etkileşim amacıyla bir görünümü veya saklı prosedürü tercih ediyorum.
Sting

0

Bence cevap hayır olurdu. Bu yaklaşımı kullanmadım ve bundan kaçındım - o rotaya gitmem için bir neden düşünemiyorum. Bir dizi ile JSON / NoSQL dünyasına doğru eğiliyorsunuz.

Önceki bir rolde de benzer tasarım tercihlerimiz vardı; bu sayede mimar ekibi, sınırlandırılmış ve daha sonra ikili dosyaya dönüştürülmüş bir "Veri" alanına sahip olmak istiyordu. Sonunda birkaç nedenden ötürü o rotaya gitmedik.

Bu tür verilere katılmak zorunda olsaydınız, bu çirkin bir deneyim olurdu. Dizenin tek öğelerini güncellemek de hoş olmazdı.

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.