Kolom duplikat untuk kueri yang lebih cepat?

30

Judulnya tidak terlalu masuk akal, tapi saya tidak bisa memikirkan judul yang lebih baik untuk masalah ini.

Saya punya tabel berikut

Proyek

  • id
  • nama

Pelanggan

  • id
  • id_project
  • nama

Pembayaran

  • id
  • id_customer
  • tanggal
  • jumlah

Ketika seorang pengguna memasuki sistem, ia akan memiliki akses ke proyek tertentu. Sekarang, saya ingin mendaftar semua pembayaran untuk proyek itu, dan itu seharusnya cukup mudah:

SELECT FROM payments where id_customer in (SELECT id from customers where id_project = 5)

Pertanyaan saya adalah: jika tidak lebih baik menambahkan kolom id_project ke tabel pembayaran dengan cara ini permintaan akan lebih mudah dan lebih cepat.

Gabriel Solomon
sumber
1
jadi kueri bukan masalah bagi RDBMS modern (atau lebih baik, gunakan join).
garik
4
Setuju, dapatkan paket permintaan untuk subselect vs gabung dan lihat mana yang lebih baik
Gayus
1
Saya pikir posting SO ini layak untuk dilihat, karena @igor disebutkan tentang menggunakan GABUNG atau IN
CoderHawk

Jawaban:

52

Tampaknya Anda bertanya apakah denasionalisasi masuk akal.

Denormalisasi adalah proses berusaha mengoptimalkan kinerja baca dari database dengan menambahkan data yang berlebihan atau dengan mengelompokkan data. Dalam beberapa kasus, denormalisasi membantu menutupi ketidakefisienan yang melekat pada perangkat lunak basis data relasional. Basis data yang dinormalisasi relasional memaksakan beban akses yang berat di atas penyimpanan fisik data bahkan jika itu disetel dengan baik untuk kinerja tinggi.

Jawabannya selalu "tergantung", jadi inilah aturan praktis saya:

Jika ...

  • jumlah data tidak besar
  • Anda belum melakukan banyak bergabung
  • dan / atau kinerja basis data saat ini tidak menjadi hambatan

kemudian tetap dinormalisasi . Ya, denormalisasi lebih cepat, tetapi itu juga berarti Anda memiliki data yang berlebihan dalam sistem - data yang harus dipertahankan dan disinkronkan. Tidak ada lagi "satu sumber" untuk data itu, tetapi banyak sumber yang bisa menyimpang. Ini berisiko dari waktu ke waktu, jadi Anda tidak boleh melakukannya kecuali Anda memiliki alasan yang sangat baik untuk melakukannya, didukung oleh beberapa tolok ukur.

Saya hanya akan mendenormalisasi ketika ...

  • jumlah datanya sangat besar
  • bergabung mahal dan Anda harus melakukan banyak dari mereka untuk mendapatkan kembali bahkan pertanyaan sepele
  • kinerja database adalah hambatan dan / atau Anda ingin pergi secepat mungkin

Bergabung sangat cepat pada perangkat keras modern, tetapi mereka tidak pernah gratis.

Jeff Atwood
sumber
9

Anda akan lebih baik menulis ulang kueri sebagai:

SELECT payments.*
FROM   customers
JOIN   payments 
ON     payments.id_customer = customers.id
WHERE  customers.id_project = 5

Meskipun ini tampaknya kurang ringkas dan perencana kueri yang baik akan melihat apa yang Anda coba lakukan dan menjalankan sub-kueri berkorelasi Anda sebagai gantinya di atas, perencana kueri yang buruk akhirnya dapat melakukan pemindaian indeks payments.id_customer(dengan asumsi Anda memiliki indeks yang relevan ) (atau lebih buruk, pemindaian tabel) daripada melakukan hal-hal dengan cara yang lebih efisien. Bahkan perencana kueri yang baik mungkin gagal melihat optimasi jika pengaturan kueri ini dibungkus dengan sesuatu yang lebih rumit. Mengekspresikan hubungan sebagai gabungan daripada sub-kueri dapat membuat lebih banyak perbedaan daripada mengubah struktur data Anda.

Seperti yang dikatakan Jeff, setiap penonaktifan harus dipertimbangkan dengan hati-hati - ini dapat membawa peningkatan kinerja yang mudah, terutama untuk beberapa tujuan pelaporan, tetapi dapat menyebabkan ketidakkonsistenan karena bug dalam logika bisnis pendukung.

Sebagai catatan: Jelas saya tidak tahu bisnis Anda sehingga saya bisa kehilangan sesuatu, tetapi hubungan meja Anda terasa aneh bagi saya. Mereka menyiratkan bahwa Anda tidak pernah dapat memiliki lebih dari satu proyek dengan pelanggan yang sama yang biasanya tidak benar dalam pengalaman saya, setidaknya selama periode yang panjang.

customer     project      payment
--------     --------     -------
                          pa_id
             pr_id    <-- payment
cu_id    <-- customer     

atau jika kurang normal (meskipun saya ragu itu perlu):

customer     project      payment
--------     --------     --------
                          pa_id
             pr_id    <-- payment
cu_id    <-- customer 
           `------------- customer    

Tentu saja itu masih mengabaikan kemungkinan proyek bersama dengan dua pelanggan ...

David Spillett
sumber
3
Aturan kinerja pertama: Jangan pernah gunakan * dalam produksi!
Brian Ballsun-Stanton
@ Brian: titik yang sangat valid. Dan juga implikasi kinerja potensial menghindari * dalam klausa pilih juga menghindari masalah dengan pemesanan kolom dalam view-on-view di MSSQL jika sys.depends keluar dari keteraturan karena DROP VIEW+ CREATE VIEWdigunakan alih-alih ALTER VIEW.
David Spillett
@Brian saya meletakkan * untuk memudahkan penulisan.
Gabriel Solomon
Proyek ini lebih dianggap sebagai aplikasi independen dengan domainnya dan milik pelanggan yang berbeda sehingga pelanggan tidak dapat memiliki akun yang sama pada proyek yang berbeda
Gabriel Solomon
4

Di beberapa basis data, Anda memiliki kemungkinan untuk membuat "Tampilan Terwujud" alih-alih PANDANGAN kompleks dengan sejumlah besar data, berdasarkan permintaan yang kompleks. Ini dapat digunakan untuk menghindari denormalisasi dalam sistem aplikasi yang dikembangkan secara historis. Jika Anda memutuskan untuk menggunakan " Tampilan Terwujud "Anda harus memiliki gagasan yang jelas tentang metode penyegaran dan jumlah penyimpanan yang akan digunakan oleh ...

Christof Prettner
sumber