Mengapa basis data relasional tidak mendukung pengembalian informasi dalam format bersarang?

46

Misalkan saya sedang membangun blog yang ingin saya posting dan komentar. Jadi saya membuat dua tabel, tabel 'posting' dengan kolom 'id' integer autoincrementing, dan tabel 'komentar' yang memiliki kunci asing 'post_id'.

Lalu saya ingin menjalankan apa yang mungkin akan menjadi permintaan paling umum saya, yaitu untuk mengambil posting dan semua komentarnya. Menjadi agak baru untuk database relasional, pendekatan yang tampak paling jelas bagi saya adalah menulis kueri yang akan terlihat seperti:

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

Yang akan memberi saya id dan konten posting yang saya inginkan, bersama dengan semua baris komentar yang relevan yang dikemas dengan rapi dalam array (representasi bersarang seperti yang akan Anda gunakan di JSON). Tentu saja, SQL dan basis data relasional tidak berfungsi seperti ini, dan yang paling dekat yang bisa mereka dapatkan adalah melakukan penggabungan antara 'posting' dan 'komentar' yang akan mengembalikan banyak duplikasi data yang tidak perlu (dengan informasi posting yang sama diulang di setiap baris), yang berarti waktu pemrosesan dihabiskan baik pada basis data untuk menggabungkan semuanya dan pada ORM saya untuk menguraikan dan membatalkan semuanya.

Bahkan jika saya menginstruksikan ORM saya untuk dengan bersemangat memuat komentar posting, yang terbaik akan lakukan adalah mengirimkan satu permintaan untuk posting, dan kemudian permintaan kedua untuk mengambil semua komentar, dan kemudian menyatukannya di sisi klien, yang juga tidak efisien.

Saya mengerti bahwa basis data relasional adalah teknologi yang telah terbukti (ya, mereka lebih tua dari saya), dan ada banyak penelitian yang dilakukan selama beberapa dekade, dan saya yakin ada alasan yang sangat bagus mengapa mereka (dan SQL standar) dirancang untuk berfungsi seperti yang mereka lakukan, tapi saya tidak yakin mengapa pendekatan yang saya sebutkan di atas tidak mungkin. Tampaknya bagi saya menjadi cara yang paling sederhana dan jelas untuk menerapkan salah satu hubungan paling mendasar antara catatan. Mengapa basis data relasional tidak menawarkan sesuatu seperti ini?

(Penafian: Saya kebanyakan menulis webapp menggunakan Rails dan NoSQL datastores, tetapi baru-baru ini saya sudah mencoba Postgres, dan saya sangat menyukainya. Saya tidak bermaksud menyerang basis data relasional, saya hanya bingung.)

Saya tidak bertanya bagaimana mengoptimalkan aplikasi Rails, atau cara meretas masalah ini di basis data tertentu. Saya bertanya mengapa standar SQL bekerja seperti ini ketika tampaknya berlawanan dengan intuisi dan boros bagi saya. Pasti ada beberapa alasan historis mengapa perancang SQL asli ingin hasil mereka terlihat seperti ini.

PreciousBodilyFluids
sumber
1
tidak semua orms bekerja seperti itu. hibernate / nhibernate memungkinkan gabungan ditentukan, dan dapat dengan cepat memuat seluruh pohon objek dari satu permintaan.
nathan gonzalez
1
juga, sementara poin yang menarik dari diskusi, saya tidak yakin ini benar-benar dapat dijawab tanpa mengadakan pertemuan dengan ansi sql guys
nathan gonzalez
@nathan: Ya, tidak semua. Saya telah menggunakan Sequel yang memungkinkan Anda memilih pendekatan mana yang Anda sukai untuk kueri ( dokumen ) tertentu, tetapi mereka tetap mendorong pendekatan multi-kueri (untuk alasan kinerja, saya kira).
5
Karena RDBMS dirancang untuk menyimpan dan mengambil set - ini tidak dimaksudkan untuk mengembalikan data untuk ditampilkan. Anggap saja seperti MVC - mengapa akan mencoba menerapkan tampilan dengan biaya membuat model lebih lambat atau lebih sulit untuk digunakan? RDBMS menawarkan manfaat yang tidak dapat dimiliki oleh database NoSQL (dan sebaliknya) - jika Anda menggunakannya karena ini adalah alat yang tepat untuk menyelesaikan masalah Anda, Anda tidak akan meminta untuk mengembalikan data yang siap untuk ditampilkan.
1
Mereka melihat xml
Ian

Jawaban:

42

Tanggal CJ ​​menjelaskan lebih detail tentang ini di Bab 7 dan Lampiran B dari SQL dan Teori Relasional . Anda benar, tidak ada dalam teori relasional yang melarang tipe data atribut menjadi relasi itu sendiri, asalkan itu tipe relasi yang sama di setiap baris. Teladan Anda akan memenuhi syarat.

Tetapi Date mengatakan struktur seperti ini "biasanya - tetapi tidak selalu - dikontraindikasikan" (yaitu Ide Buruk) karena hierarki hubungan asimetris . Misalnya, transformasi dari struktur bersarang ke struktur "flat" yang sudah dikenal tidak selalu dapat dibalik untuk menciptakan kembali sarang.

Pertanyaan, kendala, dan pembaruan lebih kompleks, lebih sulit untuk ditulis, dan lebih sulit untuk didukung oleh RDBMS jika Anda mengizinkan atribut bernilai relasi (RVA).

Hal ini juga muddies basis data prinsip-prinsip desain, karena terbaik hirarki hubungan tidak begitu jelas. Haruskah kita merancang hubungan Pemasok dengan RVA bersarang untuk suku cadang yang dipasok oleh Pemasok tertentu? Atau hubungan Bagian dengan RVA bersarang untuk pemasok yang memasok Bagian tertentu? Atau simpan keduanya, untuk memudahkan menjalankan berbagai jenis kueri?

Ini adalah dilema yang sama yang dihasilkan dari basis data hierarkis dan model basis data berorientasi dokumen . Akhirnya, kompleksitas dan biaya mengakses struktur data bersarang mendorong perancang untuk menyimpan data secara berlebihan untuk pencarian yang lebih mudah dengan berbagai pertanyaan. Model relasional mencegah redundansi, sehingga RVA dapat bekerja melawan tujuan pemodelan relasional.

Dari apa yang saya pahami (saya belum menggunakannya), Rel dan Dataphor adalah proyek RDBMS yang mendukung atribut yang bernilai relasi.


Komentar ulang dari @dportas:

Tipe terstruktur adalah bagian dari SQL-99, dan Oracle mendukung ini. Tetapi mereka tidak menyimpan beberapa tupel di tabel bersarang per baris tabel dasar. Contoh umum adalah atribut "alamat" yang tampaknya merupakan satu kolom dari tabel dasar, tetapi memiliki sub-kolom lebih lanjut untuk jalan, kota, kode pos, dll.

Tabel bersarang juga didukung oleh Oracle, dan ini memungkinkan beberapa tupel per baris tabel dasar. Tetapi saya tidak sadar bahwa ini adalah bagian dari SQL standar. Dan ingatlah kesimpulan dari satu blog: "Saya tidak akan pernah menggunakan tabel bersarang dalam pernyataan CREATE TABLE. Anda menghabiskan seluruh waktu Anda MENG-NESTING mereka untuk menjadikannya berguna lagi!"

Bill Karwin
sumber
3
Saya tidak ingin benar-benar menyimpan satu relasi di dalam relasi yang lain - relasi tersebut berada di tabel terpisah dan didenormalkan seperti biasa. Saya hanya bertanya mengapa penyisipan hasil semacam ini tidak diizinkan dalam kueri, ketika tampaknya lebih intuitif bagi saya daripada model gabung.
PreciousBodilyFluids
Set dan tabel hasil adalah sejenis. Date menyebut mereka relasi dan relavars secara berurutan (dengan analogi, 42 adalah bilangan bulat, sedangkan variabel xdapat memiliki nilai bilangan bulat 42). Operasi yang sama berlaku untuk relasi dan relade, sehingga strukturnya harus kompatibel.
Bill Karwin
2
SQL standar mendukung tabel bersarang. Mereka disebut "tipe terstruktur". Oracle adalah salah satu DBMS yang memiliki fitur ini.
nvogel
2
Bukankah agak tidak masuk akal untuk berpendapat bahwa untuk menghindari duplikasi data, Anda harus menulis pertanyaan Anda dengan cara yang datar, duplikasi data?
Eamon Nerbonne
1
@EamonNerbonne, simetri operasi relasional. Misalnya, proyeksi. Jika saya PILIH beberapa sub-atribut dari RVA, bagaimana saya bisa menerapkan operasi terbalik terhadap set hasil untuk mereproduksi hierarki asli? Saya menemukan halaman halaman 293 buku Date ada di Google Books, jadi Anda dapat melihat apa yang ia tulis: books.google.com/...
Bill Karwin
15

Beberapa sistem basis data paling awal didasarkan pada model Hierarchical Database . Ini mewakili data dalam struktur seperti pohon dengan orang tua dan anak-anak, seperti yang Anda sarankan di sini. HDMS sebagian besar digantikan oleh database yang dibangun di atas model relasional. Alasan utama untuk ini adalah bahwa RDBMS dapat memodelkan hubungan "banyak ke banyak" yang sulit untuk database hierarkis dan bahwa RDBMS dapat dengan mudah melakukan kueri yang bukan bagian dari desain asli sedangkan HDBMS membatasi Anda untuk melakukan kueri melalui jalur yang ditentukan pada waktu desain.

Masih ada beberapa contoh sistem basis data hierarkis di alam liar, khususnya jendela registri dan LDAP.

Cakupan luas dari subjek ini tersedia di artikel berikut

Steve Weet
sumber
10

Saya kira pertanyaan Anda benar-benar terpusat pada fakta bahwa sementara basis data didasarkan pada logika yang kuat dan menetapkan dasar teroretik dan mereka melakukan pekerjaan yang sangat baik menyimpan, memanipulasi dan mengambil data dalam set (2 dimensi) sambil memastikan integritas referensial, konkurensi dan banyak hal lainnya, mereka tidak menyediakan fitur (tambahan) untuk mengirim (dan menerima) data dalam apa yang bisa disebut format berorientasi objek atau format hierarkis.

Lalu Anda mengklaim bahwa "bahkan jika saya memerintahkan ORM saya untuk dengan bersemangat memuat komentar posting, yang terbaik adalah mengirimkan satu permintaan untuk posting, dan kemudian permintaan kedua untuk mengambil semua komentar, dan kemudian menyatukannya sisi klien, yang juga tidak efisien " .

Saya tidak melihat sesuatu yang tidak efisien dalam mengirim 2 kueri dan menerima 2 kumpulan hasil dengan:

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


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

Saya berpendapat bahwa (hampir) cara yang paling efisien (hampir, karena Anda tidak benar-benar membutuhkan posts.iddan tidak semua kolom dari comments.*)

Seperti yang ditunjukkan Todd dalam komentarnya, Anda seharusnya tidak meminta database untuk mengembalikan data yang siap ditampilkan. Tugas aplikasi untuk melakukan itu. Anda dapat menulis (satu atau beberapa) pertanyaan untuk mendapatkan hasil yang Anda butuhkan untuk setiap operasi tampilan sehingga tidak ada duplikasi yang tidak perlu dalam data yang dikirim melalui kabel (atau bus memori) dari db ke aplikasi.

Saya benar-benar tidak dapat berbicara tentang ORM tetapi mungkin beberapa dari mereka dapat melakukan bagian dari pekerjaan ini untuk kami.

Teknik serupa dapat digunakan dalam pengiriman data antara server web dan klien. Teknik lain (seperti caching) digunakan sehingga basis data (atau web atau server lain) tidak kelebihan dengan permintaan duplikat.

Dugaan saya adalah bahwa standar, seperti SQL, adalah yang terbaik jika mereka tetap berspesialisasi dalam satu bidang dan tidak mencoba untuk mencakup semua bidang bidang.

Di sisi lain, komite yang menetapkan standar SQL mungkin berpikir sebaliknya di masa depan dan memberikan standarisasi untuk fitur tambahan tersebut. Tapi itu bukan sesuatu yang bisa dirancang dalam satu malam.

ypercubeᵀᴹ
sumber
1
Maksud saya tidak efisien dalam arti bahwa aplikasi saya harus mengeluarkan overhead dan penundaan dua panggilan basis data, bukan hanya satu. Selain itu, bukankah bergabung juga hanya mengembalikan data dalam format yang siap untuk ditampilkan? Atau menggunakan tampilan basis data? Anda juga bisa menghindarinya dengan hanya menjalankan kueri yang lebih kecil dan menjahitnya bersama di aplikasi Anda, jika Anda mau, tetapi mereka masih merupakan alat yang berguna. Saya tidak berpikir apa yang saya usulkan secara signifikan berbeda dari bergabung, selain lebih mudah digunakan dan lebih banyak pemain.
2
@ Precious: Tidak perlu ada peningkatan overhead untuk menjalankan beberapa kueri. Sebagian besar database memungkinkan Anda untuk mengirim beberapa kueri dalam satu batch dan menerima beberapa set hasil dari satu query.
Daniel Pryden
@PreciousBodilyFluids - potongan SQL dalam jawaban ypercube adalah permintaan tunggal yang akan dikirim dalam panggilan basis data tunggal dan mengembalikan dua set hasil dalam satu respons.
Carson63000
5

Saya tidak dapat menjawab dengan jawaban yang tepat dan diperdebatkan, jadi jangan ragu untuk mengabaikan saya jika saya salah (tapi tolong perbaiki saya agar kami dapat mempelajari sesuatu yang baru). Saya pikir alasannya adalah bahwa basis data relasional berpusat pada model relasional, yang pada gilirannya didasarkan pada sesuatu yang saya tidak tahu apa-apa tentang yang disebut "logika urutan pertama". Apa yang mungkin Anda tanyakan mungkin tidak sesuai secara konseptual dalam kerangka kerja matematika / logis yang dibangun di atas basis data. Selain itu, apa yang Anda tanyakan biasanya diselesaikan dengan mudah dengan basis data grafik, memberikan lebih banyak petunjuk bahwa itu adalah konsep dasar dari database yang bertentangan dengan apa yang ingin Anda capai.

Stefano Borini
sumber
5

Saya tahu setidaknya SQLServer mendukung kueri bersarang saat Anda menggunakan UNTUK XML.

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')

Masalahnya di sini bukanlah kurangnya dukungan dari RDBMS, tetapi kurangnya dukungan tabel bersarang dalam tabel.

Selain itu, apa yang menghentikan Anda dari menggunakan gabungan dalam?

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

Anda dapat benar-benar melihat bagian dalam bergabung sebagai tabel bersarang, hanya konten dari 2 bidang pertama yang diulang suatu waktu. Saya tidak akan terlalu khawatir tentang kinerja dari join, satu-satunya bagian lambat dalam query seperti ini adalah io dari database ke klien. Ini hanya akan menjadi masalah ketika konten berisi sejumlah besar data. Dalam hal ini saya akan menyarankan dua pertanyaan, satu dengan select id, contentdan satu dengan gabungan dalam dan select posts.id, comments.*. Ini berskala bahkan dengan beberapa pos, karena Anda hanya akan menggunakan 2 kueri.

Dorus
sumber
Pertanyaan membahas ini. Entah Anda harus melakukan dua perjalanan bolak-balik (tidak optimal) atau Anda harus mengembalikan data yang berlebihan di dua kolom pertama (juga tidak optimal). Dia menginginkan solusi yang optimal (tidak realistis menurut saya).
Scott Whitlock
Saya tahu, tapi tidak ada yang menyedot sebagai solusi optimal. Satu-satunya hal yang bisa saya perdebatkan adalah di mana overhead akan minimal dan di mana itu tergantung. Jika Anda menginginkan solusi optimal, patok dan coba berbagai pendekatan. Bahkan solusi XML mungkin lebih lambat tergantung pada situasi spesifik, dan saya tidak terbiasa dengan datastore NoSQL jadi saya tidak bisa mengatakan apakah ia memiliki sesuatu yang mirip for xml.
Dorus
5

Sebenarnya Oracle mendukung apa yang Anda inginkan tetapi Anda harus membungkus sub-kueri dengan kata kunci "cursor". Hasil diambil melalui kursor terbuka. Di Jawa, misalnya komentar akan muncul sebagai set hasil. Lebih lanjut tentang ini, lihat dokumentasi Oracle pada "Ekspresi CURSOR"

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

Beberapa mendukung sarang (hierarkis).

Jika Anda menginginkan satu permintaan, Anda bisa memiliki satu tabel yang mereferensikan dirinya sendiri. Beberapa RDMS mendukung konsep ini. Misalnya, dengan SQL Server seseorang dapat menggunakan Common Table Expressions (CTEs) untuk kueri hierarki.

Dalam kasus Anda Postingan akan berada di Level 0 dan kemudian semua komentar akan berada di Level 1.

Opsi lainnya adalah 2 kueri atau Gabung dengan beberapa informasi tambahan untuk setiap catatan yang dikembalikan (yang orang lain telah sebutkan).

Contoh Hierarkis:

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

Di tautan di atas, EmpLevel menunjukkan level dari sarang (atau hierarki).

Jon Raynor
sumber
Saya tidak dapat menemukan dokumentasi tentang sub-hasil di SQL Server. Bahkan saat menggunakan CTE. Maksud saya adalah baris data dengan kolom yang diketik dengan cukup kuat. Bisakah Anda menambahkan referensi ke jawaban Anda?
SandRock
@ SandRock - Database akan mengirim kembali hasil tunggal yang ditetapkan dari SQL Query. Dengan mengidentifikasi level dalam kueri itu sendiri, Anda dapat membuat set hasil hierarkis atau bersarang yang harus diproses. Saya pikir saat ini yang terdekat kita akan mendapatkan untuk mengembalikan data yang bersarang.
Jon Raynor
0

Maaf, saya tidak yakin saya benar-benar memahami masalah Anda.

Di MSSQL Anda bisa menjalankan 2 Pernyataan SQL.

SELECT id, content
FROM posts
WHERE id = 7

SELECT * FROM comments WHERE post_id = 7

Dan itu akan mengembalikan 2 set hasil Anda secara bersamaan.

Biff MaGriff
sumber
Orang yang mengajukan pertanyaan mengatakan bahwa ini kurang efisien karena menghasilkan dua perjalanan pulang pergi ke basis data, dan kami biasanya mencoba meminimalkan perjalanan pulang-pergi karena overhead. Dia ingin melakukan satu perjalanan pulang pergi dan mendapatkan kedua meja kembali.
Scott Whitlock
Tapi itu akan menjadi perjalanan pulang pergi. stackoverflow.com/questions/2336362/…
Biff MaGriff
0

RDBM didasarkan pada teori dan mereka berpegang pada teori. Ini memungkinkan konsistensi yang bagus, dan keandalan yang terbukti secara matematis.

Karena modelnya sederhana dan didasarkan pada teori, membuatnya mudah bagi orang untuk melakukan optimasi dan banyak implementasi. Ini tidak seperti NoSQL di mana setiap orang melakukannya sedikit berbeda.

Ada upaya di masa lalu untuk membuat database hierarkis tetapi IIRC (sepertinya tidak melihatnya) ada masalah (siklus dan kesetaraan muncul dalam pikiran).

Adam Gent
sumber
0

Anda memiliki kebutuhan spesifik. Akan lebih baik untuk mengekstrak data dari database dalam format yang Anda inginkan, sehingga Anda dapat melakukannya dengan apa yang Anda inginkan.

Beberapa hal yang tidak dilakukan dengan baik oleh basis data, tetapi bagaimanapun juga bukan tidak mungkin untuk membangunnya. Meninggalkan pemformatan ke aplikasi lain adalah rekomendasi saat ini, tetapi tidak membenarkan mengapa itu tidak dapat dilakukan.

Satu-satunya argumen yang saya miliki terhadap saran Anda adalah dapat menangani hasil ini dengan cara "sql". Ini akan menjadi ide yang buruk untuk membuat hasil dalam database dan tidak dapat bekerja dengannya atau memanipulasinya sampai batas tertentu. Katakanlah saya membuat tampilan yang dibangun seperti yang Anda sarankan, bagaimana cara saya memasukkannya dalam pernyataan pilih lain? Database suka mengambil hasil dan melakukan sesuatu dengannya. Bagaimana saya akan bergabung ke meja lain? Bagaimana saya membandingkan hasil yang Anda set dengan yang lain?

Maka manfaat dari RDMS adalah fleksibilitas sql. Sintaks untuk memilih data dari tabel cukup dekat dengan daftar pengguna atau objek lain dalam sistem (Setidaknya itulah tujuannya.). Tidak yakin ada gunanya melakukan sesuatu yang sama sekali berbeda. Mereka bahkan tidak sampai pada titik penanganan kode prosedural / kursor atau BLOBS data dengan sangat efisien.

JeffO
sumber
0

Menurut pendapat saya itu sebagian besar karena SQL dan cara permintaan agregat dilakukan - fungsi agregat dan pengelompokan dieksekusi pada rowset 2-dimensi besar untuk mengembalikan hasil. Begitulah cara itu sejak awal dan sangat cepat (sebagian besar solusi NoSQL cukup lambat dengan agregasi dan mengandalkan skema denormalized alih-alih kueri kompleks)

Tentu saja PostgreSQL memiliki beberapa fitur dari database berorientasi objek. Menurut email ini ( pesan ) Anda dapat mencapai apa yang Anda butuhkan dengan membuat agregat khusus.

Secara pribadi saya menggunakan kerangka kerja seperti Doctrine ORM (PHP) yang melakukan sisi aplikasi agregasi dan fitur pendukung seperti lazy-loading untuk meningkatkan kinerja.

Daimon
sumber
0

PostgreSQL mendukung berbagai tipe data terstruktur, termasuk Array dan JSON . Menggunakan SQL atau salah satu bahasa prosedural tertanam, Anda dapat membangun nilai-nilai dengan struktur rumit yang sewenang-wenang dan mengembalikannya ke aplikasi Anda. Anda juga dapat membuat tabel dengan kolom dari salah satu tipe terstruktur, meskipun Anda harus hati-hati mempertimbangkan apakah Anda mendenormalisasi desain Anda.

Jonathan Rogers
sumber
1
ini tampaknya tidak menawarkan sesuatu yang substansial atas poin yang dibuat dan dijelaskan dalam 13 jawaban sebelumnya
nyamuk
Pertanyaan secara khusus menyebutkan JSON dan jawaban ini adalah satu-satunya yang menunjukkan bahwa JSON dapat dikembalikan dalam permintaan dari setidaknya satu RDBMS. Saya lebih suka mengomentari pertanyaan untuk mengatakan bahwa itu didasarkan pada premis yang salah dan karena itu tidak dapat mengharapkan jawaban yang pasti. Namun, StackExchange tidak akan membiarkan saya melakukan itu.
Jonathan Rogers