1. blokk · Tervezés és modellezés · 1. témakör
Mielőtt egyetlen CREATE TABLE utasítást is leírnánk, meg kell
terveznünk, mit és hogyan tárolunk. Ebben a fejezetben a relációs adatbázisok
alapfogalmait, a kulcsokat, a kapcsolatokat és a normalizálást nézzük meg —
végig egy webshop példáján.
Az adatbázis rendezett módon tárolt adatok gyűjteménye, amelyet úgy alakítunk ki, hogy gyorsan és megbízhatóan tudjunk benne keresni, adatot beszúrni, módosítani és törölni. Egy webshop esetében ilyen adat például a vásárlók listája, a termékek, vagy a leadott rendelések.
Az adatbázist nem közvetlenül kezeljük, hanem egy adatbázis-kezelő rendszeren (angolul DBMS – Database Management System) keresztül. A mi esetünkben ez a MySQL. A DBMS feladata, hogy fogadja az utasításainkat (lekérdezéseket), gondoskodjon az adatok épségéről, a párhuzamos hozzáférésről és a biztonságról.
A MySQL egy relációs adatbázis-kezelő. Az utasításait az SQL (Structured Query Language) nyelven adjuk ki. Ez egy szabványos nyelv, így amit itt megtanulsz, az nagyrészt más rendszerekben (MariaDB, PostgreSQL, SQL Server) is működik.
A relációs (SQL-alapú) rendszerek a legelterjedtebbek — ezt tanuljuk mi is —, de nem ez az egyetlen út. A nem relációs rendszereket összefoglaló néven NoSQL-nek („not only SQL” — nem csak SQL) nevezzük. Több családjuk van: mindegyik más adatmodellt használ, és más feladatra ideális.
| Csoport | Hogyan tárolja az adatot | Ismert képviselők | Tipikus felhasználás |
|---|---|---|---|
| Relációs (SQL) | Táblákban (sorok + oszlopok), kulcsokkal összekapcsolva | MySQL, MariaDB, PostgreSQL, SQL Server, Oracle, SQLite | üzleti adatok, webshopok, számlázás, banki rendszerek |
| Dokumentum-alapú | JSON-szerű, rugalmas szerkezetű dokumentumokban | MongoDB, CouchDB, Firebase Firestore | webalkalmazások változó szerkezetű adatai (pl. termékleírások) |
| Kulcs–érték | Egyszerű kulcs → érték párokban, villámgyorsan | Redis, Memcached, Amazon DynamoDB | gyorsítótár (cache), munkamenetek (session), ranglisták |
| Oszlopcsaládos | Hatalmas táblák oszloponként tárolva, sok gépen elosztva | Apache Cassandra, HBase, Google Bigtable | óriási adattömegek, naplók, mérési adatok (big data) |
| Gráf | Csomópontok és a köztük futó élek (kapcsolatok) formájában | Neo4j, Amazon Neptune | kapcsolati hálók: ismerősök, ajánlórendszerek, útvonalak |
Jól strukturált adat, fontos kapcsolatok és tranzakciók (webshop, számlázás, bank) → relációs. Rugalmas, változó szerkezet, óriási adatmennyiség vagy speciális modell (pl. ismerősi háló) → valamelyik NoSQL-család. A kettő nem ellenség: egy nagy webshop gyakran többet használ egyszerre — SQL-t a rendelésekhez, Redist a gyorsítótárhoz.
A relációs családba tartozik a beágyazott SQLite is: nem külön szerver, hanem egyetlen fájl az alkalmazáson belül. Telefonos appok és böngészők milliárdjai használják — és a tananyag élő SQL-gyakorlója is ezen fut a 4. témakörtől kezdve.
A relációs modellben az adatokat táblákban tároljuk. Egy tábla olyan, mint egy jól megszervezett táblázat: oszlopokból (mezők) és sorokból (rekordok) áll.
vasarlo tábla felépítése. A lila keret egy teljes
sort (rekordot), a kék keret egy teljes oszlopot (mezőt) jelöl — metszetük a
zöld keretes cella: egyetlen érték.Egy jól megtervezett relációs adatbázisban minden adatot pontosan egy helyen tárolunk. Ha ugyanaz az információ több helyen szerepel (redundancia), az hibákhoz vezet: könnyen előfordul, hogy az egyik helyen frissítjük, a másikon nem.
A tervezés első lépése, hogy összegyűjtjük, miről akarunk adatot tárolni. Ezeket a „dolgokat” nevezzük entitásoknak. Egy webshopban tipikus entitások:
vásárló termék kategória rendelés cím
Minden entitásból később egy tábla lesz. Az entitás tulajdonságait attribútumoknak nevezzük — ezekből lesznek az oszlopok. Például a termék entitás attribútumai: név, ár, leírás, készlet.
| Tervezési fogalom | Adatbázisbeli megfelelő | Webshop példa |
|---|---|---|
| Entitás | Tábla | termek |
| Attribútum | Oszlop (mező) | ar, nev |
| Egyed / előfordulás | Sor (rekord) | „Vezeték nélküli egér, 4990 Ft” |
Érdemes az egész adatbázisban következetes elnevezést használni: kisbetűs, ékezet nélküli,
egyes számú táblanevek (termek, nem
Termékek), és beszédes oszlopnevek. Ez később sok hibát megelőz.
Hogyan különböztetünk meg egyértelműen két vásárlót, ha történetesen mindkettőt Nagy Annának hívják? Erre valók a kulcsok.
Az elsődleges kulcs egy olyan oszlop (vagy oszlopok együttese), amely
egyértelműen azonosít minden sort a táblában. Két fontos szabály: az értéke nem
ismétlődhet (egyedi), és nem lehet üres (NULL). A gyakorlatban szinte
mindig egy automatikusan növekvő id oszlopot használunk erre.
Az idegen kulcs egy olyan oszlop, amely egy másik tábla elsődleges
kulcsára hivatkozik. Így kötjük össze a táblákat. Például a
rendeles táblában egy vasarlo_id oszlop
megmondja, melyik vásárló adta le a rendelést.
| Kulcs | Mit csinál |
|---|---|
| Elsődleges (PRIMARY KEY) | Egyértelműen azonosít minden sort. Egyedi és nem NULL. |
| Idegen (FOREIGN KEY) | Másik tábla elsődleges kulcsára hivatkozik — összeköti a táblákat. |
| Jelölt (candidate) | Bármely oszlop, ami egyedileg azonosíthatna (pl. e-mail). Ezek közül választjuk ki az elsődlegeset. |
| Összetett (composite) | Több oszlopból álló kulcs (pl. rendelés_id + termék_id együtt). |
Az e-mail-cím elméletileg egyedi, tehát lehetne elsődleges kulcs — a gyakorlatban mégis
kerüljük. Egy id rövidebb, sosem változik, és gyorsabb vele a
keresés. Az e-mailre inkább egyedi (UNIQUE) megszorítást teszünk.
Az entitások között kapcsolatok vannak. A kapcsolat számossága (kardinalitása) azt mondja meg, hány sor kapcsolódhat egymáshoz. Három alaptípus van.
1:1 — egy sorhoz pontosan egy másik tartozik (pl. egy vásárlónak egy kiterjesztett profilja). Ritka; gyakran egyetlen táblába is összevonható.
1:N — a leggyakoribb. Egy vásárlóhoz több rendelés tartozhat, de egy rendelés egyetlen vásárlóhoz. Megvalósítás: a „sok” oldali táblába (rendeles) teszünk egy idegen kulcsot.
Egy rendelés több terméket tartalmazhat, és egy termék több rendelésben is szerepelhet. Ezt közvetlenül nem lehet megvalósítani — kell egy kapcsolótábla (más néven kötőtábla), amely két 1:N kapcsolatra bontja a feladatot.
rendeles_tetel kapcsolótáblával
oldjuk meg. Ide kerülhetnek a kapcsolat saját adatai is, pl. a megrendelt mennyiség.Ha azt látod, hogy „az egyik oldalon is több, a másikon is több”, az N:M kapcsolat → mindig kell egy kapcsolótábla. A kapcsolótábla elsődleges kulcsa gyakran a két idegen kulcs együttese (összetett kulcs).
Különleges eset, amikor egy tábla saját magával áll kapcsolatban: az idegen
kulcs ugyanannak a táblának az elsődleges kulcsára mutat. Ez nem egy negyedik számosság — az
önhivatkozó kapcsolat is 1:1, 1:N vagy N:M lehet. A webshopban a
kategoria tábla ilyen: a szulo_id
oszlopa egy másik kategória id-jára mutat. Egy
szülőkategóriához több alkategória tartozhat, de minden kategóriának
legfeljebb egy szülője van → ez egy önhivatkozó 1:N. A
főkategóriáknál (pl. „Elektronika”) a szulo_id értéke
NULL.
Az ilyen táblát később saját magával összekapcsolva kérdezzük majd le — ez lesz az önkapcsolás (self join) a 7c. témakörben.
Döntsd el mindegyik helyzetről, milyen számosságú kapcsolat!
Az ER-diagram (Entity–Relationship, egyed–kapcsolat diagram) egyetlen ábrán mutatja meg az összes táblát és a köztük lévő kapcsolatokat. Ezt fogjuk a teljes kurzuson végigvinni. Íme a webshopunk teljes sémája:
rendeles_tetel a rendelés és a termék
közötti N:M kapcsolat kapcsolótáblája.„Egy vásárlónak több rendelése lehet. Egy rendelés több tételt tartalmaz, minden tétel egy termékre mutat. Egy termék egy kategóriába tartozik, és egy kategóriának lehet szülő-kategóriája is.”
A normalizálás az a folyamat, amellyel a táblákat úgy alakítjuk át, hogy elkerüljük a redundanciát és a belőle fakadó anomáliákat. Nézzünk egy rosszul megtervezett, „mindent egy táblába” típusú megoldást, és javítsuk lépésről lépésre.
| rendeles_id | vasarlo_nev | vasarlo_email | termekek |
|---|---|---|---|
| 101 | Nagy Anna | nagy.anna@pelda.hu | egér, billentyűzet, monitor |
| 102 | Nagy Anna | nagy.anna@pelda.hu | webkamera |
Itt két baj van: a vásárló adatai ismétlődnek (ha Anna e-mailt vált, két helyen
kell javítani), és a termekek cella több értéket
tartalmaz, amit így nem lehet jól kezelni.
Szabály: minden cella egyetlen, oszthatatlan értéket tartalmaz, és nincsenek
ismétlődő oszlopcsoportok. A több értéket tartalmazó termekek cellát
külön sorokra bontjuk.
| rendeles_id | vasarlo_nev | vasarlo_email | termek |
|---|---|---|---|
| 101 | Nagy Anna | nagy.anna@pelda.hu | egér |
| 101 | Nagy Anna | nagy.anna@pelda.hu | billentyűzet |
| 101 | Nagy Anna | nagy.anna@pelda.hu | monitor |
Feltétel: a tábla már 1NF-ben van, és minden nem-kulcs oszlop a teljes kulcstól függ — nem csak a kulcs egy darabjától. Ez a szabály csak akkor „élesedik”, ha a kulcs több oszlopból áll (összetett kulcs); egyoszlopos kulcs mellett egy 1NF-es tábla automatikusan 2NF-ben is van.
Nézzük meg a fenti 1NF-es táblát! Egy sorát melyik oszlop azonosítja? A
rendeles_id önmagában kevés — a 101-es
rendeléshez három sor is tartozik. Egy sort csak a
(rendeles_id, termek) páros azonosít
egyértelműen: ez tehát összetett kulcs. Most kérdezzük meg minden nem-kulcs oszlopról:
a teljes párostól függ, vagy elég hozzá a kulcs egyik fele?
| Oszlop | Mitől függ valójában? | A teljes kulcstól függ? |
|---|---|---|
vasarlo_nev | csak a rendeles_id-tól | Nem — részleges függés |
vasarlo_email | csak a rendeles_id-tól | Nem — részleges függés |
A vásárló neve és e-mailje csak attól függ, melyik rendelésről van szó —
az teljesen mindegy, hogy a sorban egér vagy monitor szerepel. Ez a részleges
függés, és pontosan emiatt ismétlődik Anna neve három soron át: a kulcs „termék”
feléhez ezeknek az adatoknak semmi közük. A 2NF megoldása: a csak a rendeléstől függő
adatokat kiemeljük a saját táblájukba — így születik meg a külön
rendeles tábla (a vásárló adataival, amelyek onnan a külön
vasarlo táblába kerülnek), a termékes sorokban pedig csak a
rendelésre mutató hivatkozás marad.
Feltétel: már 2NF-ben van, és nincs tranzitív függés — vagyis nem-kulcs
oszlop nem függhet másik nem-kulcs oszloptól. Például ha tárolnánk az
iranyitoszam mellett a varos nevét is,
az tranzitív függés lenne (a város az irányítószámtól függ, nem a sor kulcsától).
| Normálforma | Követelmény |
|---|---|
| 1NF | Minden cella atomi (egy érték), nincs ismétlődő oszlopcsoport. |
| 2NF | 1NF + nincs részleges függés (minden a teljes kulcstól függ). |
| 3NF | 2NF + nincs tranzitív függés (nem-kulcs nem függ nem-kulcstól). |
A sor a 3NF-fel nem ér véget — a szigorúbb formákkal azonban jellemzően már csak elméletben vagy speciális rendszerekben találkozni:
A legtöbb üzleti adatbázis számára a 3NF elegendő. Egyszerű emlékeztető: „minden nem-kulcs oszlop a kulcstól, a teljes kulcstól, és semmi mástól, mint a kulcstól függjön.”
Minden oszlophoz adattípust rendelünk, ami meghatározza, milyen értéket tárolhat. A jó típusválasztás helytakarékos és megelőzi a hibás adatokat. A leggyakoribb MySQL-típusok:
| Típus és változatai | Mire való | Tárolás | Webshop példa |
|---|---|---|---|
TINYINT · SMALLINT · MEDIUMINT · INT · BIGINT |
Egész számok — a méret dönti el a tartományt: ±127 · ±32 767 · ±8,4 millió · ±2,1 milliárd · ±9,2 trillió | 1 · 2 · 3 · 4 · 8 bájt | id, keszlet |
DECIMAL(p,s) |
Pontos tizedes (pénz!) — p számjegy, ebből s tizedes | változó; a DECIMAL(10,2) ≈ 5 bájt |
ar |
FLOAT · DOUBLE |
Közelítő lebegőpontos szám — pénzhez tilos (lásd lent) | 4 · 8 bájt | mérési adat, arány |
CHAR(n) · VARCHAR(n) |
Fix, illetve változó hosszú szöveg (max. n karakter) | fixen n karakter · a tényleges hossz + 1–2 bájt | nev, email (VARCHAR) |
TINYTEXT · TEXT · MEDIUMTEXT · LONGTEXT |
Hosszú szöveg — legfeljebb 255 bájt · 64 KB · 16 MB · 4 GB | a szöveg hossza + 1 · 2 · 3 · 4 bájt | leiras (TEXT) |
DATE · TIME · DATETIME · TIMESTAMP · YEAR |
Dátum · idő · dátum+idő · időbélyeg (1970–2038) · évszám | 3 · 3 · 5 · 4 · 1 bájt | rendeles_datuma (DATETIME) |
BOOLEAN |
Igaz/hamis (valójában TINYINT(1)) |
1 bájt | aktiv |
ENUM(...) |
Felsorolt értékek egyike | 1–2 bájt | statusz |
A szöveges típusok bájtmérete a karakterkódolástól is függ: utf8mb4-ben
egy karakter 1–4 bájtot foglalhat, így a CHAR(10) akár 40 bájt
is lehet. A számoknál az UNSIGNED (előjel nélküli) változat
ugyanannyi bájtból a pozitív tartomány dupláját adja — pl.
TINYINT UNSIGNED: 0–255. Az id-khoz és készletekhez ezért
gyakran UNSIGNED egész típust választunk.
A FLOAT és DOUBLE közelítő értékek —
kerekítési hibákat okoznak. Az árakhoz mindig DECIMAL-t használj,
ami pontos.
Foglaljuk össze a folyamatot, amit minden új adatbázisnál érdemes követni:
| # | Lépés | Eredmény |
|---|---|---|
| 1 | Követelmények összegyűjtése | Mit kell tárolni? (vásárlók, termékek, rendelések…) |
| 2 | Entitások azonosítása | A leendő táblák listája |
| 3 | Attribútumok meghatározása | Oszlopok és adattípusaik |
| 4 | Kulcsok kijelölése | Elsődleges kulcs minden táblához |
| 5 | Kapcsolatok feltárása | 1:1, 1:N, N:M → idegen kulcsok, kapcsolótáblák |
| 6 | Normalizálás | Redundancia megszüntetése (3NF-ig) |
| 7 | ER-diagram megrajzolása | A teljes séma egy ábrán |
Ha a terv kész, jöhet az eszköz: a következő témakörben a MySQL Workbench
segítségével rajzoljuk meg vizuálisan ezt a sémát, majd a
CREATE TABLE utasításokkal életre is keltjük.
⭐ alap · ⭐⭐ haladó · ⭐⭐⭐ kihívás — papíron vagy füzetben dolgozz, a megoldásokat az órán beszéljük meg.
Egészítsd ki: a tervezésnél a „dolgokat”, amikről adatot tárolunk, ____________-nak nevezzük, és belőlük ____________ lesznek az adatbázisban.
Mondd ki (vagy írd le) a 3NF egymondatos „aranyszabályát”!
Az alábbi séma-részlet alapján döntsd el, milyen kapcsolat van a két tábla között, és melyik oszlop az idegen kulcs!
kategoria(id PK, nev) termek(id PK, nev, ar, kategoria_id FK -> kategoria.id)
Nevezz meg két okot, amiért nem tárolunk pénzösszeget FLOAT
típusban!
A webshopot bővítjük termékértékelésekkel: egy vásárló több terméket is értékelhet, egy terméket több vásárló is. Az értékelésnek van csillagszáma (1–5) és szövege. Tervezd meg a szükséges táblá(ka)t kulcsokkal!
Egy vásárlónak lehet több címe (szállítási, számlázási). Tervezd meg a
cim táblát úgy, hogy egyértelmű legyen, melyik vásárlóé és
milyen típusú a cím!
Az alábbi tábla nincs 1NF-ben. Miért, és hogyan javítanád?
rendeles(id, vasarlo, termekek) 101 | Nagy Anna | egér, billentyűzet, monitor
Mely táblákra bontanád szét az alábbi, redundáns táblát úgy, hogy 3NF-ben legyen? Sorold fel a táblákat a kulcsaikkal!
rendeles_minden(rendeles_id, vasarlo_nev, vasarlo_email,
termek_nev, termek_ar, mennyiseg)
A kategoria táblában al-kategóriákat is szeretnénk
(pl. „Elektronika” → „Számítógép” → „Egér”). Hogyan oldanád meg egyetlen
táblával? Rajzold le a tábla szerkezetét! (Segítség: nézd meg az ER-diagramon
a kategoria tábla nyilát.)
Minden kérdésnél egy válasz helyes. A végén pontszámot kapsz.
| Fogalom | Jelentés |
|---|---|
| adatbázis | rendezetten tárolt, gyorsan kereshető adatok gyűjteménye |
| DBMS | adatbázis-kezelő rendszer (pl. MySQL) — rajta keresztül kezeljük az adatokat |
| NoSQL | a nem relációs adatbáziskezelők gyűjtőneve: dokumentum-, kulcs–érték, oszlopcsaládos és gráf-adatbázisok |
| SQL | a relációs adatbázisok szabványos lekérdezőnyelve |
| tábla · mező · rekord | az adattárolás egysége · egy oszlopa (tulajdonság) · egy sora (egy egyed) |
| elsődleges kulcs (PK) | minden sort egyedien azonosító, nem NULL oszlop |
| idegen kulcs (FK) | másik tábla elsődleges kulcsára hivatkozó oszlop |
| kapcsolótábla | két idegen kulccsal N:M kapcsolatot két 1:N-re bontó tábla |
| önhivatkozó (rekurzív) kapcsolat | a tábla saját magával áll kapcsolatban — az FK a saját PK-jára mutat (pl. kategoria.szulo_id) |
| redundancia | ugyanazon adat többszöri tárolása — frissítési anomáliák forrása |
| normalizálás | a redundancia megszüntetése lépésekben (1NF → 2NF → 3NF; szigorúbb formák: BCNF, 4NF, 5NF, 6NF) |
| ER-diagram | a táblákat és kapcsolataikat mutató egyed–kapcsolat ábra |
DECIMAL.A teljes folyamatot a webshop ER-diagramja köti össze — ezt visszük tovább az egész kurzuson.