Innanzitutto, la ragion d'essere (ragion d'essere) di un database relazionale è quella di poter modellare le relazioni tra entità. I join sono semplicemente i meccanismi attraverso i quali attraversiamo queste relazioni. Certamente hanno un costo nominale, ma senza join non c'è davvero alcun motivo per avere un database relazionale.
Nel mondo accademico apprendiamo cose come le varie forme normali (1a, 2a, 3a, Boyce-Codd, ecc.) E apprendiamo diversi tipi di chiavi (primaria, straniera, alternativa, unica, ecc.) E come queste cose si combinano insieme per progettare un database. E apprendiamo i rudimenti di SQL oltre a manipolare sia la struttura che i dati (DDL e DML).
Nel mondo aziendale, molti dei costrutti accademici si sono rivelati sostanzialmente meno praticabili di quanto ci fosse stato fatto credere. Un esempio perfetto è la nozione di chiave primaria. Dal punto di vista accademico è quell'attributo (o insieme di attributi) che identifica in modo univoco una riga nella tabella. Quindi, in molti domini problematici, la chiave primaria accademica corretta è un composto di 3 o 4 attributi. Tuttavia, quasi tutti nel mondo aziendale moderno utilizzano un numero intero sequenziale generato automaticamente come chiave primaria di una tabella. Perché? Due ragioni. Il primo è perché rende il modello molto più pulito durante la migrazione di FK dappertutto. Il secondo, e più pertinente a questa domanda, è che il recupero dei dati tramite i join è più veloce ed efficiente su un singolo intero rispetto a 4 colonne varchar (come già menzionato da alcune persone).
Scaviamo un po 'più a fondo ora in due sottotipi specifici di database del mondo reale. Il primo tipo è un database transazionale. Questa è la base per molte applicazioni di e-commerce o di gestione dei contenuti che guidano i siti moderni. Con un database delle transazioni, stai ottimizzando notevolmente il "throughput delle transazioni". La maggior parte delle app di commercio o di contenuto deve bilanciare le prestazioni delle query (da determinate tabelle) con le prestazioni di inserimento (in altre tabelle), sebbene ogni app avrà i propri problemi aziendali da risolvere.
Il secondo tipo di database del mondo reale è un database di report. Questi vengono utilizzati quasi esclusivamente per aggregare dati aziendali e per generare report aziendali significativi. Hanno in genere una forma diversa rispetto ai database delle transazioni in cui vengono generati i dati e sono altamente ottimizzati per la velocità di caricamento dei dati in blocco (ETL) e le prestazioni delle query con set di dati grandi o complessi.
In ogni caso, lo sviluppatore o l'amministratore di database deve bilanciare attentamente sia la funzionalità che le curve delle prestazioni e ci sono molti trucchi per migliorare le prestazioni su entrambi i lati dell'equazione. In Oracle puoi fare quello che viene chiamato un "piano di spiegazione" in modo da poter vedere nello specifico come una query viene analizzata ed eseguita. Stai cercando di massimizzare il corretto utilizzo degli indici da parte del DB. Un vero e proprio no-no è mettere una funzione nella clausola where di una query. Ogni volta che lo fai, garantisci che Oracle non utilizzerà alcun indice su quella particolare colonna e probabilmente vedrai una scansione completa o parziale della tabella nel piano di spiegazione. Questo è solo un esempio specifico di come potrebbe essere scritta una query che finisce per essere lenta e non ha nulla a che fare con i join.
E mentre parliamo di scansioni di tabelle, ovviamente influiscono sulla velocità delle query in modo proporzionale alle dimensioni della tabella. Una scansione completa della tabella di 100 righe non è nemmeno evidente. Esegui la stessa query su una tabella con 100 milioni di righe e dovrai tornare la prossima settimana per il ritorno.
Parliamo di normalizzazione per un minuto. Questo è un altro argomento accademico ampiamente positivo che può essere stressato. Il più delle volte quando parliamo di normalizzazione, intendiamo davvero l'eliminazione di dati duplicati inserendoli nella propria tabella e migrando un FK. La gente di solito salta l'intera faccenda della dipendenza descritta da 2NF e 3NF. Eppure, in un caso estremo, è certamente possibile avere un database BCNF perfetto che è enorme e una bestia completa per scrivere codice perché è così normalizzato.
Allora dove ci bilanciamo? Non esiste un'unica risposta migliore. Tutte le risposte migliori tendono ad essere un compromesso tra facilità di manutenzione della struttura, facilità di manutenzione dei dati e facilità di creazione / manutenzione del codice. In generale, minore è la duplicazione dei dati, meglio è.
Allora perché i join a volte sono lenti? A volte è un cattivo design relazionale. A volte è un'indicizzazione inefficace. A volte è un problema di volume di dati. A volte è una domanda scritta in modo orribile.
Ci scusiamo per una risposta così prolissa, ma mi sono sentito obbligato a fornire un contesto più carnoso intorno ai miei commenti piuttosto che a snocciolare una risposta di 4 proiettili.