Crearea unui model fizic de bază de date: ingineria performanței. Crearea unui model fizic de bază de date: Ms sql server performance design, partiționare automată a tabelelor

Când lucrăm pe tabele mari, întâmpinăm în mod constant probleme de performanță la menținerea și actualizarea datelor. Una dintre cele mai productive și convenabile soluții la problemele emergente este partiționarea.
În termeni generali, partiționarea este împărțirea unui tabel sau index în blocuri. În funcție de configurația de partiționare, blocurile pot fi diferite dimensiuni, poate fi stocat în diferite grupuri de fișiere și fișiere.
Partiționarea are atât avantaje, cât și dezavantaje.
Beneficiile sunt bine descrise pe site Microsoft, voi da un extras:

« Partiționarea tabelelor sau a indecșilor mari poate oferi următoarele beneficii de gestionare și performanță:

  • Acest lucru permite ca subseturile de date să fie mutate și accesate rapid și eficient, menținând în același timp integritatea setului de date. De exemplu, o operație precum încărcarea datelor din OLTP în sistem OLAP, se finalizează în secunde și nu în minute și ore, ca în cazul datelor nepartiționate.
  • Operațiunile de întreținere pot fi finalizate mai rapid cu una sau mai multe secțiuni. Operațiile sunt mai eficiente deoarece sunt efectuate doar pe subseturi de date, mai degrabă decât pe întregul tabel. De exemplu, puteți comprima datele în una sau mai multe partiții sau puteți reconstrui una sau mai multe partiții index.
  • Puteți îmbunătăți viteza de execuție a interogărilor pe baza interogărilor care sunt executate frecvent în configurația hardware. De exemplu, optimizatorul de interogări poate rula interogări equijoin mai rapid pe două sau mai multe tabele partiționate dacă tabelele au aceleași coloane de partiție, deoarece partițiile înseși pot fi unite.

Când sortează datele pentru operațiuni I/O în SQL Server, mai întâi sortează datele în partiții. SQL Server poate accesa doar un disc la un moment dat, ceea ce poate reduce performanța. Pentru a accelera sortarea datelor, se recomandă distribuirea fișierelor de date în secțiuni în mai multe hard disk-uri prin crearea unui RAID. În acest fel, chiar dacă datele sunt sortate după partiție, SQL Server va putea accesa simultan toate hard disk-urile din fiecare partiție.
De asemenea, puteți îmbunătăți performanța utilizând blocări la nivel de partiție, mai degrabă decât întregul tabel. Acest lucru poate reduce numărul de conflicte de blocare pentru un tabel
».

Dezavantajele includ dificultatea de a administra și susține funcționarea tabelelor partiționate.

Nu ne vom opri asupra implementării partiționării, deoarece această problemă este foarte bine descrisă pe site-ul Microsoft.

În schimb, vom încerca să arătăm o modalitate de a optimiza funcționarea tabelelor partiționate sau, mai degrabă, vom arăta modalitatea optimă (în opinia noastră) de a actualiza datele pentru orice perioadă de timp.

Marele avantaj al unui tabel partiționat este separarea fizică a acestor secțiuni. Această proprietate ne permite să schimbăm secțiuni între ele sau cu orice alt tabel.
Pentru o actualizare tipică a datelor folosind o fereastră glisantă (de exemplu, lunară), ar trebui să parcurgem următorii pași:

1. Găsiți rândurile necesare într-un tabel mare;
2. Ștergeți rândurile găsite din tabel și index;
3. Introduceți rânduri noi în tabel, actualizați indexul.

Atunci când stocați miliarde de rânduri într-un tabel, aceste operațiuni vor dura destul de mult pentru o lungă perioadă de timp, ne putem limita practic la o singură acțiune: pur și simplu înlocuiți secțiunea dorită cu un tabel (sau secțiune) pregătit în prealabil. În acest caz, nu trebuie să ștergem sau să inserăm rânduri și, de asemenea, trebuie să actualizăm indexul pe întregul tabel mare.

Să trecem de la cuvinte la acțiune și să arătăm cum să implementăm acest lucru.

1. Mai întâi, configurați un tabel partiționat așa cum este descris în articolul menționat mai sus.
2. Creați tabelele necesare schimbului.

Pentru a actualiza datele, avem nevoie de o mini-copie a tabelului țintă. Este o mini-copie deoarece va stoca date care ar trebui adăugate la tabelul țintă, de exemplu. date pentru doar 1 lună. De asemenea, veți avea nevoie de un al treilea tabel gol pentru a implementa schimbul de date. De ce este nevoie - voi explica mai târziu.

Mini-copie și tabel pentru schimb sunt impuse condiții stricte:

  • Înainte de utilizare operator SWITCH Ambele tabele trebuie să existe. Înainte de a putea fi efectuată o operație de comutare, atât tabela din care este mutată partiția (tabelul sursă), cât și tabela care primește partiția (tabelul țintă) trebuie să existe în baza de date.
  • Secțiunea de destinație trebuie să existe și trebuie să fie goală. Dacă o tabelă este adăugată ca partiție la o tabelă partiționată existentă sau o partiție este mutată dintr-un tabel partiționat în altul, partiția de destinație trebuie să existe și să fie goală.
  • Tabelul secundar nepartiționat trebuie să existe și trebuie să fie gol. Dacă partiția este destinată să formeze o singură tabelă nepartiționată, atunci tabela care primește noua partiție trebuie să existe și să fie o tabelă nepartiționată goală.
  • Secțiunile trebuie să fie din aceeași coloană. Dacă o partiție este comutată de la un tabel partiționat la altul, atunci ambele tabele trebuie să fie partiționate pe aceeași coloană.
  • Tabelele sursă și destinație trebuie să fie în același grup de fișiere. Tabelele sursă și țintă dintr-o instrucțiune ALTER TABLE...SWITCH trebuie să fie stocate în același grup de fișiere, precum și coloanele cu valori mari ale acestora. Orice indexuri corespondente, partiții indexate sau vizualizări ale partițiilor indexate trebuie, de asemenea, stocate în același grup de fișiere. Cu toate acestea, poate fi diferit de grupul de fișiere pentru tabelele corespunzătoare sau alți indecși relevanți.

Permiteți-mi să explic limitările folosind exemplul nostru:

1. Mini-copia tabelului trebuie să fie împărțită pe aceeași coloană cu cea țintă. Dacă minicopia este un tabel nepartiționat, atunci trebuie să fie stocată în același grup de fișiere ca și partiția care este înlocuită.

2. Tabelul pentru schimb trebuie să fie gol și trebuie să fie, de asemenea, partiționat de aceeași coloană sau trebuie să fie stocat în același grup de fișiere.

3. Implementăm schimbul.

Acum avem următoarele:
Tabel cu date pentru toate timpurile (denumit în continuare Tabel_A)
Tabel cu date pentru o lună (denumit în continuare Tabel_B)
Tabel gol (denumit în continuare Tabel_C)

În primul rând, trebuie să aflăm în ce secțiune stocăm datele.
Puteți afla întrebând:

SELECTA
numără (*) ca
, $PARTITION.(dt) ca
, rank() peste (ordonați după $PARTITION.(dt))
DE LA DBO. (nolock)
grupați după $PARTITION.(dt)

În această solicitare primim secțiuni care conțin rânduri de informații. Nu este nevoie să numărăm cantitatea - am făcut acest lucru pentru a verifica necesitatea schimbului de date. Folosim Rank astfel încât să putem merge într-o buclă și să actualizăm mai multe secțiuni într-o singură procedură.

De îndată ce aflăm în ce secțiuni sunt stocate datele noastre, acestea pot fi schimbate. Să presupunem că datele sunt stocate în secțiunea 1.

Apoi, trebuie să efectuați următoarele operații:
Schimbați partițiile din tabelul țintă cu tabelul care urmează să fie schimbat.
ALTER TABLE. COMUTAȚI PARTIȚIA 1 LA . PARTIȚIA 1
Acum avem următoarele:
Nu există date rămase în tabelul țintă din secțiunea de care avem nevoie, adică. secțiunea este goală
Schimbați partițiile din tabelul țintă și minicopie
ALTER TABLE. COMUTAȚI PARTIȚIA 1 LA . PARTIȚIA 1
Acum avem următoarele:
Datele lunare au apărut în tabelul țintă, dar mini-copia este acum goală
Ștergeți sau ștergeți tabelul de schimb.

Dacă aveți un index grupat pe tabel, atunci nici acesta nu este o problemă. Trebuie creat pe toate cele 3 tabele partiționate pe aceeași coloană. La schimbarea secțiunilor, indexul va fi actualizat automat fără reconstrucție.

Bună seara/zi/dimineața, dragi oameni! Continuăm să dezvoltăm și să extindem blogul despre rdbms-ul meu open source preferat Postgresql. În mod miraculos, s-a întâmplat ca subiectul subiectului de astăzi să nu fi fost niciodată abordat aici înainte. Trebuie să spun că partiționarea în postgresql este foarte bine descrisă în documentație, dar asta mă va opri cu adevărat?).

Introducere

În general, partiționarea este în general înțeleasă nu ca un fel de tehnologie, ci mai degrabă ca o abordare a proiectării bazelor de date care a apărut cu mult înainte ca SGBD-urile să înceapă să susțină așa-numita. tabele compartimentate. Ideea este foarte simplă - împărțiți masa în mai multe părți mai mici. Există două subtipuri - secționare orizontală și verticală.
Compartimentare orizontală
Părțile unui tabel conțin diferite rânduri. Să presupunem că avem un tabel de jurnal pentru o aplicație abstractă - LOGS. Îl putem împărți în părți - una pentru jurnalele din ianuarie 2009, alta pentru februarie 2009 etc.
Compartimentare verticală
Părțile unui tabel conțin coloane diferite. Găsirea unei aplicații pentru partiționarea verticală (când este de fapt justificată) este ceva mai dificilă decât pentru partiționarea orizontală. Ca un cal sferic, îmi propun să luăm în considerare această opțiune: tabelul NEWS are coloanele ID, SHORTTEXT, LONGTEXT și lasă câmpul LONGTEXT să fie folosit mult mai puțin frecvent decât primele două. În acest caz, are sens să împărțiți tabelul NEWS pe coloane (creați două tabele pentru SHORTTEXT și, respectiv, LONGTEXT, conectate prin chei primare + creați o vizualizare NEWS care conține ambele coloane). Astfel, atunci când avem nevoie doar de o descriere a știrilor, SGBD-ul nu trebuie să citească întregul text al știrilor de pe disc.
Suport pentru partiționare în SGBD-uri moderne
Cele mai multe SGBD-uri moderne acceptă partiționarea tabelelor într-o formă sau alta.
  • Oracol- acceptă partiționarea începând cu versiunea 8. Lucrul cu secțiuni, pe de o parte, este foarte simplu (nu trebuie să te gândești deloc la ele, lucrezi ca la o masă obișnuită*), iar pe de altă parte, totul este foarte flexibil. Secțiunile pot fi împărțite în „subpartiții”, șterse, divizate, transferate. Sprijinit opțiuni diferite indexarea unui tabel partitionat (index global, index partitionat). Link către o descriere voluminoasă.
  • Microsoft SQL Server- suportul pentru partiţionare a apărut recent (în 2005). Prima impresie de utilizare este „Ei bine, în sfârșit :)”, a doua este „Funcționează, totul pare să fie în regulă.” Documentatie pe msdn
  • MySQL- suportă începând cu versiunea 5.1. Descriere foarte bună pentru Habré
  • Și așa mai departe…
*-Mint, desigur că există set standard dificultăți - crearea unei noi secțiuni la timp, aruncarea celei vechi etc., dar totuși, cumva, totul este simplu și clar.

Partiționarea în Postgresql

Partiționarea tabelelor în postgresql este ușor diferită în implementare față de alte baze de date. Baza partiționării este moștenirea tabelelor (un lucru unic pentru postgresql). Adică trebuie să avem un tabel principal, iar secțiunile sale vor fi tabele succesoare. Vom lua în considerare partiționarea folosind un exemplu de sarcină apropiată de realitate.
Enunțarea problemei
Baza de date este utilizată pentru a colecta și analiza date despre vizitatorii site-ului/site-urilor. Volumele de date sunt suficient de mari pentru a se gândi la partiționare. În cele mai multe cazuri, analiza utilizează date din ultima zi.
1. Creați tabelul principal:
CREATE TABLE analytics.evenimente

user_id UUID NOT NULL ,
event_type_id SMALLINT NOT NULL ,
event_time TIMESTAMP DEFAULT now() NOT NULL ,
url VARCHAR (1024) NOT NULL ,
referitor VARCHAR (1024),
ip INET NU NUL
);

2. Vom partiționa pe zi folosind câmpul event_time. Vom crea o nouă secțiune pentru fiecare zi. Vom denumi secțiunile conform regulii: analytics.events_DDMMAAAA. Iată, de exemplu, secțiunea pentru 1 ianuarie 2010.
CREATE TABLE analytics.events_01012010
event_id BIGINT DEFAULT nextval("analytics.seq_events" ) CHEIE PRIMARĂ ,
VERIFICAȚI (event_time >= TIMESTAMP „2010-01-01 00:00:00” ȘI event_time< TIMESTAMP "2010-01-02 00:00:00" )
) MOȘTENȚE (analytics.events);

* Acest cod sursă a fost evidențiat cu Sursa de evidențiere a codului.


Când creăm o secțiune, setăm în mod explicit câmpul event_id (CHEIA PRIMĂRĂ nu este moștenită) și creăm o CONSTRINGERE VERIFICARE pe câmpul event_time pentru a nu insera lucruri inutile.

3. Creați un index în câmpul event_time. Când împărțim tabelul, ne așteptăm ca majoritatea interogărilor din tabelul de evenimente să folosească o condiție în câmpul event_time, așa că un index pe acest câmp ne va ajuta foarte mult.

CREATE INDEX events_01012010_event_time_idx ON analytics.events_01012010 UTILIZAND btree(event_time);

* Acest cod sursă a fost evidențiat cu Sursa de evidențiere a codului.


4. Dorim să ne asigurăm că atunci când sunt introduse în tabelul principal, datele ajung în secțiunea destinată acestuia. Pentru a face acest lucru, facem următorul truc - creăm un declanșator care va controla fluxurile de date.
CREATE SAU ÎNLOCUȚI FUNCȚIA analytics.events_insert_trigger()
RETURNEAZĂ DEclanșatorul ca $$
ÎNCEPE
DACĂ (NOU .event_time >= TIMESTAMP „2010-01-01 00:00:00” ȘI
NOU .event_time< TIMESTAMP "2010-01-02 00:00:00" ) THEN
INSERT INTO analytics.events_01012010 VALUES (NOU .*);
ALTE
RISCĂ EXCEPȚIA „Data % este în afara intervalului. Remediați analytics.events_insert_trigger”, NOU .event_time;
END IF ;
RETURN NULL ;
SFÂRŞIT ;
$$
LIMBA plpgsql;

* Acest cod sursă a fost evidențiat cu Sursa de evidențiere a codului.


CREATE TRIGGER events_before_insert
ÎNAINTE DE INSERT PE analytics.evenimente
PENTRU FIECARE RÂND EXECUTĂ PROCEDURA analytics.events_insert_trigger();

* Acest cod sursă a fost evidențiat cu Sursa de evidențiere a codului.

5. Totul este gata, acum avem un tabel partiționat numit analytics.events. Putem începe să analizăm cu furie datele ei. Apropo, am creat constrângeri CHECK nu numai pentru a proteja secțiunile de datele incorecte. Postgresql le poate folosi atunci când creează un plan de interogare (cu toate acestea, cu un index live pe event_time, câștigul va fi minim), trebuie doar să utilizați directiva constraint_exclusion:

SET constraint_exclusion = on ;
SELECT * FROM analytics.events WHERE event_time > CURRENT_DATE ;

* Acest cod sursă a fost evidențiat cu Sursa de evidențiere a codului.

Sfârșitul primei părți
Deci ce avem? Să mergem punct cu punct:
1. Tabelul de evenimente, împărțit pe secțiuni, analiza datelor disponibile pentru ultima zi devine mai ușoară și mai rapidă.
2. Oroarea de a realiza că toate acestea trebuie susținute cumva, secțiunile trebuie create la timp, fără a uita să schimbi declanșatorul în consecință.

Vă voi spune cum să lucrați ușor și fără griji cu tabele partiționate în a doua parte.

UPD1: partiţionarea a fost înlocuită cu partiţionarea
UPD2:
Pe baza unui comentariu al unuia dintre cititorii care, din păcate, nu are cont pe Habré:
Există mai multe probleme asociate cu moștenirea care ar trebui să fie luate în considerare la proiectare. Secțiunile nu moștenesc cheia primară și chei externe la coloanele lor. Adică, atunci când creați o secțiune, trebuie să creați în mod explicit CHEIE PRIMARĂ și CHEIE străine pentru coloanele secțiunii. Aș dori să notez pe cont propriu că crearea cheii străine pe coloanele unui tabel partiționat nu este cea mai bună modalitate. În cele mai multe cazuri, tabelul partiționat este un „tabel de fapte” și el însuși se referă la o „dimensiune” a tabelului.

Bună seara/zi/dimineața, dragi oameni! Continuăm să dezvoltăm și să extindem blogul despre rdbms-ul meu open source preferat Postgresql. În mod miraculos, s-a întâmplat ca subiectul subiectului de astăzi să nu fi fost niciodată abordat aici înainte. Trebuie să spun că partiționarea în postgresql este foarte bine descrisă în documentație, dar asta mă va opri cu adevărat?).

Introducere

În general, partiționarea este în general înțeleasă nu ca un fel de tehnologie, ci mai degrabă ca o abordare a proiectării bazelor de date care a apărut cu mult înainte ca SGBD-urile să înceapă să susțină așa-numita. tabele compartimentate. Ideea este foarte simplă - împărțiți masa în mai multe părți mai mici. Există două subtipuri - secționare orizontală și verticală.
Compartimentare orizontală
Părțile unui tabel conțin diferite rânduri. Să presupunem că avem un tabel de jurnal pentru o aplicație abstractă - LOGS. Îl putem împărți în părți - una pentru jurnalele din ianuarie 2009, alta pentru februarie 2009 etc.
Compartimentare verticală
Părțile unui tabel conțin coloane diferite. Găsirea unei aplicații pentru partiționarea verticală (când este de fapt justificată) este ceva mai dificilă decât pentru partiționarea orizontală. Ca un cal sferic, îmi propun să luăm în considerare această opțiune: tabelul NEWS are coloanele ID, SHORTTEXT, LONGTEXT și lasă câmpul LONGTEXT să fie folosit mult mai puțin frecvent decât primele două. În acest caz, are sens să împărțiți tabelul NEWS pe coloane (creați două tabele pentru SHORTTEXT și, respectiv, LONGTEXT, conectate prin chei primare + creați o vizualizare NEWS care conține ambele coloane). Astfel, atunci când avem nevoie doar de o descriere a știrilor, SGBD-ul nu trebuie să citească întregul text al știrilor de pe disc.
Suport pentru partiționare în SGBD-uri moderne
Cele mai multe SGBD-uri moderne acceptă partiționarea tabelelor într-o formă sau alta.
  • Oracol- acceptă partiționarea începând cu versiunea 8. Lucrul cu secțiuni, pe de o parte, este foarte simplu (nu trebuie să te gândești deloc la ele, lucrezi ca la o masă obișnuită*), iar pe de altă parte, totul este foarte flexibil. Secțiunile pot fi împărțite în „subpartiții”, șterse, divizate, transferate. Sunt acceptate diverse opțiuni pentru indexarea unui tabel partiționat (index global, index partiționat). Link către o descriere lungă.
  • Microsoft SQL Server- suportul pentru partiţionare a apărut recent (în 2005). Prima impresie de utilizare este „Ei bine, în sfârșit :)”, a doua este „Funcționează, totul pare să fie în regulă.” Documentatie pe msdn
  • MySQL- suportă începând cu versiunea 5.1.
  • Și așa mai departe…
*-Mint, desigur, există un set standard de dificultăți - crearea la timp a unei noi secțiuni, aruncarea celei vechi etc., dar totuși, cumva, totul este simplu și clar.

Partiționarea în Postgresql

Partiționarea tabelelor în postgresql este ușor diferită în implementare față de alte baze de date. Baza partiționării este moștenirea tabelelor (un lucru unic pentru postgresql). Adică trebuie să avem un tabel principal, iar secțiunile sale vor fi tabele succesoare. Vom lua în considerare partiționarea folosind un exemplu de sarcină apropiată de realitate.
Enunțarea problemei
Baza de date este utilizată pentru a colecta și analiza date despre vizitatorii site-ului/site-urilor. Volumele de date sunt suficient de mari pentru a se gândi la partiționare. În cele mai multe cazuri, analiza utilizează date din ultima zi.
1. Creați tabelul principal:
CREATE TABLE analytics.evenimente

user_id UUID NOT NULL ,
event_type_id SMALLINT NOT NULL ,
event_time TIMESTAMP DEFAULT now() NOT NULL ,
url VARCHAR (1024) NOT NULL ,
referitor VARCHAR (1024),
ip INET NU NUL
);

2. Vom partiționa pe zi folosind câmpul event_time. Vom crea o nouă secțiune pentru fiecare zi. Vom denumi secțiunile conform regulii: analytics.events_DDMMAAAA. Iată, de exemplu, secțiunea pentru 1 ianuarie 2010.
CREATE TABLE analytics.events_01012010
event_id BIGINT DEFAULT nextval("analytics.seq_events" ) CHEIE PRIMARĂ ,
VERIFICAȚI (event_time >= TIMESTAMP „2010-01-01 00:00:00” ȘI event_time< TIMESTAMP "2010-01-02 00:00:00" )
) MOȘTENȚE (analytics.events);

* Acest cod sursă a fost evidențiat cu Sursa de evidențiere a codului.


Când creăm o secțiune, setăm în mod explicit câmpul event_id (CHEIA PRIMĂRĂ nu este moștenită) și creăm o CONSTRINGERE VERIFICARE pe câmpul event_time pentru a nu insera lucruri inutile.

3. Creați un index în câmpul event_time. Când împărțim tabelul, ne așteptăm ca majoritatea interogărilor din tabelul de evenimente să folosească o condiție în câmpul event_time, așa că un index pe acest câmp ne va ajuta foarte mult.

CREATE INDEX events_01012010_event_time_idx ON analytics.events_01012010 UTILIZAND btree(event_time);

* Acest cod sursă a fost evidențiat cu Sursa de evidențiere a codului.


4. Dorim să ne asigurăm că atunci când sunt introduse în tabelul principal, datele ajung în secțiunea destinată acestuia. Pentru a face acest lucru, facem următorul truc - creăm un declanșator care va controla fluxurile de date.
CREATE SAU ÎNLOCUȚI FUNCȚIA analytics.events_insert_trigger()
RETURNEAZĂ DEclanșatorul ca $$
ÎNCEPE
DACĂ (NOU .event_time >= TIMESTAMP „2010-01-01 00:00:00” ȘI
NOU .event_time< TIMESTAMP "2010-01-02 00:00:00" ) THEN
INSERT INTO analytics.events_01012010 VALUES (NOU .*);
ALTE
RISCĂ EXCEPȚIA „Data % este în afara intervalului. Remediați analytics.events_insert_trigger”, NOU .event_time;
END IF ;
RETURN NULL ;
SFÂRŞIT ;
$$
LIMBA plpgsql;

* Acest cod sursă a fost evidențiat cu Sursa de evidențiere a codului.


CREATE TRIGGER events_before_insert
ÎNAINTE DE INSERT PE analytics.evenimente
PENTRU FIECARE RÂND EXECUTĂ PROCEDURA analytics.events_insert_trigger();

* Acest cod sursă a fost evidențiat cu Sursa de evidențiere a codului.

5. Totul este gata, acum avem un tabel partiționat numit analytics.events. Putem începe să analizăm cu furie datele ei. Apropo, am creat constrângeri CHECK nu numai pentru a proteja secțiunile de datele incorecte. Postgresql le poate folosi atunci când creează un plan de interogare (cu toate acestea, cu un index live pe event_time, câștigul va fi minim), trebuie doar să utilizați directiva constraint_exclusion:

SET constraint_exclusion = on ;
SELECT * FROM analytics.events WHERE event_time > CURRENT_DATE ;

* Acest cod sursă a fost evidențiat cu Sursa de evidențiere a codului.

Sfârșitul primei părți
Deci ce avem? Să mergem punct cu punct:
1. Tabelul de evenimente, împărțit pe secțiuni, analiza datelor disponibile pentru ultima zi devine mai ușoară și mai rapidă.
2. Oroarea de a realiza că toate acestea trebuie susținute cumva, secțiunile trebuie create la timp, fără a uita să schimbi declanșatorul în consecință.

Vă voi spune cum să lucrați ușor și fără griji cu tabele partiționate în a doua parte.

UPD1: partiţionarea a fost înlocuită cu partiţionarea
UPD2:
Pe baza unui comentariu al unuia dintre cititorii care, din păcate, nu are cont pe Habré:
Există mai multe probleme asociate cu moștenirea care ar trebui să fie luate în considerare la proiectare. Partițiile nu moștenesc cheia primară și cheile externe pe coloanele lor. Adică, atunci când creați o secțiune, trebuie să creați în mod explicit CHEIE PRIMARĂ și CHEIE străine pentru coloanele secțiunii. Aș dori să notez pe cont propriu că crearea cheii străine pe coloanele unui tabel partiționat nu este cea mai bună modalitate. În cele mai multe cazuri, tabelul partiționat este un „tabel de fapte” și el însuși se referă la o „dimensiune” a tabelului.