Colonna duplicata per query più veloci?


30

Il titolo non ha molto senso, ma non riuscivo a pensare a un titolo migliore per questo problema.

Ho le seguenti tabelle

progetti

  • id
  • nome

Clienti

  • id
  • id_project
  • nome

pagamenti

  • id
  • id_customer
  • Data
  • somma

Quando un utente entra nel sistema, avrà accesso a un determinato progetto. Ora, voglio elencare tutti i pagamenti per quel progetto e dovrebbe essere abbastanza semplice:

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

La mia domanda è: se non è meglio aggiungere una colonna id_project alla tabella dei pagamenti in questo modo le query saranno più facili e veloci.


1
quindi la query non è un problema per i moderni RDBMS (o meglio, usa join).
Garik,

4
Accetto, ottieni un piano di query per subselect vs join e vedi quale è meglio
Gaius

1
Penso che questo post SO valga la pena di essere visto, dato che @igor ha menzionato l'uso di JOIN o IN
CoderHawk,

Risposte:


52

Sembra che tu stia chiedendo se la denormalizzazione ha senso.

La denormalizzazione è il processo di tentativo di ottimizzare le prestazioni di lettura di un database aggiungendo dati ridondanti o raggruppando i dati. In alcuni casi, la denormalizzazione aiuta a coprire le inefficienze inerenti al software di database relazionale. Un database normalizzato relazionale impone un carico di accesso pesante sull'archiviazione fisica dei dati anche se è ottimizzato per prestazioni elevate.

La risposta è sempre "dipende", quindi ecco la mia regola empirica:

Se ...

  • la quantità di dati non è grande
  • non stai già facendo un sacco di iscritti
  • e / o le prestazioni del database non sono attualmente un collo di bottiglia

quindi rimanere normalizzato . Sì, la denormalizzazione è più veloce, ma significa anche che nel sistema sono presenti dati ridondanti, dati che devono essere mantenuti e sincronizzati. Non esiste più "una fonte" per quei dati, ma più fonti che possono deviare. Questo è rischioso nel tempo, quindi non dovresti farlo a meno che tu non abbia ottime ragioni per farlo, supportato da alcuni benchmark.

Denormalizzerei solo quando ...

  • la quantità di dati è molto grande
  • i join sono costosi e devi farne molti per ottenere risposte anche banali
  • le prestazioni del database sono un collo di bottiglia e / o vuoi andare il più velocemente possibile

I join sono molto veloci sull'hardware moderno, ma non sono mai gratuiti.


9

Sarebbe meglio riscrivere la query come:

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

Mentre questo sembra meno conciso e un buon pianificatore di query vedrà cosa stai cercando di fare ed eseguirà la tua sottoquery correlata come il join precedente invece, un cattivo pianificatore di query potrebbe finire per fare una scansione dell'indice di payments.id_customer(supponendo che tu abbia un indice rilevante ) (o peggio, scansione della tabella) invece di fare le cose nel modo più efficiente. Anche un buon pianificatore di query potrebbe non vedere l'ottimizzazione se la disposizione di questa query è racchiusa in qualcosa di più complicato. Esprimere la relazione come join anziché come query secondaria può fare più differenza che modificare la struttura dei dati.

Come dice Jeff, qualsiasi denormalizzazione dovrebbe essere considerata con attenzione: può portare a un semplice aumento delle prestazioni, in particolare per alcuni scopi di reportistica, ma può portare a incongruenze dovute a bug nella logica aziendale di supporto.

Come nota a margine: ovviamente non conosco i tuoi affari, quindi potrei mancare qualcosa, ma le tue relazioni con i tavoli mi sembrano strane. Implica che non puoi mai avere più di un progetto con lo stesso cliente, cosa che di solito non è vera nella mia esperienza, almeno per un lungo periodo.

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

o se essere meno normalizzato (anche se dubito che sarebbe necessario):

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

Ovviamente, ciò sconta ancora la possibilità di un progetto congiunto con due clienti ...


3
Prima regola di prestazione: non usare mai * in produzione!
Brian Ballsun-Stanton,

@Brian: punto molto valido. Oltre alle potenziali implicazioni sulle prestazioni che evitano * nelle clausole selezionate, si evitano anche problemi con l'ordinamento delle colonne in viste-in-vista in MSSQL se sys.depends esce di kilter a causa dell'utilizzo di DROP VIEW+ CREATE VIEWinvece di ALTER VIEW.
David Spillett,

@Brian ho messo * per facilità di scrittura.
Gabriel Solomon,

Il progetto è considerato più come un'applicazione indipendente con il suo dominio e appartiene a clienti diversi, quindi un cliente non può avere lo stesso account su progetti diversi
Gabriel Solomon,

4

In alcuni database hai la possibilità di creare "Viste materializzate" invece di VISUALIZZAZIONI complesse con una grande quantità di dati, basate su una query complessa, che possono essere utilizzate per evitare la denormalizzazione in un sistema di applicazioni storico sviluppato. Se decidi di utilizzare " Viste materializzate "devi avere una chiara idea dei metodi di aggiornamento e della quantità di memoria che verrà utilizzata dalla vista materializzata ...

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.