Misalnya, katakan saya ingin mengambil Pengguna dan semua nomor telepon serta alamat emailnya. Nomor-nomor telepon dan email disimpan dalam tabel terpisah, Satu pengguna ke banyak telepon / email. Saya bisa melakukan ini dengan mudah:
SELECT * FROM users user
LEFT JOIN emails email ON email.user_id=user.id
LEFT JOIN phones phone ON phone.user_id=user.id
Masalah * dengan ini adalah bahwa itu mengembalikan nama pengguna, DOB, warna favorit, dan semua informasi lain yang disimpan dalam tabel pengguna berulang-ulang untuk setiap catatan (pengguna email catatan telepon), mungkin memakan bandwidth dan memperlambat bawah hasilnya.
Bukankah lebih baik jika mengembalikan satu baris untuk setiap pengguna, dan dalam catatan itu ada daftar email dan daftar telepon? Itu akan membuat data lebih mudah untuk dikerjakan.
Saya tahu Anda bisa mendapatkan hasil seperti ini menggunakan LINQ atau mungkin kerangka kerja lain, tetapi tampaknya menjadi kelemahan dalam desain yang mendasari database relasional.
Kita bisa menyiasatinya dengan menggunakan NoSQL, tetapi tidakkah seharusnya ada jalan tengah?
Apakah saya melewatkan sesuatu? Mengapa ini tidak ada?
* Ya, didesain seperti ini. Saya mengerti. Saya bertanya-tanya mengapa tidak ada alternatif yang lebih mudah untuk dikerjakan. SQL dapat terus melakukan apa yang dilakukannya tetapi kemudian mereka dapat menambahkan satu atau dua kata kunci untuk melakukan sedikit pemrosesan pasca yang mengembalikan data dalam format bersarang alih-alih produk kartesius.
Saya tahu ini dapat dilakukan dalam bahasa scripting pilihan Anda, tetapi mengharuskan server SQL baik mengirim data yang berlebihan (contoh di bawah) atau agar Anda mengeluarkan beberapa pertanyaan seperti SELECT email FROM emails WHERE user_id IN (/* result of first query */)
.
Alih-alih meminta MySQL mengembalikan sesuatu yang mirip dengan ini:
[
{
"name": "John Smith",
"dob": "1945-05-13",
"fav_color": "red",
"email": "[email protected]",
},
{
"name": "John Smith",
"dob": "1945-05-13",
"fav_color": "red",
"email": "[email protected]",
},
{
"name": "Jane Doe",
"dob": "1953-02-19",
"fav_color": "green",
"email": "[email protected]",
}
]
Dan kemudian harus mengelompokkan pada beberapa pengidentifikasi unik (yang berarti saya perlu mengambil itu juga!) Sisi klien untuk memformat ulang hasil yang ditetapkan seperti yang Anda inginkan, cukup kembalikan ini:
[
{
"name": "John Smith",
"dob": "1945-05-13",
"fav_color": "red",
"emails": ["[email protected]", "[email protected]"]
},
{
"name": "Jane Doe",
"dob": "1953-02-19",
"fav_color": "green",
"emails": ["[email protected]"],
}
]
Sebagai alternatif, saya dapat mengeluarkan 3 pertanyaan: 1 untuk pengguna, 1 untuk email, dan 1 untuk nomor telepon, tetapi kemudian set hasil email dan nomor telepon harus berisi user_id sehingga saya dapat mencocokkannya kembali dengan pengguna. Saya sebelumnya mengambil. Sekali lagi, data yang berlebihan dan pasca pengolahan yang tidak perlu.
Jawaban:
Jauh di lubuk hati, dalam nyali database relasional, semua baris dan kolomnya. Itu adalah struktur di mana database relasional dioptimalkan untuk bekerja dengannya. Kursor bekerja pada baris individual pada satu waktu. Beberapa operasi membuat tabel sementara (sekali lagi, perlu baris dan kolom).
Dengan bekerja hanya dengan baris dan hanya mengembalikan baris, sistem dapat menangani lebih baik dengan memori dan lalu lintas jaringan.
Seperti yang disebutkan, ini memungkinkan dilakukannya optimasi tertentu (indeks, gabungan, serikat pekerja, dll ...)
Jika seseorang ingin struktur pohon bersarang, ini mengharuskan seseorang menarik semua data sekaligus. Lewatlah sudah optimasi untuk kursor di sisi database. Demikian juga, lalu lintas melalui jaringan menjadi satu ledakan besar yang bisa memakan waktu lebih lama daripada tetesan lambat baris demi baris (ini adalah sesuatu yang kadang-kadang hilang di dunia web saat ini).
Setiap bahasa memiliki array di dalamnya. Ini adalah hal-hal mudah untuk bekerja dan berinteraksi dengan Dengan menggunakan struktur yang sangat primitif, penggerak antara database dan program - tidak peduli bahasa apa - dapat bekerja dengan cara yang sama. Begitu seseorang mulai menambahkan pohon, struktur dalam bahasa menjadi lebih kompleks dan lebih sulit untuk dilintasi.
Tidaklah sulit bagi bahasa pemrograman untuk mengubah baris yang dikembalikan ke struktur lain. Jadikan itu menjadi pohon atau hash set atau biarkan sebagai daftar baris yang bisa Anda ulangi.
Ada juga sejarah yang bekerja di sini. Mentransfer data terstruktur adalah sesuatu yang buruk di masa lalu. Lihatlah format EDI untuk mendapatkan gambaran tentang apa yang mungkin Anda minta. Pohon juga menyiratkan rekursi - yang beberapa bahasa tidak mendukung (dua bahasa paling penting di masa lalu tidak mendukung rekursi - rekursi tidak memasuki Fortran sampai F90 dan era COBOL tidak melakukannya).
Dan sementara bahasa saat ini memiliki dukungan untuk rekursi dan tipe data yang lebih maju, sebenarnya tidak ada alasan yang baik untuk mengubah banyak hal. Mereka bekerja, dan mereka bekerja dengan baik. Orang-orang yang sedang mengubah hal-hal yang database NoSQL. Anda dapat menyimpan pohon dalam dokumen dalam satu dokumen berbasis. LDAP (yang sebenarnya sudah tua) juga merupakan sistem berbasis pohon (meskipun mungkin bukan yang Anda cari). Siapa tahu, mungkin hal berikutnya dalam database nosql akan menjadi salah satu yang mengembalikan kembali kueri sebagai objek json.
Namun, database relasional 'lama' ... mereka bekerja dengan baris karena itulah yang mereka kuasai dan semuanya dapat berbicara dengan mereka tanpa masalah atau terjemahan.
Dari RFC 1925 - The Twelve Networking Truths
sumber
Ini mengembalikan persis apa yang Anda minta: satu set rekaman berisi produk Cartesian yang ditentukan oleh gabungan. Ada banyak skenario yang valid di mana itu persis apa yang Anda inginkan, sehingga mengatakan bahwa SQL memberikan hasil yang buruk (dan dengan demikian menyiratkan bahwa akan lebih baik jika Anda mengubahnya) sebenarnya akan mengacaukan banyak permintaan.
Apa yang Anda alami dikenal sebagai " Object / Relational Impedance Mismatch, " kesulitan teknis yang timbul dari kenyataan bahwa model data berorientasi objek dan model data relasional secara fundamental berbeda dalam beberapa cara. LINQ dan kerangka kerja lainnya (dikenal sebagai ORM, Object / Relational Mappers, bukan secara kebetulan,) tidak secara ajaib "menyiasati ini;" mereka hanya mengeluarkan pertanyaan yang berbeda. Itu bisa dilakukan dalam SQL juga. Begini cara saya melakukannya:
Iterasi daftar pengguna dan buat daftar ID.
Dan kemudian Anda melakukan sisi klien bergabung. Ini adalah bagaimana LINQ dan kerangka kerja lain melakukannya. Tidak ada sihir nyata yang terlibat; hanya lapisan abstraksi.
sumber
Anda bisa menggunakan fungsi bawaan untuk menggabungkan catatan bersama. Di MySQL Anda dapat menggunakan
GROUP_CONCAT()
fungsi dan di Oracle Anda dapat menggunakanLISTAGG()
fungsi.Berikut ini contoh tampilan kueri di MySQL:
Ini akan mengembalikan sesuatu seperti
sumber
Masalahnya adalah Anda tidak cukup selektif. Anda meminta segalanya ketika Anda mengatakannya
... dan Anda mendapatkannya (termasuk DOB dan warna favorit).
Anda mungkin harus sedikit lebih (ahem) ... selektif, dan mengatakan sesuatu seperti:
Mungkin juga Anda mungkin melihat catatan yang terlihat seperti duplikat karena a
user
mungkin bergabung dengan banyakemail
catatan, tetapi bidang yang membedakan keduanya tidak ada dalamSelect
pernyataan Anda , jadi Anda mungkin ingin mengatakan sesuatu sepertiJuga, saya perhatikan Anda sedang melakukan
LEFT JOIN
. Ini akan bergabung dengan semua catatan di sebelah kiri gabungan (yaituusers
) untuk semua catatan di sebelah kanan, atau dengan kata lain:( http://en.wikipedia.org/wiki/Join_(SQL)#Left_outer_join )
Jadi pertanyaan lain adalah apakah Anda benar - benar perlu bergabung dengan kiri, atau akan sebuah
INNER JOIN
sudah cukup? Mereka adalah tipe gabungan yang sangat berbeda.Jika Anda benar-benar ingin satu kolom dalam set hasil berisi daftar yang dihasilkan saat itu juga, itu bisa dilakukan tetapi bervariasi tergantung pada basis data yang Anda gunakan. Oracle memiliki
listagg
fungsi .Pada akhirnya, saya pikir masalah Anda mungkin terpecahkan jika Anda menulis ulang kueri Anda dekat dengan sesuatu seperti ini:
sumber
left join
menjadiinner join
. Dalam hal ini, ini tidak akan mengurangi "pengulangan" yang dikeluhkan oleh pengguna; itu hanya akan menghilangkan pengguna yang tidak memiliki ponsel atau email. hampir tidak ada perbaikan. juga, ketika menafsirkan "semua catatan di sebelah kiri untuk semua catatan di sebelah kanan" melompatiON
kriteria, yang memangkas semua hubungan 'salah' yang melekat dalam produk Cartesian tetapi menyimpan semua bidang yang berulang.Kueri selalu menghasilkan set data tabular persegi panjang (tidak bergerigi). Tidak ada sub-set bersarang dalam satu set. Dalam dunia set semuanya adalah persegi panjang murni yang tidak bersarang.
Anda dapat menganggap gabungan sebagai menempatkan 2 set berdampingan. Kondisi "aktif" adalah bagaimana catatan di setiap set dicocokkan. Jika pengguna memiliki 3 nomor telepon, maka Anda akan melihat duplikasi 3 kali dalam info pengguna. Set persegi panjang bergerigi harus diproduksi oleh kueri. Ini hanya sifat bergabung dengan set dengan hubungan 1-ke-banyak.
Untuk mendapatkan yang Anda inginkan, Anda harus menggunakan kueri terpisah seperti yang dijelaskan Mason Wheeler.
Hasil kueri ini masih berupa set persegi panjang yang tidak bergerigi. Seperti segala sesuatu di dunia set.
sumber
Anda harus memutuskan di mana kemacetan ada. Bandwidth antara database dan aplikasi Anda biasanya cukup cepat. Tidak ada alasan kebanyakan database tidak dapat mengembalikan 3 dataset terpisah dalam satu panggilan dan tidak ada yang bergabung. Maka Anda bisa bergabung bersama-sama di aplikasi Anda jika Anda mau.
Jika tidak, Anda ingin database untuk menyatukan dataset ini dan kemudian menghapus semua nilai yang diulang di setiap baris yang merupakan hasil dari gabungan dan tidak harus baris itu sendiri memiliki data duplikat seperti dua orang dengan nama atau nomor telepon yang sama. Sepertinya banyak over-head untuk menghemat bandwidth. Anda akan lebih baik fokus mengembalikan data yang lebih sedikit dengan pemfilteran yang lebih baik dan menghapus kolom yang tidak Anda butuhkan. Karena Select * tidak pernah digunakan dalam sumur produksi yang tergantung.
sumber
Sederhananya, jangan bergabung dengan data Anda jika Anda ingin hasil yang berbeda untuk permintaan pengguna dan permintaan nomor telepon, jika tidak karena orang lain telah menunjukkan "Set" atau data akan berisi bidang tambahan untuk setiap baris.
Menerbitkan 2 kueri berbeda alih-alih satu dengan gabungan.
Dalam prosedur tersimpan atau inline parameterisasi sql craft 2 kueri dan kembalikan hasil keduanya. Sebagian besar basis data dan bahasa mendukung beberapa set hasil.
Sebagai contoh, SQL Server dan C # menyelesaikan fungsionalitas ini dengan menggunakan
IDataReader.NextResult()
.sumber
Anda melewatkan sesuatu. Jika Anda ingin mendenormalisasi data Anda, Anda harus melakukannya sendiri.
sumber
Konsep penutupan relasional pada dasarnya berarti bahwa hasil dari setiap kueri adalah relasi yang dapat digunakan dalam pertanyaan lain seolah-olah itu adalah tabel dasar. Ini adalah konsep yang kuat karena membuat kueri dapat dikomposisi.
Jika SQL memungkinkan Anda untuk menulis kueri yang menghasilkan struktur data bersarang, Anda akan melanggar prinsip ini. Struktur data bersarang bukan merupakan relasi, jadi Anda akan memerlukan bahasa query baru, atau ekstensi kompleks untuk SQL, untuk menanyakannya lebih lanjut atau untuk bergabung dengannya yang memiliki relasi lain.
Pada dasarnya Anda akan membangun DBMS hirarkis di atas DBMS relasional. Ini akan jauh lebih kompleks untuk keuntungan yang meragukan, dan Anda kehilangan keuntungan dari sistem relasional yang konsisten.
Saya mengerti mengapa kadang-kadang akan lebih nyaman untuk dapat menghasilkan data terstruktur secara hierarkis dari SQL, tetapi biaya dalam kompleksitas tambahan di seluruh DBMS untuk mendukung ini jelas tidak sepadan.
sumber
Pls merujuk pada penggunaan fungsi STUFF yang mengelompokkan beberapa baris (nomor telepon) dari sebuah kolom (kontak) yang dapat diekstraksi sebagai sel tunggal dengan nilai yang dibatasi dari satu baris (pengguna).
Hari ini kami menggunakan ini secara ekstensif tetapi menghadapi beberapa masalah CPU dan kinerja yang tinggi. Tipe data XML adalah pilihan lain tetapi perubahan desain bukan tingkat permintaan.
sumber
STUFF
mirip dengan sambatan. Tidak yakin bagaimana itu berlaku untuk pertanyaan saya.