ugrás a tartalomhoz

Kapcsolótáblák és SQL

foxmulder · 2008. Május. 21. (Sze), 03.52
Sziasztok!

Elnézést, ha triviális a kérdés, de nem találom a megoldást. Adatmodell szinten, ha létezik egy Személy és egy Zenei stílus egyedtípusom, akkor azt a kapcsolatot, hogy mely személyek mely zenei stílusokat kedvelnek, a két egyedtípus közötti M:N kapcsolattal, vagyis kapcsolótáblával fejezhetek ki. Vagyis van egy Személyek táblám: (id, név, egyéb adatok), egy Zenei stílusok táblám (id, név, egyéb adatok) és egy Személyek_Zenei stílusok kapcsolótáblám (id, személy_id, zenei_stílus_id, esetleges további adatok). Ugyanakkor SQL szinten sok dokumentációban olvashatok JOIN-okról, beágyazott SELECT-ekről, többtáblás lekérdezésekről stb. Csak arra nem találok példát, hogy hogyan kérdezhetném le a fenti példát felhasználva például azt, hogy kik milyen zenei stílusokat kedvelnek. Összefoglalhatnánk, hogy az adatmodellezésben használható 1:N és M:N, valamint rekurzív kapcsolattípusok hogyan valósulnak meg SQL szinten?
 
1

Mit szeretnél?

janoszen · 2008. Május. 21. (Sze), 07.31
Kérdés, hogy milyen formában szeretnéd megkapni. Ha minden személy összes kedvelt zenei stílusát szeretnéd kapni, akkor két left join a személyek felől:

SELECT * FROM szemelyek LEFT JOIN szemelyek_zenei ON szemelyek.id=szemelyek_zenei.szemely LEFT JOIN zeneistilus ON zeneistilus.id=szemelyek_zenei.zenei
Ha egy személyét, akkor értelemszerűen még egy where feltétel. Egyébként a group függvények sokat segítenek.
2

Ajjaj...

foxmulder · 2008. Május. 21. (Sze), 12.33
Mi is történik itt?

Az első LEFT JOIN által szolgáltatott találati halmaz szerepel a második LEFT JOIN bal oldalán táblaként?
9

Válasz

foxmulder · 2008. Május. 26. (H), 17.14
...hát ha nem is táblaként, de igen. Valahogy így:
SELECT *
FROM (szemelyek LEFT JOIN szemelyek_zenei ON szemelyek.id=szemelyek_zenei.szemely)
LEFT JOIN zeneistilus ON zeneistilus.id=szemelyek_zenei.zenei;

Az MS Access esetén szükséges kitenni a zárójeleket, a többi adatbázskezelőnél nem szükséges, de nem is hiba.
3

Ha a kapcsolat rekurzív

foxmulder · 2008. Május. 21. (Sze), 12.47
Másik kérdésem a rekurzív M:N kapcsolatra vonatkozik, azaz például személyek kedvelnek személyeket. Így próbáltam (legyen a kapcsolótábla neve mondjuk baratok(id, szemely_id, barat_id)):

SELECT nev FROM szemelyek LEFT JOIN baratok ON szemelyek.id=baratok.szemely_id LEFT JOIN szemelyek ON szemelyek.id=baratok.barat_id;
Erre a MySQL hibaüzenetet ad: Not unique table/alias 'szemelyek'
Buta vagyok, nem értem...
4

Hibaüzenet

vbence · 2008. Május. 21. (Sze), 13.09
A hibaüzenet szerint nem emgértelmű (not inique, ugybár) a "szemelyek" hivatkozásod. Ilyenkor, amikor kétszer szerepel egy tábla egy lekérdezésben ASLIAS használatos, tehát az esetedben:
SELECT nev FROM szemelyek AS a LEFT JOIN baratok ON a.id=baratok.szemely_id LEFT JOIN szemelyek AS b ON b.id=baratok.barat_id;
Szerintem kérj kölcsön egy SQL könyvet vlakitől, alapozásnak...
5

Megfogadtam a tanácsot ;)

foxmulder · 2008. Május. 23. (P), 23.11
... és beszereztem pár könyvet, de lehet, hogy rosszul választottam, mert a következő kérdésre nem kaptam belőlük választ:

Ha a baratok táblában a szemely_id Klári azonosítóját, míg a barat_id Béla azonosítóját tartalmazza és futtatom a következő lekérdezést (nagyjából vbence javaslatával azonos):
 SELECT a.nev as szemely, b.nev as barat FROM szemelyek AS a LEFT JOIN baratok ON a.id=baratok.szemely_id LEFT JOIN szemelyek AS b ON b.id=baratok.barat_id;
és megspékelem még egy záradékkal:
WHERE a.nev='Klári'
akkor az eredmény: Béla. Ha az utolsó záradék
WHERE a.nev='Béla'
akkor az eredmény: NULL, ami nem felel meg annak a ténynek, hogy két személy baráti kapcsolata szimmetrikus, vagyis, hogy ha Klári barátja Béla, akkor egyben Béla barátja Klári is. Naná, hogy nem felel meg, hiszen a kapcsolótáblában sem szimmetrikus a viszony (szemely_id <-> barat_id). Hogyan lehetne ezt szimmetrikussá tenni
a.) vagy a lekérdezésben
b.) vagy a táblaszerkezetekben?

Vagy PHP-ből kell megoldani, hogy amikor mentjük az adatbázisba a Klári barátja Béla viszonyt, akkor egyidejűleg rögzíteni kell a Béla barátja Klári viszonyt is?
6

Barátság modellezése

vbence · 2008. Május. 24. (Szo), 21.58
A lekérdezésben gond nélkül meg lehetne valósítani, és abban is van logika, hogy egy barátságot egyetlen rekord jelenítsen meg (és ne kettő), viszont a modell nem tükrözi a barátság kölcsönös jellegét, így hosszú távon csak fölöslegesen bonyolítanánk a lekérdezéseket...

Amit ajánlok:

kapcsolat (id, kezdemenyezo_szemely, targy_szemely, ido, status)
ismeretseg (id, alany_szemely, targy_szemely, csoport)

Egy baráságot 3 rekord testesítene meg: a kapcsolat táblában egy, ami tartalmazhatja a "bejelölés" körülményeit, valamint, hogy visszaigazolt baráságról van-e szó. Az ismeretseg táblában pedig 2 reord lenne: az egyik és másik szereplő nézetéből. Így lehetőségünk van hogy a tagok csoportokba sorolják barátaikat.

A törlést lehet triggerrel megoldnai, vagy kézzel. Írhatóak teszt-lekérdezések is, amik ellenőrizni tudják az adatok konzisztenicáját.
7

Inkább általánosítsunk

foxmulder · 2008. Május. 26. (H), 15.19
Két személy baráti kapcsolatára inkább absztrakt módon volnék kíváncsi (főleg a post-od első mondata érdekelne kifejtve :) ). Vegyük például a zöldségeket: bizonyos zöldségek kedvelik más zöldségek közelségét. Ebben az esetben pl. nincs kezdeményező fél, nincs status (legalábbis nem abban az értelemben) a fő probléma viszont ugyanez. Egyenrangú (kétoldalú) rekurzív M:N kapcsolat. És persze mi a lekérdezés (ill. előtte: mi az adatszerkezet)?

Mindamellett gyanús nekem az ajánlatodban a kezdemenyezo_szemely-targy_szemely, illetve alany_szemely-targy_szemely pár ismétlődése. Merthogy a kezdeményező=alany (csak a kapcsolat táblában kicsit speciális az alany szerepe). Nem? Vagyis miért nem egy tábla az a kettő?

És ahogy írod:
Az ismeretseg táblában pedig 2 rekord lenne: az egyik és másik szereplő nézetéből.

aminek személyek esetén lehet is értelme, hiszen nem mindegy ki a meghívó (bejelölő) fél és ki a másik. Már logikai szinten is különbözik a két fél. Ám, ha nem érdekel minket a különbség? Úgy tűnik mindenképp kényszerítve vagyunk a megkülönböztetésre: ismeretseg (id, egyik_fél, másik_fél, ...), hiszen egy táblában nem lehet két mező neve azonos. Hogyan lehet ezt a kényszert elfedni a lekérdezéssel?

UI.: Két dolog jutott eszembe:
Egyrészt, Halassy B. azt írja az adatmodellezésről szóló könyvében: valójában (a relációs modell szintjén) nincs egyenrangú M:N kapcsolat, hiszen egy M:N kapcsolatot két hierarchikus 1:N kapcsolattal valósítunk meg (egyik_fél:kapcsolótábla, másik_fél:kapcsolótábla. A rekurzív kapcsolat speciális, mert egyik_fél=másik_fél, de ez nem érinti a lényeget).
Másrészt, a való életben talán tényleg nincs teljesen szimmetrikus viszony a két fél között. Mint ahogy a személyek esetén mindenképp érdekelni fog minket, hogy ki a meghívó, zöldségek esetén talán azt a tényt szeretnénk jelezni az adatbázisban, hogy egy jó szomszédi kapcsolatban melyik fél védi a másikat annak kártevőitől (ritka, hogy ez teljesen kölcsönös).
8

Egyenrangú kapcsolat vs. aspektusok tárolása

vbence · 2008. Május. 26. (H), 16.51
A két táblás (3 rekordos) megoldásnál az egyes mezőkkel igazán csak azt akartam érzékeltetni, hogy a két táblában a kapcsolat két különböző aspektusa szerepel. A kapcsolat táblában magáról a kapcsolatról tárolhatunk információkat (a példában a kapcsolat létrejöttének körülményeit). A kapcsolat táblában egy kapcsolat egy rekord, tehát ha magával a kapcsolattal foglalkozunk (pl kívácsiak vagyunka kapcsolatok számára), akkor ezt a táblát hazsnáljuk. Az ismeretseg táblában a kapcsolat más dimenzióban jelenik meg. Növényeknél el tudom képzelni hogy ami az egyiknek előnyös, az a másiknak már nem annyira.

Például: az uborkának mindenképpen kedvezni akarunk még azon az áron is, hogy a mellé ültetett másodlagos zöldésgnek nem feltétlenül előnyös a kapcsolat. Mondjuk, hogy az uborka mission-critical, a második zöldség pedig csak egy addicionális termék. Másik esetben a föld maximális kihasználására optimalizálunk, és azt keressük, hogy mindkét növénynek jó legyen, olyankor a kölcsönösen előnyös kapcsolatokat keressük...


Az első felére válaszolva: egy teljesen egyenrangú kapcsolatot modellezhetünk egyetlen táblával is az alábbi módon:
resztvevo (id, kapcsolat_id, szemely_id)

Itt csoportokat képzünk. Az azonos kapcsolat_id fogja összekapcsolni az összetartozó személyeket. Az id mező csak kényelmi célokat szolgál, elhegyható: lehet a (kapcsolat_id, szemely_id) egy összetett kulcs. A kapcsolat_id annyiban számít, hogy egyértelmű legyen az egyes kapcsoaltokra (barátságokra). Belátható, hogy nem szükséges külön kapcsolat táblának lennie, csak gondoskodnunk kell róla, hogy egyedi azonosítókat képezzünk az egyes barátságok felvitelénél.

Mindazonáltal a lekérdezések fogják meghatározni az optimális adatszerkezetet. Nem mindig a legnormalizáltabb a legjobb. Legyen ez akár cache-elés (vagyis redundancia) a gyorsabb kiszolgálás érdekében...
10

Ez tetszik :)

foxmulder · 2008. Május. 26. (H), 17.53
resztvevo (id, kapcsolat_id, szemely_id)


Nagyon jó! Mellesleg egy kapcsolathoz tartozhat kettőnél több személy is (a zeller szeretheti a kelkáposzta, a karalábé, a brokkoli és a paradicsom társaságát is). Hát persze: nem a Személyek egyed van M:N kapcsolatban önmagával, hanem a Személyek és a Kapcsolatok egyed (és ha az utóbbiról nem akarunk nyilvántartani semmit, akkor nem is kell mint önálló tábla).

Köszi!