İlişkisel veritabanları neden iç içe geçmiş bir bilgi döndürmeyi desteklemiyor?


46

Yazıları ve yorumları almak istediğim bir blog oluşturduğumu varsayalım. Bu yüzden iki tablo, 'autocrementing integer' id 'sütununa sahip bir' posts 'tablosu ve yabancı bir' post_id 'anahtarına sahip' comments 'tablosu oluşturuyorum.

Sonra muhtemelen en yaygın sorgumun ne olacağını çalıştırmak istiyorum. Bu, bir yayını ve tüm yorumlarını almaktır. İlişkisel veritabanlarında oldukça yeniyken, bana en açık görünen yaklaşım, şuna benzer bir sorgu yazmaktır:

SELECT id, content, (SELECT * FROM comments WHERE post_id = 7) AS comments
FROM posts
WHERE id = 7

Bu da bana istediğim gönderinin kimliğini ve içeriğini, bir dizi içinde düzgünce paketlenmiş tüm ilgili yorum satırlarıyla (JSON'da kullanacağınız gibi iç içe bir gösterim) verirdi. Elbette, SQL ve ilişkisel veritabanları bu şekilde çalışmaz ve elde edebildikleri en yakın şey, 'gönderiler' ve 'yorumlar' arasında bir çok gereksiz veri çoğaltması getirecek bir katılım yapmaktır (aynı gönderi bilgisi tekrarlanırken) Her satırda), işlem süresi, hepsini bir araya getirmek için veritabanına hem de ORM'ime hepsini çözümlemek ve geri almak için harcanan anlamına gelir.

ORM’ye gönderinin yorumlarını hevesle yüklemesini söylesem bile, en iyisi gönderinin bir sorgusunu, ardından da tüm yorumları almak için ikinci bir sorguyu gönderip sonra bunları istemci tarafı koymaktır. ayrıca yetersiz.

İlişkisel veritabanlarının kanıtlanmış teknoloji olduğunu (cehennem, benden daha yaşlı olduklarını) ve on yıllardır kendilerine bir ton araştırma yaptıklarını ve bunların gerçekten iyi bir neden olduğunu biliyorum (ve SQL standardı) yaptıkları gibi çalışacak şekilde tasarlanmıştır, ancak yukarıda ana hatları çizdiğim yaklaşımın neden mümkün olmadığından emin değilim. Kayıtlar arasındaki en temel ilişkilerden birini uygulamanın en basit ve açık yolu bana geliyor. İlişkisel veritabanları neden böyle bir şey önermiyor?

(Feragatname: Çoğunlukla Rails ve NoSQL veri depolarını kullanarak webapps yazarım, ancak son zamanlarda Postgres'i deniyorum ve aslında çok hoşuma gidiyor. İlişkisel veritabanlarına saldırmak istemiyorum, sadece şaşırdım.)

Bir Rails uygulamasını nasıl optimize edeceğimi ya da belirli bir veritabanında bu problemi nasıl çözeceğimi sormuyorum. Neden SQL standardı bana karşı mantıklı ve savurgan göründüğünde bu şekilde çalışıyor diye soruyorum. SQL’in özgün tasarımcılarının sonuçlarının böyle görünmesini istemelerinin bir nedeni olmalı.


1
Tüm ormanlar o şekilde çalışmaz. hazırda bekleme / nhibernate, birleştirme işlemlerinin belirlenmesine olanak tanır ve tüm nesne ağaçlarını tek bir sorgudan yüklemek için istekli olabilir.
nathan gonzalez

1
ayrıca, ilginç bir tartışma konusu olsa da, ansi sql'li adamlarla bir toplantı yapmadan bunun gerçekten cevaplanabileceğinden emin değilim
nathan gonzalez

@ nathan: Evet, hepsi değil. Belirli bir sorgu için tercih edeceğiniz yaklaşımı seçmenize izin veren Sequel'i kullanıyorum ( dokümanlar ), ancak yine de çoklu sorgu yaklaşımını teşvik ediyorlar (performans nedenleriyle, sanırım).

5
Bir RDBMS, kümeleri depolamak ve almak için tasarlandığından, görüntüleme için veri döndürmek için tasarlanmamıştır. MVC gibi düşünün - neden modeli daha yavaş ya da kullanımı daha zor hale getirme pahasına görüşü uygulamaya çalışsın ki? RDBMS, NoSQL veritabanlarının sağlayamadığı avantajlar sunmaktadır (ve bunun tersi de geçerlidir) - eğer kullanıyorsanız, probleminizi çözmek için doğru araç olduğundan, verileri görüntülemeye hazır hale getirmesini istemeyeceksiniz.

Yanıtlar:


42

CJ Date, bununla ilgili olarak Bölüm 7'de ve SQL ve İlişkisel Teorinin Ek B'sinde ayrıntılı olarak ele alınmıştır . Haklısın, ilişkisel teoride, bir özniteliğin veri türünün, her satırdaki aynı ilişki türü olduğu sürece, ilişkinin kendisi olmasını yasaklayan hiçbir şey yoktur . Örneğin kalifiye olur.

Ancak Date, bunun gibi yapıların “genellikle - ama her zaman değil - kontrendike” olduğunu söylüyor (yani Kötü Bir Fikir) çünkü ilişkilerin hiyerarşileri asimetriktir . Örneğin, yuvalanmış yapıdan bilinen bir "düz" yapıya dönüşüm, yuvalamayı yeniden oluşturmak için her zaman geri alınamaz.

Sorgu, kısıt ve güncellemeler daha karmaşık, yazması daha zor ve ilişkinin değerli niteliklerine (RVA'lar) izin verirseniz RDBMS'nin desteklemesi daha zordur.

Aynı zamanda veritabanı tasarım ilkelerini karıştırır, çünkü ilişkilerin en iyi hiyerarşisi çok açık değildir. Belirli bir Tedarikçi tarafından tedarik edilen parçalar için Tedarikçilerin iç içe geçmiş bir RVA ile ilişkisini kurmalı mıyız? Ya da belirli bir parçayı tedarik eden tedarikçiler için iç içe geçmiş bir RVA ile parçaların ilişkisi? Ya da her ikisini de depolayarak farklı türde sorguları çalıştırmayı kolaylaştırmak için?

Bu hiyerarşik veri tabanından ve belge yönelimli veri tabanından kaynaklanan ikilemdir . Sonunda, iç içe geçmiş veri yapılarına erişimin karmaşıklığı ve maliyeti, tasarımcıları farklı sorgularla daha kolay arama yapabilmek için gereksiz verileri depolamaya yönlendirir. İlişkisel model fazlalığı engeller, böylece RVA'lar ilişkisel modelleme hedeflerine karşı çalışabilirler.

Anladığım kadarıyla (onları kullanmadım), Rel ve Dataphor ilişki değerli özellikleri destekleyen RDBMS projeleridir.


@Dportas tarafından yapılan yorumdan tekrar:

Yapısal türler SQL-99'un bir parçasıdır ve Oracle bunları desteklemektedir. Ancak iç taban tablosunda, taban tablosunun her satırı için birden fazla tup saklamazlar. Ortak örnek, temel tablonun tek bir sütunu gibi görünen, ancak sokak, şehir, posta kodu vb. İçin alt sütunlara sahip olan bir "adres" özelliğidir.

İç içe geçmiş tablolar da Oracle tarafından desteklenir ve bunlar temel tablonun satırı başına birden çok tuple izin verir. Ancak bunun standart SQL'nin bir parçası olduğunu bilmiyorum. Vebir blogun sonucunu aklınızda bulundurun: "CREATE TABLE ifadesinde iç içe geçmiş bir tabloyu asla kullanmayacağım. Tüm zamanınızı tekrar faydalı hale getirmek için UN-NESTING (BOŞALTMA)!"


3
Aslında bir ilişkiyi diğerinin içinde saklamak istemem - ayrı masalarda olacaklar ve her zamanki gibi normalleştirilmişlerdi. Ben sadece neden bu tür katıştırmaya katılmayacağımı soruyorum, bu bana katılmamın modelinden daha sezgisel göründüğü zaman sorgulara dahil edilmiyor.
PreciousBodilyFluids

Sonuç kümeleri ve tabloları bir tür. Tarih, sırasıyla ilişkileri ve yeniden çağrıları çağırır (analojiye göre, 42 bir tamsayıdır, oysa bir değişken x42 tamsayısına sahip olabilir). Aynı işlemler ilişkiler ve değişimler için de geçerlidir, bu yüzden yapılarının uyumlu olması gerekir.
Bill Karwin

2
Standart SQL iç içe geçmiş tabloları desteklemez. Bunlara "yapılandırılmış tip" denir. Oracle, bu özelliğe sahip bir DBMS'dir.
nvogel

2
Veri yinelemesinden kaçınmak için sorgunuzu düz, veri yineleyici bir şekilde yazmanız gerektiğini iddia etmek saçma değil mi?
Eamon Nerbonne

1
@ AmonNerbonne, ilişkisel işlemlerin simetrisi. Örneğin, projeksiyon. Bir RVA'dan bazı alt öznitelikleri SEÇİRSem, orijinal hiyerarşiyi yeniden üretmek için sonuç kümesine karşı ters bir işlemi nasıl uygulayabilirim? Date'nin kitabının 293. sayfasını Google Kitaplar'da bulduğum için ne yazdığını görebilirsiniz: books.google.com/…
Bill Karwin

15

En eski veri tabanı sistemlerinden bazıları Hiyerarşik Veri Tabanı modeline dayanıyordu . Bu, ebeveynler ve çocuklar içeren ağaç benzeri bir yapıdaki verileri temsil ediyor, tıpkı burada önerdiğiniz gibi. HDMS, büyük ölçüde ilişkisel model üzerine kurulu veritabanları ile değiştirildi. Bunun temel nedenleri, RDBMS'nin hiyerarşik veritabanları için zor olan "çoktan çoğa" ilişkileri modelleyebilmesi ve RDBMS'nin orijinal tasarımın bir parçası olmayan sorguları kolayca gerçekleştirebilmesi, HDBMS'nin ise tasarım zamanında belirtilen yolları sorgulamanızı zorlamasıydı.

Vahşi doğada, özellikle de Windows kayıt defterinde ve LDAP'de bazı hiyerarşik veritabanı sistemleri örnekleri bulunmaktadır.

Bu konunun geniş kapsamı aşağıdaki makalede mevcuttur.


10

Sorunuzun gerçekten, veritabanlarının sağlam bir mantığa dayandığı ve theroretik temeli oluşturduğu ve merkezî bir bütünlük, eşzamanlılık sağlarken (2 boyutlu) kümelerdeki verileri depolamak, işlemek ve almak için çok iyi bir iş yaptıkları gerçeğine odaklandığını düşünüyorum. ve diğer birçok şey, nesneye yönelik format veya hiyerarşik format olarak adlandırılabilecek şekilde veri gönderme (ve alma) özelliği (ek) özelliği sağlamazlar.

Ardından, "ORM'me gönderinin yorumlarını istekli bir şekilde yüklemesini bildirsem bile, yapacağı en iyi şey, gönderinin bir sorgusunu, ardından da tüm yorumları almak için ikinci bir sorguyu gönderip sonra bunları bir araya getirmektir. Ayrıca, istemci tarafı, etkisiz " .

2 sorgu göndermek ve 2 toplu sonuç almak için yetersiz bir şey görmüyorum:

--- Query-1-posts
SELECT id, content 
FROM posts
WHERE id = 7


--- Query-2-comments
SELECT * 
FROM comments 
WHERE post_id = 7

Bunun (neredeyse) en etkili yol olduğunu iddia ediyorum (neredeyse posts.idtüm sütunlara ihtiyaç duymadığınız için ve neredeyse tüm sütunlara ihtiyacınız olmadığı için comments.*)

Todd'un yorumunda işaret ettiği gibi, veritabanından verileri gösterime hazır hale getirmesini istememelisiniz. Bunu yapmak uygulamanın işi. Her ekran işleminde ihtiyaç duyduğunuz sonuçları elde etmek için (bir ya da birkaç) sorgu yazabilirsiniz, böylece kablo üzerinden (ya da bellek veri yolu) db'den uygulamaya gönderilen verilerde gereksiz bir tekrarlama olmaz.

ORM'ler hakkında gerçekten konuşamam ama belki bazıları bizim için bu işin bir kısmını yapabilir.

Bir web sunucusu ile bir müşteri arasında veri dağıtımında benzer teknikler kullanılabilir. Diğer teknikler (önbellekleme gibi) kullanılır, böylece veritabanı (veya web veya diğer sunucu) yinelenen isteklerle aşırı yüklenmez.

Tahminime göre, SQL gibi standartlar tek bir alanda uzmanlaşmış kalırsa ve bir alanın tüm alanlarını kapsamazsa en iyisidir.

Öte yandan, SQL standardını belirleyen komite ileride başka türlü düşünebilir ve böyle bir ek özellik için standardizasyon sağlayabilir. Ama bu bir gecede tasarlanabilecek bir şey değil.


1
Demek istediğim, başvurumun sadece bir yerine iki veritabanı çağrısının genel giderine ve gecikmesine neden olması gerektiği anlamına gelmiyordu. Bunun dışında, bir katılım yapmak da sadece gösterime hazır bir formatta veri döndürmek değil mi? Veya bir veritabanı görünümü kullanarak? İsterseniz basitçe daha küçük sorgular çalıştırarak ve uygulamanızda bunları bir araya dikilerek de onları engelleyebilirsiniz, ancak yine de faydalı araçlardır. Ne teklif ettiğimin, daha kolay ve daha fazla performans göstermesinin yanı sıra, bir birleşmeden önemli ölçüde farklı olduğunu sanmıyorum.

2
@ Değerli: Birden fazla sorgu çalıştırmak için herhangi bir ek yükün olması gerekmez. Çoğu veritabanı, tek bir grupta birden fazla sorgu göndermenize ve tek bir sorgudan birden fazla sonuç kümesi almanıza izin verir.
Daniel Pryden

@PreciousBodilyFluids - ypercube'in cevabındaki SQL pasajı, tek bir veritabanı çağrısında gönderilecek ve tek bir cevapta iki sonuç kümesi döndürecek tek bir sorundur.
Carson63000

5

Düzgün, tartışmalı bir cevapla cevaplayamıyorum, bu yüzden yanlış olursam beni unutmaya çekinmeyin (ama lütfen beni düzeltin, böylece yeni bir şeyler öğrenebiliriz). Sebebi ilişkisel veritabanlarının ilişkisel modelde toplandığını düşünüyorum, ki bu da "birinci dereceden mantık" denilen hakkında hiçbir şey bilmediğim bir şeye dayanıyor. Muhtemelen isteyebileceğiniz şey, kavramsal / mantıksal çerçeveye sığmaz, ilişkisel veritabanları üzerine kurulur. Dahası, sormak istedikleriniz genellikle grafik veri tabanları ile kolayca çözülür ve elde etmek istediğinizle çakışan veri tabanının kavramsallaştırılması olduğuna dair daha fazla ipucu verir.


5

En azından SQLServer'ın XML kullandığınızda iç içe geçmiş sorguları desteklediğini biliyorum.

SELECT id, content, (SELECT * FROM comments WHERE post_id = posts.id FOR XML PATH('comments'), TYPE) AS comments
FROM posts
WHERE id = 7
FOR XML PATH('posts')

Buradaki sorun RDBMS'den destek almama değil, iç içe geçmiş tabloları desteklememektir.

Ayrıca, iç birleşim kullanmanıza engel olan şey nedir?

SELECT id, content, comments.*
FROM posts inner join comments on comments.post_id = posts.id
WHERE id = 7

İç birleştirmeye iç içe geçmiş bir tablo olarak gerçek bakabilirsiniz, yalnızca ilk 2 alanın içeriği bir süre tekrarlanır. Çok katılım katılmak hakkında endişelenmiyorum, böyle bir sorguda tek yavaş kısmı veritabanından istemciye io. Bu yalnızca içerik çok miktarda veri içeriyorsa sorun olacaktır. Bu durumda, biri select id, contentiç diğeri iç birleşimi olan iki sorgu öneririm select posts.id, comments.*. Bu, yalnızca 2 sorgu kullanacağınız gibi, birden fazla gönderiyle bile ölçeklenir.


Sorular buna değiniyor. İki gidiş-dönüş yapmak zorundasınız (optimal değil) veya ilk iki sütunda (ayrıca en uygun değil) yedek veriyi geri göndermelisiniz. En uygun çözümü istiyor (bence gerçekçi değil).
Scott Whitlock

Biliyorum, ama en uygun çözüm olarak emmek bir şey yok. Tartışabileceğim tek şey, ek yükün minimum olacağı ve nereye bağlı olacağıdır. En uygun çözümü istiyorsanız, kıyaslayın ve farklı yaklaşımlar deneyin. XML çözümü bile belirli bir duruma bağlı olarak daha yavaş olabilir ve NoSQL veri merkezlerini bilmiyorum, bu yüzden benzer bir şey olup olmadığını söyleyemem for xml.
Dorus

5

Aslında Oracle istediğinizi destekler ancak alt sorguyu "imleç" anahtar kelimesiyle kaydırmanız gerekir. Sonuçlar açık imleç ile alınır. Java’da, örneğin yorumlar sonuç setleri olarak gösterilecektir. Bununla ilgili daha fazla bilgi için Oracle'ın "CURSOR Expression" belgelerine bakın.

SELECT id, content, cursor(SELECT * FROM comments WHERE post_id = 7) AS comments
FROM posts
WHERE id = 7

1

Bazıları yuvalamayı (hiyerarşik) destekler.

Bir sorgulama istediyseniz, kendi kendine referans veren bir tabloya sahip olabilirsiniz. Bazı RDMS bu kavramı desteklemektedir. Örneğin, SQL Server ile bir sıradüzenli sorgu için Ortak Tablo İfadeleri (CTE'ler) kullanılabilir.

Senin durumunda Mesajlar Seviye 0’da olacak ve ardından tüm yorumlar Seviye 1’de olacaktı.

Diğer seçenekler 2 sorundur veya geri gönderilen her kayıt için (başkalarının bahsettiği) ilave bilgiler içeren bir Üyeliktir.

Hiyerarşik Örnek:

https://stackoverflow.com/questions/14274942/sql-server-cte-and-recursion-example

Yukarıdaki bağlantıda EmpLevel, iç içe geçme düzeyini (veya hiyerarşisini) gösterir.


SQL Server'da alt sonuçlar hakkında hiçbir belge bulamıyorum. CTE kullanırken bile. Resultset ile, yeterince güçlü yazılan sütunlara sahip veri satırlarını kastediyorum. Cevabınıza referans ekleyebilir misiniz?
SandRock

@SandRock - Bir veritabanı, bir SQL Query'den ayarlanan tek bir sonuç geri gönderir. Sorgudaki düzeyleri belirleyerek işlenmesi gereken hiyerarşik veya iç içe geçmiş bir sonuç kümesi oluşturabilirsiniz. Bence şu anda en yakın olan, iç içe geçmiş verileri döndürmeye başlayacağız.
Jon Raynor,

0

Üzgünüm, sorununuzu tam olarak anladığımdan emin değilim.

MSSQL'de sadece 2 SQL İfadesini çalıştırabilirsiniz.

SELECT id, content
FROM posts
WHERE id = 7

SELECT * FROM comments WHERE post_id = 7

Ve 2 sonuç kümenizi aynı anda döndürür.


Soruyu soran kişi bunun daha az verimli olduğunu söylüyor, çünkü veri tabanına iki kez gidişatla sonuçlanıyor ve genellikle genel gider nedeniyle gidiş dönüşleri en aza indirmeye çalışıyoruz. Bir tur atmak ve iki masayı da geri almak istiyor.
Scott Whitlock

Ama bir gidiş dönüş olacak. stackoverflow.com/questions/2336362/…
Biff MaGriff

0

RDBM'ler teoriye dayanır ve teoriye sadık kalırlar. Bu, bazı güzel tutarlılık ve matematiksel olarak kanıtlanmış güvenilirlik sağlar.

Model basit ve tekrar teoriye dayandığından, insanların optimizasyon ve birçok uygulama yapmalarını kolaylaştırıyor. Bu, herkesin biraz farklı yaptığı NoSQL'in aksine.

Geçmişte hiyerarşik veri tabanları yapmak için girişimlerde bulunuldu ancak IIRC (google gibi görünmüyor) sorunlar oldu (çevrimler ve eşitlik akla geliyor).


0

Özel bir ihtiyacın var. İstediğiniz formatta bir veri tabanından veri çıkarmanız tercih edilir, böylece istediğiniz şeyi yapabilirsiniz.

Bazı şeylerin veritabanları da yapmazlar, ancak bunları yapmaları imkansız değildir. Diğer uygulamalara biçimlendirme yapmak mevcut öneridir, ancak neden yapılamadığını haklı çıkarmaz.

Önerinize aykırı olan tek iddia, bu sonucun "sql" şeklinde ele alınabilmesi. Veritabanında bir sonuç oluşturmak ve onunla çalışmamak veya bir dereceye kadar manipüle etmek kötü bir fikir olabilir. Diyelim ki önerdiğiniz şekilde bir görünüm oluşturdum, başka bir select deyimine nasıl eklerim? Veritabanları sonuç almak ve onlarla bir şeyler yapmak ister. Başka bir masaya nasıl katılırım? Sonuç kümenizi bir başkasıyla nasıl karşılaştırabilirim?

O zaman RDMS'nin yararı sql'nin esnekliğidir. Bir tablodan veri seçmek için kullanılan sözdizimi, sistemdeki kullanıcıların veya diğer nesnelerin listesine oldukça yakındır (En azından amaç budur). Tamamen farklı bir şey yapmanın bir anlamı olduğundan emin değilim. Bunları prosedür kodunu / imlecini veya BLOBS verilerini çok verimli bir şekilde işleme koydukları noktaya bile getirmediler.


0

Benim düşünceme göre, çoğunlukla SQL ve toplama sorgularının gerçekleştirilme biçimi nedeniyle - toplama işlevleri ve gruplama, sonuçları döndürmek için büyük 2 boyutlu satır kümelerinde yürütülür. Bu başlangıçtan beri böyle ve çok hızlı (NoSQL çözümlerinin çoğu toplamada oldukça yavaş ve karmaşık sorgular yerine denormalize şemaya dayanıyor)

Elbette PostgreSQL, nesne yönelimli veritabanından bazı özelliklere sahiptir. Bu postalara ( mesaj ) göre, ihtiyacınız olanı özel toplama oluşturarak elde edebilirsiniz.

Şahsen ben toplama uygulama tarafı yapan ve tembel yükleme gibi özellikleri destekleyen performansı destekleyen Doctrine ORM (PHP) gibi çerçeveler kullanıyorum.


0

PostgreSQL, Diziler ve JSON dahil olmak üzere çeşitli yapılandırılmış veri türlerini desteklemektedir . SQL veya yerleşik yordamsal dillerden birini kullanarak, rasgele karmaşık bir yapıya sahip değerler oluşturabilir ve bunları uygulamanıza geri verebilirsiniz. Yapısal türlerden herhangi birinin sütunlarını içeren tablolar da oluşturabilirsiniz, ancak tasarımınızı gereksiz yere denormalize edip etmediğinizi dikkatlice düşünmelisiniz.


1
Bu, önceki 13
cevapta

Soru özellikle JSON'dan bahseder ve bu cevap, JSON'un en az bir RDBMS'den gelen sorgularda iade edilebileceğini gösteren tek cevaptır. Yanlış bir önermeye dayandığını ve bu nedenle kesin bir cevap bekleyemeyeceğini söylemek üzerine yorum yapmayı tercih ederim. Ancak, StackExchange bunu yapmama izin vermiyor.
Jonathan Rogers
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.