Perché le corrispondenze chiave primaria / chiave esterna non vengono utilizzate per i join?


48

Per quanto ho potuto scoprire molti DBMS (ad esempio mysql, postgres, mssql) usano le combinazioni fk e pk solo per vincolare le modifiche ai dati, ma raramente vengono utilizzate in modo nativo per selezionare automaticamente le colonne da unire (come fa l'unione naturale con i nomi). Perché? Se hai già definito una relazione tra 2 tabelle con un pk / fk, perché il database non riesce a capire che se mi unisco a quelle tabelle voglio unirle nelle colonne pk / fk?

EDIT: per chiarire un po 'questo:

supponiamo che io abbia un table1 e un table2. la tabella 1 ha una chiave esterna sulla colonna a, che fa riferimento alla chiave primaria sulla tabella2, la colonna b. Ora, se mi unisco a questi tavoli, dovrò fare qualcosa del genere:

SELECT * FROM table1
JOIN table2 ON table1.a = table2.b

Tuttavia, ho già definito usando le mie chiavi che table1.a fa riferimento a table2.b, quindi mi sembra che non dovrebbe essere difficile far sì che un sistema DBMS usi automaticamente table1.a e table2.b come colonne di join, tale che si può semplicemente usare:

SELECT * FROM table1
AUTO JOIN table2

Tuttavia, molti DBMS non sembrano implementare qualcosa del genere.

Risposte:


32

In molti casi, ci sono più di un modo per unire due tabelle; Vedi le altre risposte per molti esempi. Naturalmente, si potrebbe dire che sarebbe un errore utilizzare il "join automatico" in quei casi. Quindi rimarrebbe solo una manciata di semplici casi in cui può essere utilizzato.

Tuttavia, c'è un grave inconveniente! Query che sono corrette oggi, potrebbero diventare un errore domani semplicemente aggiungendo un secondo FK alla stessa tabella!

Consentitemi di ripeterlo: aggiungendo colonne, le query che non utilizzano quelle colonne potrebbero passare da "corretto" a "errore"!

Questo è un incubo di manutenzione tale che qualsiasi guida di stile sano vieta di utilizzare questa funzione. La maggior parte già proibisce select *per lo stesso motivo!

Tutto ciò sarebbe accettabile, se le prestazioni fossero migliorate. Tuttavia, non è così.

Riassumendo, questa funzione potrebbe essere utilizzata solo in un numero limitato di casi semplici, non aumenta le prestazioni e la maggior parte delle guide di stile ne vieterebbe comunque l'utilizzo.

Pertanto non è scontato che la maggior parte dei venditori di database scelga di dedicare il proprio tempo a cose più importanti.


1
Probabilmente ci sarebbe un piccolo successo in termini di prestazioni in quanto deve capire le colonne di join piuttosto che sprecarle.
HLGEM,

1
@HLGEM, potrebbe essere memorizzato nella cache e potrebbe anche essere irrilevante per le query più grandi. Il vantaggio è che possiamo essere certi che le chiavi non vengano perse a causa di un errore umano.
Pacerier,

Anche l'aggiunta e l'alterazione di colonne potrebbe interrompersi NATURAL JOIN(motivo per cui di solito le evito), ma non credo che di per sé significhi che un dbms non potrebbe implementare un modo automatico di unire tabelle basate su chiavi esterne.
Jay K,

2
Molti casi? Su un DB di mille tabelle, ho solo alcuni casi di relazione più di 1 tra due tabelle. Comunque, non è un problema, sarebbe sufficiente aggiungere un nome di relazione come AUTO JOIN mytable THROUGH myrelation, sarebbe molto bello.
Teejay,

Questo è quello che facciamo nel nostro builder .NET SQL su misura, con intellisense, tipoInnerJoin(SRC_TABLE.rDEST_TABLE.REL_NAME_F01)
Teejay

27

Una chiave esterna ha lo scopo di limitare i dati. vale a dire applicare l'integrità referenziale. Questo è tutto. Nient'altro.

  1. Puoi avere più chiavi esterne nella stessa tabella. Considerare quanto segue in cui una spedizione ha un punto iniziale e un punto finale.

    table: USA_States
    StateID
    StateName
    
    table: Shipment
    ShipmentID
    PickupStateID Foreign key
    DeliveryStateID Foreign key

    Potresti voler unirti in base allo stato del ritiro. Forse vuoi unirti allo stato di consegna. Forse vuoi eseguire 2 join per entrambi! Il motore sql non ha modo di sapere cosa vuoi.

  2. Spesso incrociamo valori scalari di join. Sebbene gli scalari siano generalmente il risultato di calcoli intermedi, a volte avrai una tabella di scopi speciali con esattamente 1 record. Se il motore provasse a rilevare una chiave foriegn per il join ... non avrebbe senso perché i cross join non corrispondono mai a una colonna.

  3. In alcuni casi speciali ti unirai a colonne dove nessuno dei due è unico. Pertanto la presenza di un PK / FK su quelle colonne è impossibile.

  4. Si può pensare ai punti 2 e 3 di cui sopra non sono rilevanti in quanto le vostre domande è di circa quando vi È un singolo PK / FK relazione tra le tabelle. Tuttavia la presenza di un singolo PK / FK tra le tabelle non significa che non si possano avere altri campi su cui unirsi oltre al PK / FK. Il motore sql non saprebbe a quali campi vuoi unirti.

  5. Supponiamo che tu abbia una tabella "USA_States" e altre 5 tabelle con un FK negli stati. Le "cinque" tabelle hanno anche alcune chiavi esterne tra loro. Il motore sql dovrebbe unirsi automaticamente alle "cinque" tabelle con "USA_States"? O dovrebbe unire i "cinque" tra loro? Entrambi? È possibile impostare le relazioni in modo che il motore sql entri in un ciclo infinito, cercando di unire le cose. In questa situazione è impossibile per il motore sql indovinare ciò che si desidera.

In sintesi: PK / FK non ha nulla a che fare con i join di tabella. Sono cose separate non correlate. È solo un incidente naturale che spesso ti unisci alle colonne PK / FK.

Vorresti che il motore sql indovinasse se è un join completo, sinistro, destro o interno? Io non la penso così. Anche se questo sarebbe probabilmente un peccato minore che indovinare le colonne su cui unirsi.


7
Ritengo che le chiavi esterne e la normalizzazione siano molto rilevanti per i join di tabella.

3
I tuoi argomenti valgono quando la normale parola chiave JOIN cerca sempre di corrispondere a quello (come ho fatto nel mio esempio, lo riparerò). Tuttavia, molti join possono essere derivati ​​direttamente solo dai join, quindi non vedo alcun motivo per cui non ci possa essere alcuna sintassi esplicita per unirli. Molti DBMS hanno un join naturale, che sostanzialmente fa la stessa cosa ma con nomi di colonna (= non valido). La stessa cosa si potrebbe fare con questo tipo di join, ad esempio specificando un'operazione AUTO JOIN.

5
"È solo un incidente della natura che spesso ti unisci alle colonne PK / FK" - Non ne sono convinto!
onedayquando il

2
"Normalizzazione?" Penso che il pensiero qui sia che se avessi iniziato con una relvar 1NF poi scomposta in relvars 6NF, allora le probabilità sono a) che avrebbero chiavi esterne sull'implementazione eb) sarebbero frequentemente unite in query.
onedayquando il

4
Vorrei votare se non ci fosse che "PK / FK non ha nulla a che fare con i join di tabella".
ypercubeᵀᴹ

11

il concetto di "joinability". Le relazioni r1e r2sono unibili se e solo se gli attributi con lo stesso nome sono dello stesso tipo ... questo concetto si applica non solo all'unione in quanto tale, ma anche a varie altre operazioni [come l'unione].

SQL e teoria relazionale: come scrivere codice SQL accurato per CJ Date

SQL standard ha già una tale funzionalità, nota come NATURAL JOIN, ed è stata implementata in mySQL.

Sebbene il tuo suggerimento non sia altrettanto degno, sembra ragionevole. Con SQL Server (che non supportaNATURAL JOIN ), utilizzo SQL Prompt in Management Studio: quando scrivo un INNER JOINInteliSense suggerisce ONclausole basate su nomi di attributi comuni e chiavi esterne e lo trovo molto utile. Tuttavia, non ho molta voglia di vedere un nuovo tipo di join SQL (standard) per questo.


1
Unire e unire naturali su colonne comuni è distinto e ortogonale alla nozione di unire su FK-PK. (Vedi la mia risposta.)
philipxy

@philipxy: d'accordo, non intendevo implicare diversamente. (La tua è una risposta eccellente!)
onedayquando il

9

SQL è arrivato per primo!

I vincoli di chiavi esterne e di chiavi esterne sono arrivati ​​in seguito e sono essenzialmente un'ottimizzazione per le applicazioni in stile "transazione".

I database relazionali erano originariamente concepiti come un metodo per applicare query complesse su insiemi di dati in un modo che era matematicamente dimostrabile usando l'algebra relazionale. IE per un determinato set di dati e una determinata query c'è sempre un'unica risposta corretta.

I database relazionali hanno fatto molta strada da allora, e l'uso primario come strato di persistenza per i sistemi transazionali non era ciò che CODD et. tutto previsto.

Tuttavia, l'organismo degli standard ANSI per tutti i suoi obiettivi contrastanti e le politiche dei fornitori ha sempre cercato di preservare le proprietà "matematicamente dimostrabili" di SQL.

Se si consentisse al database di dedurre le proprietà del join da dati di chiave esterna "nascosti", si perderebbe questa proprietà (considerare l'ambiguità se fosse stata definita più di una serie di chiavi esterne).

Inoltre, un programmatore che legge l'SQL non necessariamente saprebbe quali chiavi esterne sono state attualmente definite per le due tabelle e dovrebbe esaminare lo schema del database per capire cosa stava facendo la query.


3
Grazie, questo aveva senso per me! Tuttavia, i join naturali non hanno gli stessi problemi? Sebbene i join naturali presentino anche problemi maggiori, molti DBMS li supportano. IMO un join basato su pk / fk sarebbe un join naturale fatto bene.

1
Non vi è alcuna differenza per quanto riguarda la maggior parte dei motori di database tra un join naturale e un esplicito "JOIN ... ON". Il motore analizza la query e esegue il join nel modo migliore in base ai vari predicati. L'uso di un join esplicito non impone l'utilizzo di un determinato indice o percorso di accesso, è lì principalmente per supportare la sintassi del join "LEFT, OUTER, INNER" che deve conoscere i predicati del join esplicito per sapere quando inserire una riga "mancante" .

6
SQL non è arrivato per primo! Il modello relazionale (che includeva ovviamente il concetto di chiavi esterne) fu delineato per la prima volta da EFCodd nel 1969. SEQUEL, com'era allora, non vide la luce del giorno fino al 1974 circa. I suoi inventori chiarirono fin dall'inizio che SEQUEL / SQL doveva essere basato sul modello relazionale preesistente, sebbene SQL non riuscisse a essere un linguaggio veramente relazionale.
nvogel

@sqlvogel - true! Avrebbe dovuto essere definito "SQL è stato implementato per primo".
James Anderson,

CJ Date in "An Introduction to Database Systems" (p276) afferma che Codd ha inventato il concetto di chiave esterna; non dice quando ma suppongo che fosse prima della prima implementazione di SQL.
Onedayquando il

7

Sebbene sia stata definita una relazione Chiave esterna, ciò non significa che si desideri unire le tabelle in tutte le query. È il metodo più probabile per unire le tabelle, ma ci sono casi in cui non è corretto.

  • Potresti voler usare un prodotto cartesiano delle due tabelle o parte di esso per qualche scopo.
  • Potrebbero esserci altri campi su cui puoi unirti per un altro scopo.
  • Se si uniscono tre o più tabelle, una delle tabelle potrebbe essere correlata a due o più tabelle. In questo caso, in genere solo una delle possibili relazioni FK può essere appropriata nella query.

7

Potresti operare su un falso presupposto. Dici "per quanto puoi scoprire" ma non dai alcuna prova empirica o probatoria. Se pk o fk sono l'indice migliore per una query, verrà utilizzato. Non so perché lo stai vedendo, ma la mia ipotesi è che le domande siano scarsamente formate.


Modifica ora che la domanda è stata completamente riscritta: il caso che stai descrivendo sarebbe solo per una serie molto piccola di query. Cosa succede se ci sono 12 tavoli uniti? Che cosa succede se non ci sono FK ... Anche se ci fosse un join predefinito su, specificarei sempre il join solo per leggibilità. (Non voglio guardare i dati e poi provare a capire su cosa si sta unendo)

Alcuni strumenti Query eseguono effettivamente un join automatico per te, quindi ti consentono di rimuovere o modificare il join. Penso che il generatore di query di MS Access faccia questo.

Infine lo standard ANSII afferma che il join deve essere specificato. Questa è una ragione sufficiente per non permetterlo.


3
Scusa, forse non ero abbastanza chiaro. Non sto parlando di indici, sto parlando di join. Supponiamo di avere table1 e table2, con un fk su table1.a che punta a table2.b. Se mi unisco a queste tabelle dovrò dire esplicitamente che voglio unirle su colonne aeb (es. 'SELECT * FROM table1 JOIN table2 ON table1.a = table2.b '), mentre ho già definito nel mio database schema che quei due sono correlati. La domanda è perché non posso fare 'SELEZIONA * DA tabella1 UNISCITI tabella2' e lasciare che il DBMS scelga automaticamente le colonne di join in base a fk / pk.

3
Soprattutto la leggibilità aveva senso per me! Tuttavia, il fatto che lo standard lo dica non è un ottimo argomento IMO. Molti standard hanno già fatto scelte sbagliate (ad esempio HTML).

3

Ci sono molti motivi per cui il database non può farlo in modo sicuro, incluso il fatto che l'aggiunta / rimozione di chiavi esterne cambierà il significato delle query pre-scritte, comprese le query nel codice sorgente dell'applicazione. La maggior parte dei database inoltre non ha un buon set di chiavi esterne che coprono tutti i possibili join che probabilmente vorrai fare. Inoltre, per il meglio o per il valore, le chiavi esterne vengono spesso rimosse per velocizzare i sistemi e non possono essere utilizzate su tabelle caricate nell'ordine "sbagliato" dal file.

Tuttavia, non vi è alcun motivo per cui uno strumento di progettazione query o l'editor di testo non possano completare automaticamente un join con l'aiuto di chiavi esterne allo stesso modo in cui forniscono intellisense sul nome della colonna. È possibile modificare la query se lo strumento ha sbagliato e salvare una query completamente definita. Tale strumento potrebbe anche utilmente utilizzare la convenzione di denominazione delle colonne di chiavi esterne dal nome della tabella "padre" e colonne con lo stesso nome sia nella tabella padre / figlio, ecc.

(Mia moglie non riesce ancora a capire la differenza tra Management Studio e Sql Server e parla dell'avvio di SQL Server quando avvia Management Studio!)


3

L'unione naturale "automaticamente" si unisce all'uguaglianza delle colonne comuni, ma dovresti scriverlo solo se è quello che vuoi in base ai significati della tabella e al risultato desiderato. Non c'è "automaticamente" sapere come due tabelle "debbano" essere unite o in qualsiasi altro modo una tabella "dovrebbe" apparire in una query. Non è necessario conoscere i vincoli per le query. La loro presenza significa solo che gli ingressi possono essere limitati e, di conseguenza, anche l'uscita può essere. È possibile definire un tipo di operatore join_on_fk_to_pk che "automaticamente" unisce per vincoli dichiarati; ma se vuoi che il significato della query rimanga lo stesso se cambiano solo i vincoli ma non i significati della tabella, dovresti cambiare quella query per non usare i nuovi constain dichiarati.lascia già il significato lo stesso nonostante qualsiasi modifica del vincolo .

I vincoli (inclusi PK, FK, UNIQUE e CHECK) non influiscono sul significato delle tabelle. Naturalmente, se i significati della tabella cambiano, allora potrebbero cambiare le controindicazioni. Ma se i vincoli cambiano, ciò non significa che le query dovrebbero cambiare.

Non è necessario conoscere i vincoli da interrogare. Conoscere i vincoli significa che possiamo usare ulteriori espressioni che senza il mantenimento dei vincoli non restituirebbero la stessa risposta. Ad esempio, aspettando tramite UNIQUE che una tabella abbia una riga, quindi possiamo usarla come scalare. Queste query possono interrompersi se il vincolo è stato assunto ma non dichiarato. Ma dichiarare un vincolo che la query non ha assunto non può romperlo.

Esiste una regola empirica per costruire query SQL da una descrizione leggibile dall'uomo?


2

Il motivo è che c'è la LINGUA, e poi ci sono i principi sottostanti. Il linguaggio è scarso e privo di molte funzionalità che ti aspetteresti di vedere in un linguaggio generico. Questa è semplicemente una bella funzionalità che non è stata aggiunta alla lingua e probabilmente non lo sarà. Non è una lingua morta, quindi c'è qualche speranza, ma non sarei ottimista.

Come altri hanno sottolineato, alcune implementazioni utilizzano un'estensione in cui join (colonna) unisce due tabelle basate su un nome di colonna comune, che è in qualche modo simile. Ma non è ampiamente diffuso. Si noti che questa estensione è diversa dalla SELECT * FROM employee NATURAL JOIN department;sintassi che non include un modo per specificare quali colonne utilizzare. Né fare affidamento su una relazione tra le tabelle, che le rende inaffidabili (la sintassi del join naturale più dell'estensione).

Non esiste alcun ostacolo fondamentale alla "tabella di join interna su PKFK" in cui PKFK è una parola chiave che significa "la relazione di chiave esterna definita tra le due tabelle", potrebbero esserci problemi con più fk sulla stessa tabella, ma ciò potrebbe semplicemente causare un errore. La domanda è: le persone che progettano la lingua ritengono che a) una buona idea eb) su cui lavorare meglio rispetto ad altri cambi di lingua ...


3
Ciò presume che sia una buona idea che avrebbero già dovuto farlo. È anche probabile che lo abbiano già preso in considerazione e abbiano deciso di non farlo. Forse è una pessima idea in pratica: Sjoerd ha menzionato un esempio, in cui una query potrebbe interrompersi semplicemente aggiungendo una nuova colonna e una relazione FK. Lord Tydus spiega anche che le chiavi esterne hanno una responsabilità diversa dal dettare le modalità di unione dei tavoli.

1
@JonathanHobbs: intendevo dire che la mia risposta era generalmente neutrale, ma abbandonando la neutralità, la logica dijoerd è difettosa, i cambiamenti alle tabelle già interrompono le query, l'aggiunta di una nuova colonna a una chiave primaria della tabella interromperà le query o inizierà a restituire risultati errati. Questo in effetti ti isolerebbe da ciò in una misura in cui, finché la relazione tra tabelle fosse mantenuta, le modifiche alle colonne potrebbero essere fatte in modo sicuro. Ciò aumenterebbe probabilmente l'utilizzo delle relazioni FK, in quanto sarebbe utile per qualcosa di diverso da RI. sono sul PK o includono il Pk. Per gestire il multi fk, usa il nome della colonna.
jmoreno,

1

Se si presume che l'omissione della clausola ON segua i campi in base all'integrità referenziale, come si farebbe un prodotto cartesiano?

Modifica: usando AUTO I vantaggi di questo sono un po 'meno la digitazione e non devi sapere come sono uniti o ricordare un join complicato. Se la relazione cambia, viene gestita automaticamente, ma ciò accade raramente se non nello sviluppo iniziale.

Quello che devi fare ora è decidere se tutti i tuoi join AUTO vengono trattenuti durante una modifica della relazione per corrispondere all'intento dell'istruzione select.


@JeffO: il vantaggio principale è che esprime l'intento in modo più preciso, in un modo dichiarativo molto chiaro. I join sui nomi delle colonne non dicono nulla, a parte il fatto che alcuni dei contenuti delle colonne sono simili a quelli di un altro (ma potrebbero non essere dello stesso tipo). Un join su un ref fk, ti dice che non v'è un ref fk, nessun elenco di colonne vorrebbe dire c'era solo 1 fk tra i tavoli, o al contrario che non v'è 1+ (considerare una chiave più colonne con più di 1 Rif cosa accade quando mescoli le colonne c1 = fk1_c1 e c2 = fk2_c2). Anche con una media di più digitando, questo sarebbe buono.
jmoreno,

L'uso di (INNER) JOIN senza ON non è SQL standard. Virgola, CROSS JOIN & (INNER o qualsiasi ESTERNO) JOIN ON 0 = 0 restituisce prodotto cartesiano.
Philipxy,

-1

perché il database non riesce a capire che se mi unisco a quelle tabelle voglio unirle nelle colonne pk / fk?

Parti del motivo sono:

1 - in teoria è possibile unire tabelle su colonne arbitrarie dalle due tabelle. Sebbene questa non sia una pratica comune, è valida. Ricorda che SQL è come un linguaggio di programmazione, non capisce quali informazioni siano all'interno delle colonne del corso e i nomi, per SQL, non significano molto al riguardo.

2 - Esistono diversi tipi di join (sinistro, destro, interno) - Inner Joins è solo uno di essi.

3 - Lo standard SQL può essere guidato dal principio di essere un linguaggio di livello inferiore che consente ai dialetti di livello superiore di formare l'intelligenza che lo utilizza. Il confronto è in qualche modo più chiaro se si pensa a una lingua di quarta generazione rispetto a una lingua di terza generazione. In effetti, uno strumento che ho usato, IEF, ti ha permesso di scrivere qualcosa del genere:

ReadEach Customer 
Where Customer Places Orders and That Customer LivesIn "California" 
and OrderValue > 100.00

In sintesi, il tuo suggerimento è interessante e potrebbe essere implementato come parte dello standard o come una procedura memorizzata (per impostazione predefinita sarebbe un Join interno).


-10

Tiddo, credo che tu abbia perfettamente ragione, SQL su quell'argomento è piuttosto stupido , e ricordo di aver pensato la stessa cosa che hai fatto con le chiavi esterne durante l'apprendimento di SQL circa dieci anni fa.

Ok, dato che, alla fine ho dovuto passare quell'esame; e per passarlo, ho dovuto lasciar andare . SQL è più un disastro ferroviario di quanto chiunque possa ammettere, il suo percorso di standardizzazione è un disastro completo e alcune implementazioni sono minacciosamente complete . Comunque è abbastanza utile, in generale. (Non sono un luddite K / V)

Chiavi esterne, quindi ... non è affatto utile. Sono un concetto importante nel modello relazionale , ok, ma la funzione SQL con lo stesso nome non si confronta bene.

Dirvi dritto: non utilizzare tale caratteristica SQL chiamata Foreign Keya tutti , fino a colpire qualche grande sistema con problemi di prestazioni. Indicare esplicitamente al motore quale campo è una chiave esterna e quale non viene utilizzato solo per l'indicizzazione ed è invisibile all'utente del database.

È fuorviante?
Sì.

Lo renderanno più potente ora, dopo 30 anni di persone fuorvianti?
Non una possibilità.

Ignorando completamente le chiavi esterne fino a quando necessario ... risolto SQL per me?
Sì!

E perché diavolo è successo tutto questo in primo luogo?
Bene, la funzione che chiamiamo chiavi esterne è stata aggiunta in seguito a SQL; SQL è uno standard che si è evoluto nel tempo, dal basso verso l'alto. I venditori hanno implementato caratteristiche ridicole, mentre i corpi standard sono stati affrontati.

Chiavi esterne come detto, dove significava solo indicizzare e non era disponibile alcun costrutto JOIN. (si unisce alle SELECTquery, le JOINquery sono piuttosto recenti e hanno solo lo scopo di alias SELECTfunzionalità) Probabilmente pensavano che quella chiamata a quel flag di indicizzazione FOREIGN KEYfosse un trucco di denominazione intelligente rispetto ai concetti di teoria db relazionale.


13
Per quanto riguarda le chiavi esterne, suppongo che tu abbia mai toccato il motore MyISAM su MySQL? Perché anche ignorando quel piccolo sfogo, ogni singola cosa in questa risposta è sbagliata.

Gli Fk non vengono utilizzati per l'indicizzazione, in effetti un problema comune è non avere un indice sulla colonna FK che può avere un impatto drammatico sulle prestazioni.
jmoreno,
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.