5.1 Bevezetés
Az internet tele van dogmatikai előírásokkal arról, hogyan kell kulcsokat választani és használni a relációs adatbázisokban. Néha a viták holivarokká fajulnak: természetes vagy mesterséges kulcsokat kell használni? Egész számok vagy UUID-k automatikus növelése?
Miután elolvastam hatvannégy cikket, átlapoztam öt könyv szakaszát, és rengeteg kérdést tettem fel az IRC-ről és a StackOverflow-ról, úgy tűnik, én (Joe "begriffs" Nelson, az eredeti cikk szerzője) összeraktam a puzzle darabjait, és most kibékítheti az ellenfeleket. Sok kulcsfontosságú vita valójában valaki más nézőpontjának félreértéséből fakad.
Szedjük szét a problémát, és a végén rakjuk vissza. Először is tegyük fel a kérdést – mi az a „kulcs”?
Egy pillanatra felejtsük el az elsődleges kulcsokat, minket egy általánosabb ötlet érdekel. A kulcs olyan oszlop (oszlop) vagy oszlopok, amelyeknek nincs ismétlődő értéke a sorokban . Ezenkívül az oszlopoknak redukálhatatlanul egyedieknek kell lenniük, azaz az oszlopok egyik részhalmaza sem rendelkezik ezzel az egyediséggel.
De először egy kis elmélet:
elsődleges kulcs
Elsődleges kulcsközvetlenül a táblázat sorainak azonosítására szolgál. Meg kell felelnie a következő korlátozásoknak:
- Az elsődleges kulcsnak mindig egyedinek kell lennie .
- Mindig jelen kell lennie a táblázatban, és értékkel kell rendelkeznie.
- Nem szabad gyakran változtatni az értékét. Ideális esetben egyáltalán nem szabad megváltoztatnia az értéket .
Az elsődleges kulcs általában egy táblázat egyetlen oszlopát jelöli, de lehet több oszlopból álló összetett kulcs is.
Kompozit kulcs
Egyéni kulcs- attribútumok (oszlopok) kombinációja, amelyek egyedileg azonosítják az egyes táblázatsorokat. Lehet minden oszlop, több és egy is. Ebben az esetben az attribútumok értékeit tartalmazó sorokat nem szabad megismételni.
Potenciális kulcs
jelölt kulcs- a reláció (tábla) minimális összetett kulcsát képviseli, azaz olyan attribútumkészletet, amely számos feltételnek eleget tesz:
- Irreducibility : Nem redukálható, az attribútumok lehető legkisebb halmazát tartalmazza.
- Egyediség : A sorváltástól függetlenül egyedi értékekkel kell rendelkeznie.
- Érték jelenléte : Nem lehet null értéke, azaz értéke kell.
5.2 Az elsődleges kulcsok különös esete
Amit az előző részben csak „kulcsoknak” neveztünk, azokat általában „jelölt kulcsoknak” nevezzük. A „jelölt” kifejezés azt jelenti, hogy minden ilyen kulcs verseng az „elsődleges kulcs” (elsődleges kulcs) megtisztelő szerepéért, a többihez pedig „alternatív kulcsok” (alternatív kulcsok) vannak hozzárendelve.
Eltartott egy ideig, amíg az SQL-megvalósítások túljutottak a kulcsok és a relációs modell közötti eltérésen, és a legkorábbi adatbázisok az elsődleges kulcs alacsony szintű koncepciójára irányultak. Az ilyen adatbázisokban elsődleges kulcsokra volt szükség egy sor fizikai helyének azonosításához a szekvenciális adathordozón. Joe Celko így magyarázza:
A "kulcs" kifejezés egy fájlrendezési kulcsot jelentett, amely a szekvenciális fájlrendszeren végzett feldolgozási műveletek végrehajtásához szükséges. Egy sor lyukkártyát egy és egyetlen sorrendben olvastak el; lehetetlen volt visszamenni. A korai szalagos meghajtók ugyanezt a viselkedést utánozták, és nem tették lehetővé a kétirányú hozzáférést. Vagyis az eredeti Sybase SQL Servernek a táblázat elejére kell „visszatekernie” az előző sor olvasásához.
A modern SQL-ben nem kell az információk fizikai megjelenítésére koncentrálni, a táblák modellezik a kapcsolatokat, és a sorok belső sorrendje egyáltalán nem fontos. Az SQL szerver azonban még most is alapértelmezés szerint fürtözött indexet hoz létre az elsődleges kulcsokhoz, és a régi hagyomány szerint fizikailag rendezi a sorok sorrendjét.
A legtöbb adatbázisban az elsődleges kulcsok a múlté, és alig jelentenek többet, mint tükrözés vagy fizikai hely. Például egy PostgreSQL táblában az elsődleges kulcs deklarálása automatikusan kényszerít egy kényszert, NOT NULL
és meghatároz egy alapértelmezett idegen kulcsot. Ezenkívül az elsődleges kulcsok az operátor preferált oszlopai JOIN
.
Az elsődleges kulcs nem írja felül más kulcsok deklarálásának lehetőségét. Ugyanakkor, ha nincs kulcs elsődlegesként hozzárendelve, akkor a táblázat továbbra is jól fog működni. A villám mindenesetre nem fog beléd csapni.
5.3 Természetes kulcsok keresése
A fentebb tárgyalt kulcsokat "természetesnek" nevezzük, mert a modellezett objektum olyan tulajdonságai, amelyek önmagukban is érdekesek, még akkor is, ha senki nem akar kulcsot csinálni belőlük.
Az első dolog, amit észben kell tartani, amikor egy táblázatot megvizsgálunk a lehetséges természetes kulcsok keresésére, az az, hogy ne legyünk túl okosak. A StackExchange sqlvogel felhasználója a következő tanácsokat adja:
Vannak, akiknek nehézséget okoz a „természetes” kulcs kiválasztása, mert olyan hipotetikus helyzeteket találnak ki, amelyekben egy adott kulcs nem egyedi. Nem értik a feladat értelmét. A kulcs jelentése annak a szabálynak a meghatározása, amely szerint az attribútumoknak bármikor egyedinek kell lenniük és mindig egyediek is lesznek egy adott táblában. A táblázat egy konkrét és jól érthető kontextusban tartalmaz adatokat (a "témakörben" vagy "beszédterületen"), és az egyetlen értelme a korlátozásnak az adott területen való alkalmazása.
A gyakorlat azt mutatja, hogy kulcsfontosságú megszorítást kell bevezetni, ha az oszlop egyedi a rendelkezésre álló értékekkel, és az is marad a valószínű forgatókönyvekben. És ha szükséges, a korlátozás eltávolítható (ha ez zavarja, akkor az alábbiakban a kulcsstabilitásról fogunk beszélni.)
Például egy hobbiklub-tagok adatbázisának két oszlopa lehet egyedisége - first_name
, last_name
. Kis mennyiségű adat esetén a duplikációk nem valószínűek, és mielőtt valódi konfliktus keletkezne, teljesen ésszerű egy ilyen kulcs használata.
Az adatbázis növekedésével és az információ mennyiségének növekedésével a természetes kulcs kiválasztása nehezebbé válhat. Az általunk tárolt adatok a külső valóság leegyszerűsítése, és nem tartalmaznak olyan szempontokat, amelyek megkülönböztetik a világban lévő objektumokat, például azok koordinátáit, amelyek idővel változnak. Ha egy tárgyon hiányzik a kód, hogyan lehet megkülönböztetni két doboz italt vagy két doboz zabpelyhet a térbeli elrendezésüktől, illetve a súly vagy a csomagolás kismértékű eltérésétől?
Ez az oka annak, hogy a szabványügyi testületek megkülönböztető jelzéseket hoznak létre és alkalmaznak a termékeken. A járműveket járműazonosító számmal (VIN) látják el , a könyveket ISBN- számmal nyomtatják , az élelmiszer-csomagolásokon pedig UPC-t . Kifogásolhatja, hogy ezek a számok nem tűnnek természetesnek. Akkor miért nevezem őket természetes kulcsoknak?
Az adatbázisban szereplő egyedi tulajdonságok természetessége vagy mesterségessége a külvilághoz viszonyított. Az a kulcs, amely egy szabványügyi testületben vagy kormányhivatalban létrehozásakor mesterséges volt, természetessé válik számunkra, mert az egész világon szabvány lesz és/vagy tárgyakra nyomtatják.
Számos ipari, állami és nemzetközi szabvány létezik számos témakörben, beleértve a pénznemeket, nyelveket, pénzügyi eszközöket, vegyi anyagokat és orvosi diagnózisokat. Íme néhány olyan érték, amelyeket gyakran természetes kulcsként használnak:
- ISO 3166 országkódok
- ISO 639 nyelvi kódok
- Valutakódok az ISO 4217 szerint
- Részvényszimbólumok ISIN
- UPC/EAN, VIN, GTIN, ISBN
- bejelentkezési nevek
- email címek
- szobaszámok
- hálózati mac cím
- szélesség, hosszúság a Föld felszínén lévő pontokhoz
Javaslom a kulcsok deklarálását, amikor csak lehetséges és ésszerű, esetleg több kulcsot is táblánként. De ne feledje, hogy a fentiek mindegyike tartalmazhat kivételeket.
- Nem mindenkinek van e-mail címe, bár ez bizonyos adatbázis-feltételek mellett elfogadható lehet. Ezenkívül az emberek időről időre megváltoztatják az e-mail címüket. (A kulcsstabilitásról később.)
- Az ISIN részvényszimbólumok időről időre változnak, például a GOOG és a GOOGL szimbólumok nem írják le pontosan a vállalat Google-ról Alphabetre való átszervezését. Néha zavarok adódhatnak, mint például a TWTR és a TWTRQ esetében, egyes befektetők tévedésből az utóbbit vásárolták meg a Twitter IPO során.
- A társadalombiztosítási számokat csak amerikai állampolgárok használhatják, adatvédelmi korlátozások vonatkoznak rájuk, és haláluk után újra felhasználják. Ráadásul az iratok ellopása után az emberek új számokat kaphatnak. Végül ugyanaz a szám azonosíthat személyt és jövedelemadó-azonosítót is.
- Az irányítószámok rossz választás a városok számára. Néhány városnak van közös indexe, vagy fordítva, egy városban több index is található.
5.4 Mesterséges kulcsok
Tekintettel arra, hogy a kulcs egy olyan oszlop, amely minden sorban egyedi értékekkel rendelkezik, létrehozásának egyik módja a csalás - minden sorba fiktív egyedi értékeket írhat. Ezek mesterséges kulcsok: kitalált kód, amellyel adatokra vagy objektumokra hivatkoznak.
Nagyon fontos, hogy a kód magából az adatbázisból álljon elő, és az adatbázis felhasználóin kívül senki számára ismeretlen legyen. Ez különbözteti meg a mesterséges billentyűket a szabványosított természetes kulcsoktól.
Míg a természetes kulcsoknak megvan az az előnyük, hogy megvédenek a táblázatban lévő ismétlődő vagy inkonzisztens sorok ellen, a mesterséges kulcsok hasznosak, mert megkönnyítik az emberek vagy más rendszerek számára a sorra való hivatkozást, valamint felgyorsítják a keresést és az összekapcsolást, mivel nem használják karakterlánc (vagy több oszlopos) összehasonlítások.
Helyettesítők
A mesterséges kulcsokat horgonyként használják – függetlenül attól, hogy a szabályok és az oszlopok hogyan változnak, egy sor mindig azonos módon azonosítható. Az erre a célra használt mesterséges kulcsot "pótkulcsnak" nevezik, és különös figyelmet igényel. Az alábbiakban a helyettesítőket vesszük figyelembe.
A nem helyettesítő mesterséges kulcsok hasznosak az adatbázison kívüli sorok hivatkozására. A mesterséges kulcs röviden azonosít egy adatot vagy objektumot: megadható URL-ként, számlához csatolható, telefonon bediktált, bankból beszerezhető, vagy rendszámra nyomtatható. (Az autó rendszáma nálunk természetes kulcs, de a kormány mesterséges kulcsnak tervezte.)
A szintetikus kulcsokat a lehetséges átviteli módok figyelembevételével kell kiválasztani, hogy minimálisra csökkentsék az elírásokat és hibákat. Megjegyzendő, hogy a billentyű kimondható, kinyomtatható, SMS-ben elküldhető, kézírással olvasható, a billentyűzetről begépelhető és URL-be ágyazható. Ezenkívül egyes mesterséges kulcsok, például a hitelkártyaszámok ellenőrző összeget tartalmaznak, hogy bizonyos hibák esetén legalább felismerhetők legyenek.
Példák:
- Az egyesült államokbeli rendszámok esetében szabályok vonatkoznak a kétértelmű karakterek, például az O és a 0 használatára.
- A kórházaknak és a gyógyszertáraknak különösen óvatosnak kell lenniük, tekintettel az orvosok kézírására.
- Küldsz SMS-ben visszaigazoló kódot? Ne lépje túl a GSM 03.38 karakterkészletet.
- A Base64-től eltérően, amely tetszőleges bájtadatokat kódol, a Base32 korlátozott karakterkészletet használ, amelyet az emberek kényelmesen használhatnak és kezelhetnek régebbi számítógépes rendszereken.
- A proquints olvasható, írható és kiejthető azonosítók. Ezek egyértelmûen értett mássalhangzók és magánhangzók PRO-mondható QUINT-upletjei.
Ne feledje, hogy amint bemutatja mesterséges kulcsát a világnak, az emberek furcsa módon különös figyelmet fognak fordítani rá. Csak nézze meg a "tolvajok" rendszámtábláit vagy a kiejthető azonosítók létrehozására szolgáló rendszert, amely a hírhedt automata átokgenerátorrá vált.
Még ha a számbillentyűkre korlátozzuk is magunkat, vannak olyan tabuk, mint a tizenharmadik emelet. Míg a proquintokban nagyobb az információsűrűség kimondott szótagonként, a számok sok szempontból is jók: URL-ekben, gombos billentyűzetekben és kézzel írt jegyzetekben, mindaddig, amíg a címzett tudja, hogy a kulcs csak számok.
Ne feledje azonban, hogy ne használjon szekvenciális sorrendet a nyilvános numerikus kulcsokban, mivel ez lehetővé teszi az erőforrások (/videos/1.mpeg, /videos/2.mpeg és így tovább) közötti turkálást, valamint a számokkal kapcsolatos információk kiszivárogtatását. adat. Helyezzen egy Feistel-hálót egy számsorozatra, és őrizze meg az egyediséget, miközben elrejti a számok sorrendjét.
Az egyetlen érv a további kulcsok deklarálása ellen az, hogy minden új újabb egyedi indexet hoz magával, és növeli a táblába írás költségeit. Természetesen ez attól függ, mennyire fontos Önnek az adatok helyessége, de valószínűleg a kulcsokat továbbra is deklarálni kell.
Érdemes több mesterséges kulcsot is deklarálni, ha van ilyen. Például egy szervezetnek vannak állásra jelöltjei (Jelentkezők) és alkalmazottai (Employees). Valaha minden alkalmazott jelölt volt, és a jelöltekre a saját azonosítójukkal hivatkozik, amely egyben az alkalmazott kulcsa is. Egy másik példa, beállíthatja az alkalmazotti azonosítót és a bejelentkezési nevet két kulcsként az Alkalmazottak oldalon.
5.5 Helyettesítő kulcsok
Mint már említettük, a mesterséges kulcsok egy fontos típusát "helyettesítő kulcsnak" nevezik. Nem kell tömörnek és átadhatónak lennie, mint más mesterséges kulcsoknak, hanem belső címkeként használják, amely mindig azonosítja a karakterláncot. SQL-ben használják, de az alkalmazás nem fér hozzá kifejezetten.
Ha ismeri a PostgreSQL rendszeroszlopait, akkor a helyettesítőket szinte adatbázis-megvalósítási paraméterként képzelheti el (mint például a ctid), amely azonban soha nem változik. A helyettesítő érték soronként egyszer kerül kiválasztásra, és azután nem változik.
A helyettesítő kulcsok kiválóak idegen kulcsként, és lépcsőzetes kényszereket kell megadni ON UPDATE RESTRICT
, hogy megfeleljenek a helyettesítő kulcs megváltoztathatatlanságának.
Másrészt a nyilvánosan megosztott kulcsokhoz tartozó idegen kulcsokat ON UPDATE CASCADE
a maximális rugalmasság érdekében jellel kell megjelölni. A lépcsőzetes frissítés ugyanazon az elkülönítési szinten fut, mint a környező tranzakció, ezért ne aggódjon a párhuzamossági problémák miatt – az adatbázis rendben lesz, ha szigorú elkülönítési szintet választ.
Ne tegye „természetessé” a pótkulcsokat. Ha egyszer megmutatja a helyettesítő kulcs értékét a végfelhasználóknak, vagy ami még rosszabb, hagyja, hogy ezzel az értékkel dolgozzanak (különösen egy kereséssel), akkor gyakorlatilag értéket ad a kulcsnak. Ekkor az Ön adatbázisából megjelenített kulcs természetes kulcsmá válhat valaki más adatbázisában.
A külső rendszerek más, kifejezetten átvitelre tervezett mesterséges kulcsok használatára kényszerítése lehetővé teszi számunkra, hogy ezeket a kulcsokat szükség szerint módosítsuk a változó igények kielégítése érdekében, miközben a helyettesítőkkel megőrizzük a belső hivatkozási integritást.
Auto-növekmény INT/BIGINT
A helyettesítő kulcsok leggyakoribb használata az automatikusan növekvő "bigserial" oszlop , más néven IDENTITY . (Valójában a PostgreSQL 10 most már támogatja az IDENTITY konstrukciót, akárcsak az Oracle, lásd a TÁBLÁZAT LÉTREHOZÁSA.)
Úgy gondolom azonban, hogy az automatikusan növekvő egész szám rossz választás helyettesítő kulcsokhoz. Ez a vélemény nem népszerű, ezért hadd magyarázzam el.
A soros kulcsok hátrányai:
- Ha minden sorozat 1-től kezdődik és fokozatosan növekszik, akkor a különböző táblák sorai ugyanazokkal a kulcsértékekkel rendelkeznek. Ez az opció nem ideális, továbbra is célszerű diszjunkt kulcskészleteket használni a táblákban, hogy például a lekérdezések ne keverjék össze véletlenül a konstansokat,
JOIN
és ne adjanak váratlan eredményeket. (Alternatív megoldásként, hogy ne legyenek metszéspontok, minden sorozatot különböző prímszámok többszöröséből is összeállíthatunk, de ez meglehetősen munkaigényes lenne.) - A
nextval()
sorozat létrehozására irányuló hívás a mai elosztott SQL-ben azt eredményezi, hogy az egész rendszer nem skálázódik jól. - A szekvenciális kulcsokat is használó adatbázisból származó adatok fogyasztása ütközéseket eredményez, mivel a szekvenciális értékek nem lesznek egyediek a rendszerek között.
- Filozófiai szempontból a számok szekvenciális növekedése olyan régi rendszerekhez kötődik, amelyekben a sorok sorrendje volt. Ha most szeretné rendezni a sorokat, akkor ezt kifejezetten tegye meg egy időbélyegző oszloppal vagy valamivel, aminek értelme van az adatokban. Ellenkező esetben az első normál forma sérül.
- Gyenge ok, de ezek a rövid azonosítók csábítóak, hogy elmondják valakinek.
UUID
Nézzünk egy másik lehetőséget: véletlenszerű minta szerint generált nagy egész számok (128 bites) használata. Az ilyen univerzálisan egyedi azonosítók (UUID) generálására szolgáló algoritmusok rendkívül kicsi a valószínűsége annak, hogy kétszer ugyanazt az értéket választják, még akkor is, ha egyidejűleg két különböző processzoron futnak.
Ebben az esetben az UUID-k természetes választásnak tűnnek helyettesítő kulcsként való használathoz, nem igaz? Ha egyedi módon szeretné felcímkézni a sorokat, akkor semmi sem jobb, mint egy egyedi címkét!
Miért nem használja mindenki ezeket a PostgreSQL-ben? Ennek több kitalált oka van, és egy logikus, amely megkerülhető, és bemutatom a benchmarkokat az álláspontom illusztrálására.
Először a távoli okokról fogok beszélni. Vannak, akik úgy gondolják, hogy az UUID-k karakterláncok, mert hagyományos hexadecimális jelöléssel írják őket kötőjellel: 5bd68e64-ff52-4f54-ace4-3cd9161c8b7f
. Valójában egyes adatbázisok nem rendelkeznek kompakt (128 bites) uuid típussal, a PostgreSQL-nek viszont igen, és mérete kettő bigint
, azaz az adatbázisban lévő egyéb információk mennyiségéhez képest a többletterhelés elhanyagolható.
Az UUID-ket is méltánytalanul nehézkességgel vádolják, de ki fogja őket kiejteni, begépelni vagy elolvasni? Azt mondtuk, hogy van értelme a mesterséges kulcsok megjelenítésének, de (a definíció szerint) senki sem láthatja a helyettesítő UUID-t. Lehetséges, hogy az UUID-vel egy fejlesztő fog foglalkozni, aki SQL parancsokat futtat psql-ben a rendszer hibakeresése érdekében, de ennyi. A fejlesztő pedig hivatkozhat a karakterláncokra kényelmesebb kulcsokkal is, ha adottak.
Az UUID-k valódi problémája az, hogy az erősen randomizált értékek íráserősítéshez vezetnek a teljes oldal írása miatt az előreírási naplóba (WAL) . A teljesítmény romlása azonban valójában az UUID generálási algoritmustól függ.
Mérjük meg az írási erősítést . Valójában a probléma a régebbi fájlrendszerekben van. Amikor a PostgreSQL lemezre ír, megváltoztatja az "oldalt" a lemezen. Ha kikapcsolja a számítógépet, a legtöbb fájlrendszer továbbra is sikeres írást jelez, mielőtt az adatok biztonságosan lemezre kerülnének. Ha a PostgreSQL naivan úgy érzékeli, hogy egy ilyen művelet befejeződött, akkor az adatbázis megsérül a következő rendszerindításkor.
Mivel a PostgreSQL nem tudja megbízni a legtöbb operációs rendszerben/fájlrendszerben/lemezkonfigurációban, hogy biztosítsák a folytonosságot, az adatbázis elmenti a megváltozott lemezoldal teljes állapotát egy előreírási naplóba, amely felhasználható az esetleges összeomlás utáni helyreállításra. Az erősen randomizált értékek, például az UUID-k indexelése általában egy csomó különböző lemezoldalt foglal magában, és azt eredményezi, hogy minden új bejegyzés esetén a teljes oldalméret (általában 4 vagy 8 KB) kerül a WAL-ba. Ez az úgynevezett teljes oldalas írás (full-page write, FPW).
Egyes UUID-generáló algoritmusok (például a Twitter „hópehelye” vagy az uuid_generate_v1() a PostgreSQL uuid-ossp kiterjesztésében) monoton növekvő értékeket generálnak minden gépen. Ez a megközelítés kevesebb lemezoldalba tömöríti az írást, és csökkenti az FPW-t.
5.6 Következtetések és ajánlások
Most, hogy láttuk a különböző típusú kulcsokat és azok felhasználását, szeretném felsorolni az adatbázisokban való használatukra vonatkozó javaslataimat.
Minden táblázathoz:
- Definiálja és deklarálja az összes természetes kulcsot.
- Hozzon létre egy UUID
<table_name>_id
típusú helyettesítő kulcsot alapértelmezett értékkel . Akár elsődleges kulcsként is megjelölheti. Ha ehhez az azonosítóhoz hozzáadja a tábla nevét, akkor ez leegyszerűsíti , pl. helyett kapni . Ne adja át ezt a kulcsot az ügyfeleknek, és egyáltalán ne tegye ki az adatbázison kívül.uuid_generate_v1()
JOIN
JOIN foo USING (bar_id)
JOIN foo ON (foo.bar_id = bar.id)
- Azon köztes táblák esetében, amelyek áthaladnak a -n
JOIN
, deklarálja az összes idegen kulcs oszlopot egyetlen összetett elsődleges kulcsként. - Opcionálisan adjon hozzá egy mesterséges kulcsot, amely használható az URL-ben vagy más karakterlánc-hivatkozási jelzésekben. Használjon Feistel rácsot vagy pg_hashids-t az automatikusan növekvő egész számok elfedésére.
- Adjon meg egy lépcsőzetes megszorítást
ON UPDATE RESTRICT
helyettesítő UUID-k használatával idegen kulcsként és mesterséges idegen kulcsok eseténON UPDATE CASCADE
. Válasszon természetes kulcsokat saját logikája alapján.
Ez a megközelítés biztosítja a belső kulcsok stabilitását, miközben lehetővé teszi, sőt meg is védi a természetes kulcsokat. Ráadásul a látható mesterséges kulcsok nem tapadnak semmihez. Ha mindent helyesen megértett, nem ragadhat le csak az „elsődleges kulcsokra”, és használhatja a billentyűk használatának összes lehetőségét.
GO TO FULL VERSION