Uno schema di relazione si può decomporre in più schemi, ognuno un sottoinsieme degli attributi di su cui valgono le dipendenze funzionali ereditate da , rilevanti per i suoi attributi. Ciò equivale a proiettare ogni tupla dell’istanza originaria sugli attributi dei singoli sottoschemi.

Definizione formale:

Una decomposizione di è una famiglia di sottoinsiemi di che partiziona (, i sottoinsiemi possono avere intersezione non vuota).

Gli schemi si decompongono

  • per motivi di performance (le tuple di taglia più piccola aumentano la velocità e la capacità massima delle operazioni di lettura)
  • o per raggruppare attributi semanticamente diversi, usati da operazioni diverse
  • o per ottenere più schemi in 3NF (terza forma normale) quando lo schema originale non è in 3NF.

N.B.: È sempre possibile trovare una decomposizione in 3NF valida, ma non vale lo stesso per la forma normale di Boyce-Codd.

Anche se uno schema è stato decomposto in più schemi in 3NF, non è detto che la decomposizione è soddisfacente, infatti ad esempio , decomposto e poi ricomposto tramite il join naturale tra sue decomposizioni, non conserva .

R1 = A  B     R2 = A  C     R = A  B  C
     a1 b1         a1 c1        a1 b1 c1
     a2 b1         a2 c2        a2 b1 c2

Criteri per distinguere una buona decomposizione da una svantaggiosa

  1. Ogni sottoschema deve essere in 3NF.
  2. La decomposizione deve preservare tutte le dipendenze in , anche quelle parziali e transitive.
  3. la decomposizione deve permettere di ricostruire ogni istanza legale senza perdita di informazione (ricostruzione mediante join senza perdita), che contro-intuitivamente può anche significare un’aggiunta di tuple sbagliate (“tuple Frankenstein” ottenute da pezzi di tuple giuste).

Criterio 1: determinare se ogni sottoschema è in 3NF

Basta calcolare le chiavi di ogni sottoschema per controllare che ognuno sia in 3NF.

Criterio 2: applicare l’algoritmo per determinare se la decomposizione preserva tutte le dipendenze

Sia una decomposizione dello schema , sul quale valgono le dipendenze funzionali . Diciamo che preserva se

Dove , cioè l’insieme delle dipendenze funzionali, anche quelle calcolate con gli assiomi di Armstrong, che valgono nello schema (che è il risultato della proiezione di su ).

Chiamiamo il risultato di . Per verificare , dobbiamo dimostrare che , quindi .

Per come abbiamo definito , sicuramente varrà sempre , quindi per il lemma delle chiusure (vedi sotto) varrà .

Manca solo di verificare se nel caso specifico considerato vale , quindi se (lemma delle chiusure): l’algoritmo controlla solo questo.

Lemma delle chiusure:

Questo lemma ci permette di verificare l’equivalenza tra e in tempo polinomiale, senza dover calcolare chiusure.

Dimostrazione

Ipotesi:

Tesi:

Sia ().

  1. Ogni dipendenza funzionale in può essere derivata da con gli assiomi di Armstrong da per ipotesi e perché .
  2. è derivabile con gli assiomi di Armstrong da perché .
  3. se è derivabile da e ogni dipendenza in può essere derivata da , allora è derivabile da con gli assiomi di Armstrong, quindi .

Algoritmo

Input: Gli insiemi di dipendenze funzionali e su .

Output: La variabile booleana successo, vera se .

  1. successo := true
  2. for in :
    1. calcola
    2. if : successo := false
  3. return successo

Cioè basta verificare che una sola dipendenza non appartiene a per dire che .

Come calcolare nello step 2.1 dell’algoritmo sopra?

Se volessimo usare l’algoritmo per il calcolo della chiusura di un insieme di attributi, dovremmo prima calcolare , che per definizione richiede il calcolo di , ma ciò richiede tempo esponenziale; però esiste un altro algoritmo per il calcolo di da .

Input: uno schema , un insieme di dipendenze su , una decomposizione , un sottoinsieme .

Output: salvata nella variabile .

  1. for to :
    1. calcolare questa chiusura non richiede tempo esponenziale, perché si calcola direttamente sull’insieme delle dipendenze funzionali di , che è molto più piccolo di completo.
    2. while :
      1. for to :
  2. return

Dimostrazione della validità dell’algoritmo

todo pag. 22 pdf 13

Criterio 3: applicare l’algoritmo per determinare se ogni istanza legale è ricostruibile tramite join naturale

Operatore

Una decomposizione di ha un join senza perdita se .

Per aiutarci nella dimostrazione, definiamo l’operatore unario “m rho” che agisce su un’istanza di .

Osserviamo che gode delle seguenti proprietà:

Dimostrazione di 1: Sia una tupla di . , che è un sottoinsieme di , quindi .

Dimostrazione di 2:

Dimostrazione di 3: , ma per la proprietà 2 sappiamo che , quindi per definizione.

Algoritmo per verificare se esiste un join senza perdita (tempo polinomiale)

Input: uno schema , un insieme di dipendenze su , una decomposizione di .

Output: Una booleana che indica se esiste o meno un join senza perdita.

  1. Costruisci una tabella nel modo seguente: ha colonne e righe. all’incrocio dell’i-esima riga e della j-esima colonna metti il simbolo se l’attributo , altrimenti metti il simbolo .
  2. (fase iterativa di modifica dell’istanza) for every :
    1. if ci sono due tuple e in tali che e :
      1. for every in :
        1. if then else
        2. if ha una riga con tutte “a” o non è cambiato:
          1. return true
  3. return false

N.B.: e sono solo etichette, valori particolari contenuti nel dominio dell’attributo e possiamo considerare tutti i valori uguali fra loro, ma ciò non vale per .

N.B.: Nell’algoritmo diamo precedenza alle , che non diventano mai .

Dimostrazione della validità dell’algoritmo

todo pag 25 pdf 15

Esempio

Si hanno:

Determinare se , così decomposto, ha un join senza perdita.

Costruzione iniziale della tabella:

ABCDE
ACa1b12a3b14b15
ADEa1b22b23a4a5
CDEb31b32a3a4a5
ADa1b42b43a4b45
Bb51a2b53b54b55

Prima iterazione

  • : la prima e la terza riga coincidono sull’attributo a3, quindi cambiano b14 in a4 in modo che sia soddisfatta (si cambiano sempre le b in a, mai il contrario).
ABCDE
ACa1b12a3b14 a4b15
ADEa1b22 b12b23a4a5
CDEb31b32 b12a3a4a5
ADa1b42 b12b43a4b45
Bb51a2b53b54b55
  • : la dipendenza funzionale è già soddisfatta, in quanto non ci sono (ancora) tuple uguali su AB e diverse su E.
  • : nelle prime quattro righe D=a4, quindi cambiamo b22 in b12, b32 in b12, b42 in b12 (potevano scegliere una diversa sostituzione delle b, purché le rendesse tutte uguali)

Seconda iterazione

  • è già soddisfatta.
  • : sostituiamo b15 e b45 con a5
ABCDE
ACa1b12a3b14 a4b15 a5
ADEa1b22 b12b23a4a5
CDEb31b32 b12a3a4a5
ADa1b42 b12b43a4b45 a5
Bb51a2b53b54b55

Terza iterazione

  • è già soddisfatta
  • è già soddisfatta
  • è già soddisfatta

Non c’è una riga con tutte a, il join non è senza perdita.