Home > programavimas > sudėtiniai pirminiai lentelių raktai

sudėtiniai pirminiai lentelių raktai

August 23rd, 2007 Leave a comment Go to comments

Kuriant duomenų bazės struktūrą, verta nurodyti pirminius lentelių raktus. Didžioji dalis taip ir daro: sukuria sveikojo skaičiaus tipo stulpelį, pavadina jį “id”, arba “lenteleles_pavadinimas_id”, ir nurodo, jog į jį bus rašomos automatiškai inkrementuojamos reikšmės. Pavyzdžiui (mySQL):

create table customers(
id int not null auto_increment,
/* ...*/
primary key(id)
);

viskas atrodo gražu ir paprasta, tačiau ar visada prasminga ir efektyvu? Pirminis raktas turi vienareikšmiškai apibūdinti DB lentelės eilutę. Automatiškai sugeneruotas id tai daro, tačiau tai yra sintetinis būdas padaryti duomenys unikaliais. Reikia nepamiršti, kad pirminis raktas gali būti ne tik natūralus skaičius, tačiau ir tekstiniai duomenys, data, bei kelių stulpelių rinkinys (šiuo atveju raktas vadinamas sudėtiniu), vienareikšmiškai apibūdinantis esybę:

/* .. */
primary key(col1, col2);
/* .. */

Panagrinėkime realią, dažnai praktikoje sutinkamą situaciją: turime lentelę, kurioje saugome klientų duomenis (apibrėžta aukščiau) ir produktų lentelę:

create table products(
/* .. */
primary key(id)
);

mums šiuo atveju visiškai neįdomu, kaip sudaryti šių lentelių pirminiai raktai. Norime suprojektuoti duomenų bazės lentelę, skirtą saugoti, tarkime apribojimams, kuriuos turi klientai, norintys įsigyti mūsų produkcijos. Čia turime sąryšį daug su daug (m su n). Natūralu, kad šiems duomenims saugoti kursime atskirą sąryšio lentelę products_customers, kurioje yra apibrėžti išoriniai raktai į produktų ir klientų lenteles, bei stulpelis, skirtas saugoti mus dominantiems apribojimams. Taikydami aukščiau aprašytą praktiką naudoti automatiškai generuojamą pirminį raktą, gausime lentelę, aprašomą tokiais DDT sakiniais:

create table products_customers(
id int not null auto_increment,
product_id int not null,
customer_id int not null,
limit_ int not null,
foreign key (product_id) references products(id),
foreign key (customer_id) references customers(id),
primary key(id)
);

sukuriame išorinius raktus, pirminį raktą, atrodo lyg ir užtikriname duomenų vientisumą. Deja.. Tarkime, turime tokius duomenis lentelėje “products_customers”:

id | product_id | customer_id | limit_
—————————————
1 | 25 | 100 | 200
2 | 25 | 100 | 259

produktas tas pats, klientas taip pat, taigi, koks jo apribojimas? negalime vienareikšmiškai pasakyti.. galime tik spėlioti. Kodėl taip atsitiko? Atsakymas paprastas: mūsų DB struktūra neužtikrina duomenų neprieštaringumo. Kaip to išvengti? Paprasta: pakeisti pirminį lentelės raktą į štai tokį:

/* .. */
primary key (product_id, customer_id);
/* .. */

toks pirminis raktas įpareigos DBVS prižiūrėti, jog nagrinėjamoje lentelėje būtų užtikrinama unikali rinkinio (product_id, customer_id) reikšmė įterpiant, arba keičiant duomenis. Ko visiškai pakanka, kad neatsirastų minėtos duomenų anomalijos. Be to, greičiausiai, mums jau nebereikia stulpelio id, taip padarome DB struktūrą dar paprastesne. Taigi galutinis lentelės struktūros kūrimo sakinio variantas būtų toks:

create table products_customers(
product_id int not null,
customer_id int not null,
limit_ int not null,
foreign key (product_id) references products(id),
foreign key (customer_id) references customers(id),
primary key(product_id, customer_id)
);

Taigi, panaudodami sudėtinį raktą:

  • išsprendėme vieną duomenų anomaliją,
  • supaprastinome DB schemą.

Sakyčiau neblogas rezultatas :)

Categories: programavimas Tags: ,
  1. Stratego
    August 24th, 2007 at 14:41 | #1

    Čia pats viską rašei? Good :)

  2. kran
    August 24th, 2007 at 16:19 | #2

    ne, copy + paste :) vienas stripsnis mane ikvepe, tai nusprendziau parasyti sita tema :)

  3. Sakinis
    August 31st, 2007 at 13:51 | #3

    gud job! tos lenteles kartais tampa mistika. Laukiam dar tokios tematikos straipsniu :)

  4. ZaZa
    September 1st, 2007 at 15:39 | #4

    Kalbant apie pirminius raktus:
    “id int not null auto_increment” – gali laisvai daryti “unsigned” MySQL:e (čia prie žodžio).

    Be to tavo pavyzdis yra many-to-many ryšis, todėl jam primary key su ID nereikalinas.

    Tokie nelogiški pirminio rakto panaudojimai (IMHO) su many-to-many atsirado atejus į mūsų pasaulį ActiveRecord modeliams, kurie nevisada gerai palaike ne auto_increment pirminius raktus.

  1. No trackbacks yet.