Daha hızlı sorgular için yinelenen sütun?


30

Başlık çok fazla anlam ifade etmiyor, ancak bu sorun için daha iyi bir başlık düşünemiyorum.

Aşağıdaki tablolara sahibim

Projeler

  • İD
  • isim

Müşteriler

  • İD
  • id_project
  • isim

Ödemeler

  • İD
  • id_customer
  • tarih
  • toplam

Bir kullanıcı sisteme girdiğinde, belirli bir projeye erişebilecek. Şimdi, o proje için tüm ödemeleri listelemek istiyorum ve bu oldukça kolay olmalı:

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

Sorum şu: ödemeler tablosuna bir sütun id_project eklemek daha iyi değilse, bu şekilde sorgular daha kolay ve daha hızlı olacaktır.


1
bu nedenle sorgu modern RDBMS'ler için bir sorun değildir (veya daha iyisi, join kullanın).
garik

4
Katılıyorum, vs alt için bir seçim planı katıl ve hangisinin daha iyi olduğunu gör
Gaius

1
Sanırım bu SO yazısının görülmeye değer olduğunu düşünüyorum , çünkü @organ JOIN veya IN
CoderHawk

Yanıtlar:


52

Denormalizasyonun mantıklı olup olmadığını soruyor gibisin .

Denormalizasyon, yedekli veri ekleyerek veya verileri gruplandırarak bir veritabanının okuma performansını optimize etmeye çalışmaktır. Bazı durumlarda, denormalizasyon, ilişkisel veritabanı yazılımında var olan verimsizlikleri örtmeye yardımcı olur. İlişkisel normalleştirilmiş bir veritabanı, yüksek performans için iyi ayarlanmış olsa bile, verilerin fiziksel depolanması üzerine yoğun erişim yükü getirir.

Cevap her zaman "bağlıdır", işte benim ilk kuralım:

Eğer ...

  • veri miktarı büyük değil
  • sen zaten bir tonaj yapmıyorsun
  • ve / veya veritabanı performansı şu anda bir tıkanıklık değil

sonra normalize kalın . Evet, denormalizasyon daha hızlıdır, fakat aynı zamanda sistemde fazladan veriye sahip olduğunuz anlamına gelir - bu da senkronize edilmesi ve senkronize edilmesi gereken verilerdir. Bu veriler için artık "bir kaynak" yoktur, ancak sapma gösterebilecek birden fazla kaynak vardır. Bu zaman içinde risklidir, bu nedenle bazı kriterler ile desteklenen çok iyi nedenleriniz olmadığı sürece yapmamalısınız.

Ben sadece ...

  • veri miktarı çok büyük
  • katılımlar pahalıdır ve önemsiz sorgular bile alabilmeleri için çoğunu yapmanız gerekir.
  • veritabanı performansı bir tıkanıklık ve / veya olabildiğince hızlı gitmek istediğinizde

Birleştirmeler modern donanımda çok hızlıdır, ancak asla ücretsiz değildirler.


9

Sorguyu şu şekilde yeniden yazmak daha iyi olur:

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

Bu daha az özlü görünmekle birlikte, iyi bir sorgu planlayıcısı ne yapmaya çalıştığınızı görecek ve yukarıdaki alt üye olarak ilişkili alt sorgunuzu çalıştırmaya devam edecektir, bunun yerine, kötü bir sorgu planlayıcısı bir dizin taraması yaparak sona erebilir payments.id_customer(ilgili bir dizine sahip olduğunuzu varsayarak) ) (ya da daha kötüsü, tablo taraması) işleri daha verimli yapmak yerine. İyi bir sorgu planlayıcısı bile, bu sorgunun düzenlenmesi daha karmaşık bir şeye sarılırsa optimizasyonu göremeyebilir. İlişkiyi bir alt sorgu yerine bir birleştirme olarak ifade etmek, veri yapınızı değiştirmekten daha fazla fark yaratabilir.

Jeff'in dediği gibi, herhangi bir normalleştirme, dikkatle değerlendirilmelidir - özellikle bazı raporlama amaçları için kolay performans artışı sağlayabilir, ancak destekleyici iş mantığındaki hatalar nedeniyle tutarsızlığa neden olabilir.

Yan not olarak: Açıkçası işinizi bilmiyorum bu yüzden bir şeyleri kaçırabilirim, ama masa ilişkileriniz bana tuhaf geliyor. En azından uzun bir süre boyunca, benim deneyimimde genellikle doğru olmayan aynı müşteriyle asla birden fazla projeye sahip olamayacağınız anlamına gelir.

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

veya daha az normalleştirilmiş olmak gerekirse (bunun gerekli olacağından şüpheliyim):

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

Tabii ki hala iki müşteriyle ortak bir proje olasılığını düşürüyor ...


3
İlk performans kuralı: Üretimde asla * kullanmayın!
Brian Ballsun-Stanton,

@Brian: Çok geçerli nokta. Ayrıca, belirli cümlelerde * kaçınılması muhtemel performans etkilerinin yanı sıra, sys.depends yerine DROP VIEW+ CREATE VIEWkullanılmasından dolayı katliamdan çıkarsa, MSSQL'de görüntülenen görünümlerde sütun sıralamasıyla ilgili sorunlardan kaçınır ALTER VIEW.
David Spillett

@Brian ben * kolay yazma için koydu.
Gabriel Solomon

Proje, kendi alanında bağımsız bir uygulama olarak düşünülmüş ve farklı müşterilere ait olduğu için bir müşteri farklı projeler için aynı hesaba sahip olamaz
Gabriel Solomon

4

Bazı veritabanlarında, karmaşık bir sorguya dayalı olarak büyük miktarda veri içeren karmaşık VIEWS yerine "Materyalleştirilmiş Görünümler" oluşturma olanağınız vardır. Bu, geçmiş bir uygulama sisteminde denormalizasyondan kaçınmak için kullanılabilir. "Materyalleştirilmiş Görünümler", "Materyalleştirilmiş Görünüm tarafından kullanılacak olan yenileme yöntemleri ve depolama miktarı hakkında net bir fikre sahip olmalısınız ...

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.