Dátumok csoportosítása

dátumintervallumok logó

dátumintervallumok logóEbben a Java projektben dátumok csoportosítását oldjuk meg, többféleképpen is. Mikor van erre szükség? Jelentés, kimutatás, riport, lista készítése során.

Példaként tekintsünk egy blogot. A blogban rendszeresen jelenik meg új tartalom (bejegyzés, poszt). Azért, hogy a blog hosszabb távon, sok bejegyzéssel is könnyen kereshető, átlátható, böngészhető legyen/maradjon a felhasználók, látogatók számára, célszerű:

  • taxonómia kialakítása. Ez kategóriákat és címkéket jelent. Ebből címkefelhő vagy szófelhő is készíthető, ahogyan erről blogoltunk már: Címkefelhő generálása.
  • marketing analitika használata. Ezek általában toplisták valamilyen könnyen hozzáférhető adat alapján. Például: látogatottság, népszerűség, eltöltött idő, hozzászólások száma, megosztások száma, egérmutató mozgása alapján hőtérkép. Ezek általában toplisták, amelyek eleje listázódik csökkenő sorrendben.
  • dátum szerint is csoportosítani a blog bejegyzéseit. Érdemes megjeleníteni a legújabbtól a régebbi felé haladó (retrospektív) listát, hierarchikus fa struktúrát, lenyíló panelt. Mindez kombinálható toplistával. A csoportosítás elvégezhető igény szerint tetszőlegesen, például évente, negyedévente, havonta.

Lássuk, hogyan lehet megvalósítani a dátumok csoportosítását Java programozás nyelven!

Milyen adatokra van szükség?

Egy megadott zárt dátumtartományban véletlenszerűen előállítunk néhány dátumot. Nem számít, hogy különböznek-e. A dátumokat tároló listát érdemes csökkenő sorrendben tárolni. Minden dátum múltbeli, így ez a sorrend a jelenhez legközelebbitől halad a legtávolabbi felé. Például a Java program ezekkel a dátumokkal dolgozik (lapozható):

Milyen eredményeket kaphatunk?

Az évenkénti csoportosítás így jelenik meg:

A havonkénti csoportosítás így jelenik meg (lapozható):

Természetesen blog esetén gyűjtőoldalra mutató hivatkozást kell tenni a megjelenő elemekre. Azok az évek és hónapok nem jelennek meg, ahol nincs dátum (blog bejegyzés).

Hogyan kapjuk meg az eredményeket?

Természetesen Java nyelven programozva készítünk megoldást, sőt többféle megoldást. Ezek szépen összevethetők és mindenki kiválaszthatja azt, amit szívesen használna. A dátumobjektumok tárolása generikus listában történik, aminek típusa LocalDate. A dátumok formátuma: DateTimeFormatter.ofPattern("yyyy.MM.dd.").

1. megoldás

Ez a hagyományosnak tekinthető megoldás. Végigjárja a dátumobjektumokat tartalmazó dateList dátumlista adatszerkezetet. Két egymásba ágyazott ciklussal csoportváltást valósít meg. Feltételezi – nem ellenőrzi -, hogy az adatok sorrendje megegyezik az eredmény kiírásának megfelelő sorrenddel. Amíg két egymást követő dátum GROUP_BY_FORMAT formátuma azonos, addig ugyanabba a csoportba tartoznak. A csoportváltáskor az eredmény TYPE_FORMAT formátumú. Közben a beépített megszámolás programozási tétel is működik.

A groupByDate1() függvény képes az évente és havonta csoportosítás megvalósítására. Mindez a paraméterezésén múlik. Évente csoportosít, ha így hívja meg a vezérlés:

Évenkénti csoportosítás során például a 2024.02.26. és a 2024.01.30. (dátumként, nem szövegként értelmezve) azért tartozik egy csoportba, mert a dátumobjektumoktól elkért év "2024" szövegként mindkettő esetében megegyezik.

A groupByDate1() függvény havonta csoportosít, ha így hívja meg a vezérlés:

Havonkénti csoportosítás során például a  2023.06.14. és a 2023.06.08. (szintén dátumként értelmezve) azért tartozik egy csoportba, mert mindkettő illeszkedik a "202306" szövegmintára.

2. megoldás

Ez a Stream API-t és funkcionális programozást használó, újabb megoldás. Ciklus helyett beépített műveletek vannak. A groupByDate2() függvény a dátumok évenkénti csoportosítását képes megoldani:

A groupByDate3() függvény a dátumok havonkénti csoportosítására készült. A YearMonth osztály beépített ( java.time csomag). A DateCount saját POJO. Konstruktora 4 paramétert kap: YearMonth key, Long value, DateTimeFormatter format és String groupText, valamint van két hasznos metódusa. Az egyik az örökölt és felüldefiniált toString() a formázott kiíráshoz, a másik pedig a Comparable interfésztől implementált compareTo() a sorrend kialakításához szükséges összehasonlításhoz.

A funkcionális programozáshoz kötődő lambda műveletekről többször is blogoltunk már, így azokat most nem részletezem. Helyette ajánlom a szakmai blog lambda kifejezés címkéjét.

Továbbfejlesztés

Érdemes átgondolni az 1. és 2. megoldás markáns különbözőségeit, illetve egymást kiegészítő gondolatmenetét. Zárjuk két ötlettel a továbbfejlesztésre vonatkozóan:

  • A 2. megoldás két függvénye megoldható egyetlen függvénnyel, amely hasonlóan paraméterezhető, mint az 1. megoldás függvénye. Ezáltal univerzális(abb)nak tekinthető megoldás is készülhetne. Aki kellően motivált és végiggyakorolja a fentieket, biztosan meg tudja oldani. Várjuk hozzászólásban, vagy az ILIAS-ban a megoldást!
  • A csoportosítás egyben hierarchiát jelent, amiből fa szerkezet építhető. A fa vizuális komponensen is megjeleníthető, ahogyan blogoltunk is róla: Fát építünk.

A bejegyzéshez tartozó teljes forráskódot ILIAS e-learning tananyagban tesszük elérhetővé tanfolyamaink résztvevői számára.

A feladat a Java SE szoftverfejlesztő tanfolyam, a Java EE szoftverfejlesztő tanfolyam és a Java adatbázis-kezelő tanfolyam szakmai moduljának több alkalmához és az orientáló moduljának 1-4. óra: Programozási tételek alkalmához is kötődik. A Stream API-val és a lambda kifejezésekkel sokszor foglalkozunk.

Táblázatos hőtérkép készítése

Ebben a projektben táblázatos hőtérképet készítünk Java és JS nyelveken. Java programot készítünk az adatok véletlenszerű előállításához és a sablon alapján történő HTML fájl generálásához. JavaScript program fogja a grafikont megjeleníteni a weboldalon. Tervezünk, kódolunk, tesztelünk. Lássunk hozzá!

Mi az a hőtérkép?

A hőtérkép (heatmap) olyan grafikon, amely könnyen áttekinthetővé tesz nagy mennyiségű adatot úgy, hogy kategorizál/csoportosít és az előfordulások tartományai alapján különböző színeket rendel azokhoz. A szín hozzárendelése egy intervallumból történik. Például a világosabb a ritkább, a sötétedő szín az egyre gyakoribb értékeket jelenti. A tipikus hőtérkép kétdimenziós és az előforduló adatok mennyiségét, azok arányait, eloszlását, szóródását (nem szórását), gyakoriságát jeleníti meg. A hőtérkép gyors vizuális összefoglalást, áttekintést biztosít. A projekt során sorokból és oszlopokból álló táblázatos hőtérképet készítünk.

A táblázatos hőtérkép nem összekeverendő a következő két megközelítéssel:

  • Ha az adatok lokációhoz kötődnek és térképen jelennek meg, akkor azt tematikus térképnek nevezzük. Erről már blogoltam korábban, lásd: Céline Dion – Courage World Tour, amikor az énekesnő USA-beli államokban előforduló koncertjeinek számát jelenítettem meg. Ehhez hasonlók az amerikai elnökválasztás során használt tematikus térképek, amelyek a(z egyes) jelöltek szavazatainak arányát ábrázolják meg szintén államonként.
  • Ha az adatok webergonómiához, bannervaksághoz, inkább fókuszban lévő tartalmakhoz, felhasználói élmény (UX) tervezéshez kötődnek, akkor a weboldal grafikus elemeihez (azok pozíciója) alapján rendelünk hozzá színeket attól függően, hogy mennyi ideig nézi azt a felhasználó.

Mi a feladat (koncepcionálisan)?

Adott egy konditerem, amely hétköznapokon egy megadott időintervallumban használható. Ehhez kulcsot kell felvenni és leadni. Az első belépő nyitja és az utolsó távozó zárja az ajtót. A teremhasználat offline nyilvántartott, így nehézkes bármiféle kimutatás, statisztika. A „menet közbeni” jövés-menést nem tudjuk követni. Természetesen adott számos szabály (felelősség, biztonság, balesetvédelem, létszámkorlát), amit most nem részletezek.

Elhelyezünk az ajtó mellett, belülről egy belépéshez és egy távozáshoz tartozó QR kódot. Készítünk egy egyszerű mobil alkalmazást és megkérjük a konditermek használóit, hogy belépéskor és távozáskor „csekkoljanak”. Anonim gyűjtjük a belépések és távozások időpontját (valahol egy szerveren, bármilyen fájlban, adatbázisban).

A konditermek használatára vonatkozó összesített adatokat könnyen átlátható módon szeretnénk weboldalon megjeleníteni: heti bontásban, a nyitva tartás időszakát órás blokkokra bontva az igénybevételtől függően jelenjenek meg az adatok táblázatos hőtérképen.

Ez az állapot átmeneti. Segítheti a konditermekbe tervezett – egyéni használattól független – események ütemezését. Ezeket akkor lenne célszerű időzíteni, amikor nem, alig, vagy kevésbé használt, foglalt az adott konditerem. A továbbfejlesztés következő állapotában könnyen lecserélhető a QR kód RFID alapú proximity kártyára, proxy kulcstartóra: először csak a jelenlét nyilvántartásához, később akár az ajtó nyitásához is.

Mit valósítunk meg mindebből Java nyelven?

Egyetlen konditeremre fókuszálunk. A hétköznaponként nyitva tartás legyen 16 órától 21 óráig. Aki edzésre jön, véletlenszerűen 20 és 40 perc közötti időszakot tölt a konditeremben. 16 órától lehet belépni. Az utolsó belépés 20:40-kor lehet (érdemes). 21 órakor mindenki elhagyja a konditermet. Nem fordul elő, hogy valaki nem jelzi a belépését vagy a távozását. Mivel anonim a nyilvántartás, így elegendő a dátum/időhöz, időbélyeghez egyetlen állapotot tárolni: be vagy ki. Valaki belépett nyitáskor vagy valamikor utána, majd távozott 20-40 perccel később, de legfeljebb záráskor.

A szükséges adatokat véletlenszerűen állítjuk elő. Egy hétre vonatkozó adatokat generálunk. Ezek a fenti paramétereknek megfelelő, összetartozó belépéshez és távozáshoz köthető időpontok. Kiegészítve a napok ciklusban való léptetésével hétfőtől péntekig. Az adatokat feldolgozva, összegyűjtve, csoportosítva, kategorizálva olyan (kimeneti) formátumra alakítjuk, amely kompatibilis a táblázatos hőtérkép adatmodelljével (bemenetével). Mindez Java nyelven valósul meg.

Mi történik JavaScript nyelven?

A webes megjelenítéshez szükséges egy HTML fájl, amelyben beágyazva található meg egy téglalap alakú területként megjelenő táblázatos hőtérkép. Ez sokféleképpen testre szabható: adható hozzá felirat, beállítható a sorokhoz és oszlopokhoz tartozó szöveg és a táblázat celláiban megjelenő értékek, adott a lebegő, az egér kurzor helyzetétől függő – cellánként különböző – jelmagyarázat, és persze mindennek van formátuma (betűtípus, méret, szín, igazítás, kitöltés). A fix, adatoktól nem függő beállításokat tartalmazó weboldalt sablonként elkészítjük. Mindez JavaScript nyelven történik. Ezután Java program a weboldal sablonját kiegészíti (cseréli, behelyettesíti, feltölti) a szükséges adatokkal.

Hogyan alakul az időintervallumok átfedése?

A konditerem használatának alakulását követjük, amihez táblázatos hőtérképet készítünk. Ehhez a nyitva tartás időintervallumát órás blokkokra bontjuk. Blokkonként összesítjük a jelenlétet, azaz megszámoljuk, hogy éppen akkor hányan veszik igénybe a konditermet, hányan vannak jelen/benn.

A konkrét paraméterektől függően az alábbi képen látható 3 eset egyike fordulhat elő. A és B jelöli az órás blokk elejét és végén, tehát ez 60 perces intervallum. Tarthat például: 16:00:00-16:59:59-ig. X és Y jelöli a jelenlét intervallumát, azaz a belépés és távozás időpontjait. Ez 20 és 40 perc között alakul. Haladjunk balról jobbra az ábrán.

Az első esetben ugyanarra az órára esik a belépés és a távozás. Ez az eset egyértelmű. A másik két esetben átfedés van több órás blokk között, mert különböző órára esik a belépés és a távozás. El kell dönteni, hogy ekkor hogyan összesítjük a jelenlétet. Válasszunk az alábbi két módszer közül:

  • Az első módszer szerint mindkét órához – ahol átfedés van – összesítjük a jelenlétet 1-1 főként, hiszen ha nem is végig, de jelen volt mindkét órás blokkban. Például: egy 16:50:00-17:20:00 jelenlétet a 16 és 17 órás blokkban is figyelembe veszünk.
  • A második módszer szerint időarányosan tesszük mindezt, azaz súlyozunk aszerint, hogy milyen hosszú jelenlét esik az egymást követő órákban. Például: egy 16:50:00-17:20:00 jelenlétet a 16 órás blokk esetében egyharmad, a 17 órás blokk esetében kétharmad a jelenlét adott órára eső aránya, súlya.

Az első módszert valósítjuk meg. Ez a döntés jelentősen befolyásolja, hogyan kell értelmezni később az elkészült táblázatos hőtérképet.

Objektumorientált tervezés

A koncepcionális terv alapján modellezünk. A szükséges adatok tárolására és alapfunkcióira fókuszálunk. Az osztály tárolja az összetartozó adatokat és megvalósít rajtuk értelmezhető műveleteket. Az időintervallum/időtartam kezelését a Duration ősosztály oldja meg. Tárolja a start és stop – naptól független időpontokat tároló – adatok és a rájuk vonatkozó FORMAT – megjelenítéshez kötődő – konstanst. Biztosítja a szükséges műveleteket: konstruktor, getterek, megvalósítja az időintervallumok átfedését az isOverlapped() függvénnyel, valamint ad szöveges reprezentációt a toString() függvénnyel. Az ősosztályból öröklődik az utódosztály. A DurationMap osztály a naptól független időpontokat kibővíti a hozzájuk tartozó day nappal, valamint képes tárolni az összesített, megszámolt jelenlétet a count változóban. Részt vesz a megszámolás folyamatában azzal, hogy lépésenként meghívható az  incrementCount() eljárása.

A java.time csomagbeli LocalTime osztály képes a dátumtól független, napon belüli időpont tárolására és biztosít néhány alapvető funkciót a kezelésükre. Az adattárolás a napon belül eltelt időn alapul és nekünk (bőven) elegendő a másodperc alapú megjelenítés. A DateTimeFormatter alkalmas ezen időpontok formátumának tárolására, például óó:pp:mm alakban.

A Duration osztályból annyi objektum készül, ahány jelenlét adódik véletlenszerűen. Akár több száz is lehet. A DurationMap  osztályból generált objektumok száma jóval kevesebb. Heti 5 napra, napi 5 órás blokkra 25 db készül belőle.

A vezérléshez kötődő osztály tervezését nem részletezem.

Íme a Java forráskód

A Java forráskód minden megtervezett funkciót megvalósít, támogatva a koncepciót. Most nem részletezem a működését.

A véletlenszerűen előállított adatok

A lista görgethető:

A weboldal sablonja

HTML és JavaScript nyelvű forráskód vegyesen. A Java program a fájl 31. sorában lévő ##HEATMAP_DATA## szöveget cseréli le a táblázatos hőtérkép megjelenítéséhez szükséges véletlenszerűen előállított adatokra.

További részletekért, beállításra vonatkozó, testre szabási lehetőségekért érdemes tanulmányozni az AnyChart dokumentáció Heat Map Chart fejezetét.

Az eredmény

Az előállított weboldalt böngészőben megjelenítve ezt kaphatjuk eredményként (vagy a véletlenszerűen generált adatoktól függően hasonlót):

A táblázatos hőtérkép hasznos eszköz. Elemezve könnyen döntéseket hozhatunk a koncepcionális tervezés során vázoltak alapján.

Továbbfejlesztési lehetőségek

  • Lehetne több konditerem is. Ekkor rögtön felmerül az összehasonlítás lehetősége, egyben igénye is.
  • Lehetne hétköznaponként eltérő a konditerem nyitva tartása.
  • Az időpontok kezelési precíz. Egy másodpercen múlik, hogy nem fedik át egymást. Az időpontok megjelenítése lehetne óó:pp alapú is.
  • Az időintervallumok jelenleg állandóak, mindig 1 órásak. Könnyen megoldható lenne, hogy dinamikusak legyenek: például a népszerűbb időszakok felbonthatók lennének két 30 perces blokkra. A népszerűség értelmezhető minden nap (héten) másképp. Egyszerű képlettel: átlag felett, medián felett.
  • Általánosíthatnánk a létesítménygazdálkodáshoz kötődő erőforrást nem (feltétlenül) helyhez kötött, mozgatható, kölcsönözhető eszközökre is: hangszer, projektor, stúdió felszerelés.
  • Másképpen valósulna meg az adatgyűjtés, ha egyetlen QR kód állna rendelkezésre és back-end helyett a „mobil alkalmazás emlékezne” a belépésre és távozásra. Ez jelentheti legalább az aznapi adatokat, de tárolható historikusan is.
  • Hibát is kellene, lehetne kezelni. Például a kiléptetés lehetne automatikus a nyitva tartás végén. A jelenlét igazából igaz-hamis állapot: ha eddig hamis volt és történt valami, akkor igaz lesz és fordítva. Ha van mögötte állapotmegőrző emlékezet (mivel programozunk, így nyilvánvalóan azonnal objektumra gondolunk, vagy annak valamilyen fájlba vagy adatbázisba történő leképezésére).
  • A nyilvántartás könnyen megvalósítható személy hozzárendelésével, azaz lehetne nem anonim is a jelenlét.
  • Egymást átfedő időpontok esetén (ha nincsenek a hosszukra vonatkozó korlátozó feltételek) általánosítva 6 eset fordulhat elő. Például ha a jelenlét lehetne 60 percnél hosszabb, de 120 percnél rövidebb is, akkor nem lenne elegendő a fenti 3 esetet kezelni a jelenlét összesítése során.
  • Valósítsuk meg az időintervallumok átfedésénél bemutatott második – időarányos – módszert!
  • A napi jelenlét 20 fővel valósul meg a programban. Lehetne ez a paraméter is véletlenszerű, például 15-30 fő között, vagy esetleg népszerűbb a péntek.
  • Jelenleg a táblázatos hőtérkép statikus. Csak a (befejezett egész heti) múltbeli adatokat tudja megjeleníteni. Az aktuális, jelenlegi állapothoz szinkronizáció, ütemezés kell. Óránként (a blokkok végén automatizáltan), de akár 5 percenként is aktualizálható a hőtérkép.
  • A táblázatos hőtérkép megjelenítése önálló weboldal helyett beágyazható widget felületén is történhet.
  • Többféleképpen is készítettünk már grafikonokat, íme néhány a szakmai blogunkból: Kockadobás kliens-szerver alkalmazás, Sankey-diagram készítése, JFreeChart grafikon készítése.

A bejegyzéshez tartozó forráskódot ILIAS e-learning tananyagban tesszük elérhetővé tanfolyamaink résztvevői számára.

A projektfeladat – attól függően, milyen szinten valósítjuk meg – kapcsolódhat több tanfolyamunk tematikájához. A fenti forráskód a Java SE szoftverfejlesztő tanfolyam 17-28. óra: Objektumorientált programozás és a 37-44. óra Fájlkezelés alkalmaihoz kötődik. Ha többrétegű, elosztott alkalmazásként valósítjuk meg, akkor a Java EE szoftverfejlesztő tanfolyam a 9-16. óra: XML és JSON feldolgozás, dinamikusan generált weboldalba beépítve a 33-40. óra: Java Server Pages alkalmaihoz kapcsolódik. Ha fájlok helyett egyszerű adatbázist használnánk, akkor a Java SE szoftverfejlesztő tanfolyam 45-52. óra: Adatbázis-kezelés JDBC alapon, ha objektumrelációs leképezéssel oldanánk meg, akkor a Java EE szoftverfejlesztő tanfolyam 25-32. óra: Adatbázis-kezelés JPA alapon alkalmakhoz kötődhet.

Születésnap-paradoxon

Mennyi a valószínűsége, hogy n ember között van kettő, akiknek egy napon van a születésnapja? A meglepő a dologban az, hogy már 23 ember esetén a kérdéses valószínűség 1/2-nél nagyobb. Másképpen: már 23 ember esetén nagyobb annak az esélye, hogy megegyezik a születésnapjuk, mint az ellenkezőjének. Ez a 23 nagyon kevésnek tűnik. Ezért paradoxon.

Közismert néhány hétköznapi valószínűség. Íme néhány szabályos eset. A pénzfeldobás során 1/2 az esélye a fej és 1/2 az esélye az írás eredménynek (másképpen 50%-50%, azaz fifty-fifty). A kockadobás esetén 1/6 az esélye bármelyik számnak 1-től 6-ig. Két kocka esetén blogoltam már a dobott számok összegének alakulásáról, eloszlásáról: Kockadobás kliens-szerver alkalmazás.

Néhány egyszerűsítés

  • Az év 365 napból áll. Nem számítanak a 366 napos szökőévek.
  • A születések eloszlása egyenletes, azaz minden nap körülbelül ugyanannyian születnek. Nem számít, hogy hétköznap, hétvége, ünnepnap. Az áramszüneti városi legendák sem.
  • Nem vesszük figyelembe az azonos napon született ikreket. Persze ikrek születhetnek különböző napokon is.

Azonos születésnap valószínűsége grafikonon

Lássuk, hogyan alakul az azonos születésnap valószínűsége az emberek számától függően! Grafikonon ábrázolva:

A fenti grafikonhoz szükséges adatok könnyen előállíthatók az alábbi Java forráskóddal:

A fenti Google Chart típusú szórásgrafikon (Scatter Chart, korrelációs diagram) megjelenítéséhez adatpárok sorozata szükséges. Ezek a konkrétumok (70 db adatpár), görgethető:

Hasonló grafikon készítéséről szintén blogoltam már: Céline Dion – Courage World Tour.

Párok előállítása

Az emberek születésnapjainak összehasonlítása párokban történik. 23 ember esetén 23*22/2=253 pár van. Általános esetben n ember esetén (n*(n-1))/2 pár adódik. A levezetés részletei a források között megtalálható. 59 ember esetén 1711 pár adódik és szinte garantált az előforduló azonos születésnap, hiszen már 0,99 ennek a valószínűsége.

Az alábbi Java forráskód – rekurzív módon – előállítja a 23 konkrét esetre a párokat, az embereket 1-23-ig sorszámozva. Kombinációk:

A main() metódusban az i változó paraméterezhető és a konkrét eset könnyen intervallumra változtatható. Eredményül ezt írja ki a program a konzolra, görgethető:

Kísérleti ellenőrzés

Tekintsünk például 1000 esetet! Készítsünk Java programot, amely 23 db véletlen születésnapot generál! Legyen ez a születésnap sorszáma az évben (másképpen hányadik napon született az ember az évben). Ez lényegesen egyszerűsíti a megoldást, összevetve a dátumkezelésen alapuló megközelítéssel. Ajánljuk a szakmai blog dátumkezelés címkéjét az érdeklődőknek, ahol megtalálhatók a témához kapcsolódó Java forráskódrészletek részletes magyarázatokkal kiegészítve. Íme a többféle generikus listát és programozási tételt használó forráskód:

Érdemes elemezni, tesztelni a fenti forráskódot: milyen lépésekben, milyen adatszerkezeteket épít. Hasznos lehet lambda kifejezésekkel kiegészíteni, módosítani a programot. Részlet a program szöveges eredményéből:

A 12. sorban lévő számhármasok jelentése: esetszám 1-től, azonos nap, előfordulás száma. Például: a kísérlet során a 8. esetben az év 225. napja azonos 3 embernél. Természetesen nincs garancia arra, hogy az 1000 eset vizsgálatánál mindig 500-nál nagyobb kedvező esetet kapunk.

A bejegyzéshez tartozó teljes forráskódot ILIAS e-learning tananyagban tesszük elérhetővé tanfolyamaink résztvevői számára.

A feladat a Java SE szoftverfejlesztő tanfolyam szakmai moduljának 17-28. óra Objektumorientált programozás alkalmaihoz kötődik.

Források

Tankocka – Rövid válasz: Java konstansok

Folytatjuk Tankockák blog bejegyzés sorozatunkat. A feladatban 12 db Java konstans értékét kell megadni, számként. Ez a témakör főként a Java SE szoftverfejlesztő tanfolyamunkhoz kötődik. Hasznos ismerni a JDK-beli konstansokat, mert alapvetően befolyásolják egy adatszerkezet, objektum működését, viselkedését akkor is, ha külön nem adjuk meg a létrehozó konstruktorok meghívásakor minden paramétert.

A konstansok hathatnak a memóriafoglalásra, a grafikus komponensek megjelenésre, Meghatározhatják a hálózati kommunikáció időzítését, paraméterezhetik a fájlkezelés működését. Egy-egy algoritmus (például: rendezés, keverés, véletlenszám-generálás) indítását, működését is befolyásolják a konstansok. Vajon hányat tudsz az alábbi konstansok közül? Hogyan, mire hatnak ezek?

Kik vettek részt projektmunkában?

projektmunka logó

projektmunka logóHasonlítsuk össze a részlegeket fókuszálva arra, hogy az alkalmazottak mennyire vettek korábban részt projektmunkákban! Hányan igen és hányan nem? Van(nak) olyan részleg(ek), amelyik vezetője egyetlen alkalmazottat sem vont be projektmunkába? Van(nak) olyan részleg(ek), ahonnan mindenki csatlakozott? Vannak a feladatkiosztásban olyan aránytalanságok, amelyek kimutathatók és így a későbbiek során korrigálhatók? Készítsünk egy kimutatást arról, hogy részlegenként hány fő vett részt projektmunkában és mi a létszám! (Persze tudjuk, hogy nem minden munkakörből vonhatók be alkalmazottak.) Milyen projektjeink szoktak lenni? Van olyan részleg, ahol érdemes bővíteni a létszámot, esetleg átcsoportosítani oda erőforrást? Ezekre a kérdésekre keressük a választ.

Tervezés

Az Oracle HR sémában három tábla kapcsolódik a feladathoz: JOB_HISTORY, EMPLOYEES, DEPARTMENTS. A kapcsolatok fokszámai láthatók az alábbi ábrán. Egy részlegben több alkalmazott is lehet. Egy alkalmazott részt vehetett korábban több projektmunkában is.

Oracle HR séma

A DEPARTMENTS táblában található a részleg azonosítója ( DEPARTMENT_ID, kulcs) és neve ( DEPARTMENT_NAME). A többi adat most nem kell. 11 olyan részleg van, amihez tartozik alkalmazott.

A JOB_HISTORY tábla tárolja, hogy a már befejeződött projektekben ki ( EMPLOYEE_ID, külső kulcs) és melyik részlegből ( DEPARTMENT_ID, külső kulcs) vett részt. A dátumokat ( START_DATE, END_DATE) és a munkakör külső kulcsát ( JOB_ID) most nem használjuk. Minden projekt lezárt. 10 lezárt projekt van.

Az EMPLOYEES táblából szükséges az alkalmazott azonosítója ( EMPLOYEE_ID, kulcs), valamint részlegének azonosítója ( DEPARTMENT_ID, külső kulcs). A többi adatra most nincs szükség, de egy részletesebb – például név szerinti – kimutatáshoz már igen. 106 olyan alkalmazott van, akihez tartozik részleg (1-nek nincs).

Hozzunk létre négy oszlopból álló eredménytáblát: DEPARTMENT_ID, DEPARTMENT_NAME, COUNT_PROJECT_EMPLOYEES, COUNT_EMPLOYEES. Ennek áttekintésével választ kaphatunk a fenti kérdésekre.

1. megoldás

Induljunk ki abból, hogy a JOB_HISTORY táblában lévő DEPARTMENT_ID-hez hozzárendeljük a DEPARTMENTS táblából a DEPARTMENT_NAME-t. Ezekre csoportosítva könnyen aggregálható az adott részlegből projektmunkát végző alkalmazottak száma: COUNT_PROJECT_EMPLOYEES. Végül egy belső lekérdezés (összekapcsolva a JOB_HISTORY és az EMPLOYEES táblákat) megadja az adott részleg alkalmazotti létszámát. Az SQL lekérdezés:

SQL-megold1a

A részeredmény:

SQL-eredmeny1a

Ezután állítsuk elő a hiányzó adatokat! Tudjuk, hogy azokban a részlegekben, amelyek DEPARTMENT_ID-je nem szerepel a JOB_HISTORY táblában, de szerepel az EMPLOYEES táblában, azok léteznek, de nem „adtak” projektmunkára alkalmazottat (azaz COUNT_PROJECT_EMPLOYEES=0). Nevük és alkalmazottaik száma ugyanúgy megadható, ahogyan az előbb. Az SQL lekérdezés:

SQL-megold1b

A részeredmény:

SQL-eredmeny1b

A két részeredményt egyesíteni kell és egyben hasznos DEPARTMENT_NAME szerint növekvő sorrendbe rendezni az alábbi lekérdező paranccsal:

SQL-megold1c

Az eredmény:

SQL-eredmeny1c

2. megoldás

Kiindulhatunk abból is, hogy a DEPARTMENTS egy szótártábla, így közvetlenül hozzáférhető a DEPARTMENT_ID és a DEPARTMENT_NAME, de össze kell kapcsolni az EMPLOYEES táblával, hogy csak olyan részlegeket adjon vissza a lekérdezés, ahol van(nak) alkalmazott(ak). Az eredményhez szükséges további két oszlop könnyen aggregálható az adott részlegre vonatkozóan: a JOB_HISTORY táblában előforduló EMPLOYEE_ID-k száma adja a COUNT_PROJECT_EMPLOYEES-t (probléma nélkül tud 0 lenni) és az EMPLOYEES táblában előforduló EMPLOYEE_ID-k száma adja a COUNT_EMPLOYEES-t. A rendezés most is szükséges. Lényegesen tömörebb lekérdező parancsot kapunk:

SQL-megold2

Az eredményül kapott táblázat megegyezik az 1. megoldás eredményével.

A két megoldás teljesen különböző gondolatmenettel született. Mindkettőben vannak olyan elemek, amelyek – konkrét feladatból általánosítva – univerzálisan használhatók. Természetesen összehasonlítjuk a két megoldás végrehajtási tervét és részletesen elemezzük is.

A bejegyzéshez tartozó teljes forráskódot ILIAS e-learning tananyagban tesszük elérhetővé tanfolyamaink résztvevői számára.

A feladat a Java adatbázis-kezelő tanfolyam 9-12. óra: Oracle HR séma elemzése, 13-16. óra: Konzolos kliensalkalmazás fejlesztése JDBC alapon, 1. rész, 33-36. óra: Grafikus kliensalkalmazás fejlesztése JDBC alapon, 2. rész alkalomhoz kapcsolódik.