Documentazione di PostgreSQL 9.0 > Il linguaggio SQL > Definizione dei dati > Ereditarietà
PrecedenteSchemiPartizionamentoSuccessivo

5.8. Ereditarietà

PostgreSQL™ implementa l'ereditarietà delle tabelle, che può essere uno strumento utile per progettare un databse. (SQL:1999 e successivi definiscono l'ereditarietà dei tipi, che è molto diversa dalle caratteristiche descritte qui.)

Partiamo con un esempio: si supponga di provare a costruire un modello di dati per le città. Ogni stato ha molte città, ma solo una capitale. Vogliamo essere capaci di ricavare velocemente la città capitale di un particolare stato. Questo può essere fatto creando due tabelle, una per le capitali statali e una per le città che non sono capitali. Comunque, cosa succede quando si vogliono informazioni su una città, senza badare se sia una capitale o meno? L'ereditarietà può aiutare a ricolvere questo problema. Definire la tabella capitals di modo che erediti da cities:

CREATE TABLE cities (
    name            text,
    population      float,
    altitude        int     -- in feet
);

CREATE TABLE capitals (
    state           char(2)
) INHERITS (cities);

In questo caso, la tabella capitals eredità tutte le colonne della sua tabella genitore, cities. Le capitali statali hanno una colonna in più, state, che mostra il loro Stato.

In PostgreSQL™, una tabella può ereditare da zero o più tabelle, e una query può riferirsi sia a tutte le righe di una tabella che a tutte le righe di una tabella più tutte quelle di una tabella discendente. Quest'ultimo casi è il default. Per esempio, la query seguente trova i nomi di tutte le città, include le capitali statali, che sono collocate ad un'altitudine maggiore di 500 piedi:

SELECT name, altitude
    FROM cities
    WHERE altitude > 500;

Prendendo i dai di esempio dal tutorial di PostgreSQL™ (see Sezione 2.1, «Introduzione»), la query restituisce:

   name    | altitude
-----------+----------
 Las Vegas |     2174
 Mariposa  |     1953
 Madison   |      845

Invece, la query seguente trova tutte le città che non sono capitali statali e sono situate ad un'altezza maggiore di 500 piedi:

SELECT name, altitude
    FROM ONLY cities
    WHERE altitude > 500;

   name    | altitude
-----------+----------
 Las Vegas |     2174
 Mariposa  |     1953

Qui la parola chiave ONLY indica che la query deve essere applicata solo sulla tabella cities, e non sulle tabelle sotto cities nella gerarchia di ereditarietà. Molti dei comandi che abbiamo già discusso -- SELECT, UPDATE e DELETE -- supportano la parola chiave ONLY.

In alcuni casi si potrebbe voler sapere da quale tabella è stata generata una particolare riga. In ogni tabella c'è una colonna di sistema chiamata tableoid che può dire la tabella originatrice:

SELECT c.tableoid, c.name, c.altitude
FROM cities c
WHERE c.altitude > 500;

che restituisce:

 tableoid |   name    | altitude
----------+-----------+----------
   139793 | Las Vegas |     2174
   139793 | Mariposa  |     1953
   139798 | Madison   |      845

(Se provi a riprodurre questo esempio, probabilmente otterrai differenti OID.) Eseguendo una join con pg_class si possono avere i nomi delle tabelle:

SELECT p.relname, c.name, c.altitude
FROM cities c, pg_class p
WHERE c.altitude > 500 AND c.tableoid = p.oid;

che restituisce:

 relname  |   name    | altitude
----------+-----------+----------
 cities   | Las Vegas |     2174
 cities   | Mariposa  |     1953
 capitals | Madison   |      845

L'ereditarietà non propaga automaticamente i dati dai comandi INSERT o COPY alle altre tabelle nella gerarchia di ereditarietà. Nel nostro esempio, il comando INSERT seguente fallirà:

INSERT INTO cities (name, population, altitude, state)
VALUES ('New York', NULL, NULL, 'NY');

Si potrebbe sperare che i dati fossero in qualche modo mandati alla tabella capitals, am questo non succede: l'INSERT inserisce sempre esattamente nella tabella specificata. In alcuni casi è possibile redirigere l'inserimento usando una regola (see Capitolo 37, The Rule System). Comunque questo non aiuta nel caso sopra, perchè la tabella cities non contiene la colonna state, e quindi il comando sarebbe respinto prima che la regola possa essere applicata.

Tutti i vincoli check e not-null su una tabella genitore sono automaticamente ereditati da i suoi figli. Altri tipi di vincoli (unique, chiave primaria e chiave esterna) non sono ereditati.

Una tabella può ereditare da più di una tabella genitore, nel qual caso avrà l'unione delle colonne definite dalle tabelle genitrici. Le colonne dichiarate nella definizione della tabella figlia sono aggiunte a quelle. Se lo stesso nome di colonna appare in più tabelle genitrici, o sia in una tabella genitore che in una figlia, allora queste colonne sono «fuse» così che ci sia una sola di queste colonne nella tabella figlia. Per essere fuse, le colonne devono avere lo stesso tipi di dato, altrimenti viene generato un errore. La colonna fusa avrà copie di tutti i vincoli check provenienti da qualsiasi definizione di colonna originaria, e sarà segnata come not-null se lo è qualsiasi colonna originiaria.

L'ereditarietà delle tabelle è tipicamente stabilita quando la tabella figlia viene creata, usando la clausola INHERITS del comando CREATE TABLE(7) Alternativamente, ad una tabella che è già definita in modo compatibile, è possibile aggiungere una genitore, usando la variante INHERIT di ALTER TABLE(7). Per fare questo la nuova tabella figlia deve già includere colonne con lo stesso nome e tipo delle colonne del genitore. Deve anche includere vincoli check con gli stessi nomi e espressioni check del genitore. Similarmente, un collegamento di ereditarietò può essere rimosso da un figlio usando la variante NO INHERIT di ALTER TABLE. Aggiungere e rimuovere dinamicamente collegamenti di ereditarietà in questo modo può essere utile quando la relazione di ereditarietà viene usata per il partizionameno (vedere Sezione 5.9, «Partizionamento»).

Un modo comodo per creare una tabella compatibile che successivamente diventerà una nuova figlia è di usare la clausola LIKE nel CREATE TABLE. Questo crea una nuova tabella con le stesse colonne della tabella sorgente. Se ci sono alcuni vincoli CHECK definiti sulla tabella sorgente, l'opzione INCLUDING CONSTRAINTS della LIKE dovrebbe essere specificata, dato che la nuova figlia deve avere vincoli corrispondenti alla genitrice per essere considerata compatibile.

Una tabella genitore non può essere eliminata finchè una delle sue figlie esiste. Nemmeno le colonne o i vincoli check delle tabelle figlie possono essere eliminate o modificate se sono ereditate da tabelle genitrici. Se si desidera rimuovere una tabella e tutte le sue discendenti, un modo semplice è eliminare la tabella genitrice con l'opzione CASCADE.

ALTER TABLE(7) propagherà i cambiamenti nelle definizioni dei dati di colonna e nei vincoli check lungo tutta la gerarchia di ereditarietà. Di nuovo, la cancellazione delle colonne che dipendono da altre è possibile solo usando l'opzione CASCADE. ALTER TABLE segue le stesse regole per la fusione di colonne duplicate e per il rifuto applicate nella CREATE TABLE.

Notare come sono gestiti i permessi di accesso alla tabella. Eseguire una query su una tabella genitrice può automaticamente permettere l'accesso ai dati in tabelle figlie senza ulteriori controlli sui privilegi di accesso. Questo comportamento mantiene l'apparenza che i dati siano (anche) nella tabella genitrice. Comunque, accedere alle tabelle figlie direttamente non è permesso automaticamente e richiederebbe la concessione di ulteriori privilegi .

5.8.1. Avvertimenti

Notare che non tutti i comandi SQL riescono a lavorare con le gerarchie di ereditarietà. I comandi usati per le query, la modifica dei dati, o la modifica degli schemi (ad es., SELECT, UPDATE, DELETE, molte varianti di ALTER TABLE, ma non INSERT e ALTER TABLE ... RENAME) tipicamente di default includono le tabelle figlie e supportano la notazione ONLY per escluderle. I comandi per fare la manutenzione del database e la messa a punto (ad es., REINDEX, VACUUM) tipicamente funzionano solo su tabelle fisiche e individuali e non supportano la recursione attraverso gerarchie di eredità. Il rispettivo comportamento di ogni singolo comando è documentato nel riferimento (SQL Commands).

Una seria limitazione dell'ereditarietà è che gli indici (inclusi i vincoli unique) e i vincoli chiave esterna si applicano solo su singole tabelle, non le loro figlie. Questo è vero sia per il lato referente che referenziato, di un vincolo chiave esterna. Così, riprendendo l'esempio sopra:

  • Se cities.name viene dichiarato UNIQUE o PRIMARY KEY, questo non fermerebbe la tabella capitals dall'avere righe con nomi che duplicano le righe nella tabella cities. E queste righe duplicate saranno mostrate di default nelle query riguardanti la tabella cities. Infatti, di default capitals non avrebbe nessun vincolo unique, e così potrebbe contenere diverse righe con lo stesso nome. È possibile aggiungere un vincolo unique a capitals, ma questo non preverrebbe duplicazioni rispetto a cities.

  • Similarmente, se abbiamo specificato che cities.name referenzia (REFERENCES) un altra tabella, questo vincolo non si propagherà automaticamente in capitals. In questo caso potresti ovviare alla cosa aggiungendo manualmente lo stesso vincolo REFERENCES a capitals.

  • Specificare che una colonna REFERENCES cities(name) permetterebbe all'altra tabella di contenere nomi di città, ma non nomi di capitale. Non ci sono modi di ovviare a questa cosa.

Queste carenze saranno probabilmente corrette in versioni future, ma nel frattempo è necessaria particolare attenzione nel decidere se l'ereditarietà è utile per la propria applicazione.

[Nota]

Deprecato

Nelle versioni di PostgreSQL™ precedenti alla 7.1, il comportamento di default non era di includere le tabelle figlie nelle query. Questo fu trovato incline ad errori e in violazione dello standard SQL. Puoi avere il comportamento delle versioni precedenti la 7.1 agendo sull'opzione di configurazione sql_inheritance.

Documentazione di PostgreSQL 9.0 > Il linguaggio SQL > Definizione dei dati > Ereditarietà
PrecedenteSchemiPartizionamentoSuccessivo