Kezdőoldal » Számítástechnika » Programozás » MySQL, group by, komplex...

2*Sü kérdése:

MySQL, group by, komplex szempontrendszer alapján a legjobb recordokról lista. Egyszerűbb megoldás?

Figyelt kérdés

Adott egy tábla, ami több különböző tesztnek és több különböző felhasználónak a tesztkitöltési eredményeit tárolja, a következő mezőkkel:


testresult

- - - - - - - -

id - a teszteredmény autoinc azonosítója

testid - a teszt azonosítója

uid - felhasználó azonosítója

starttime - a teszt kitöltésének kezdetei időpontja

endtime - a teszt kitöltésének vége (ha elvileg még tart a kitöltés – akár pl. részeredmény mentve lett, de a netkapcsolat megszakadt a teszt befejezése előtt – akkor nulla)

point - pontszám

rating - szöveges értékelés (a tartalma lényegtelen, a lényeg, hogy van extra mező)

cheat - false: nem csalt, true: csalt, ezt a teszteredményt nem szabad figyelembe venni

stb…


Egy felhasználó egy tesztet többször is kitölthet. Ebből kellene kinyernem egy adott azonosítójú tesztnél a userek legjobb eredményeit, sorrendben. Ha egy user többször is kitöltötte a tesztet, akkor a legjobb eredménye az, aminek a legnagyobb a pontszáma. Pontszámegyezés esetén a legrövidebb a kitöltési idő, azonos kitöltési idő esetén a legkorábban kitöltött teszt számít. (Egyéb esetben a sorrendiség lényegtelen, de legyen következetes.)


SELECT *

FROM testresult

WHERE (testid=123) AND NOT cheat

ORDER BY point DESC, IF(endtime=0,CURRENT_TIMESTAMP(),endtime)-starttime, starttime, id


Csak mindezt úgy, hogy egy felhasználónak csak a legjobb eredménye legyen benne a lekérdezésben, ne az összes, meg aztán az egész össze legyen joinolva más táblákkal is esetleg, mondjuk egy users táblával.


Adatbáziskezelő: MariaDB 10.0.28

> mysql --version

mysql Ver 15.1 Distrib 10.0.28-MariaDB, for debian-linux-gnu (x86_64) using readline 5.2


~ ~ ~


Eddig csak nagyon nyakatekert megoldást találtam erre. Nincs erre valami általánosabb, átláthatóbb megoldás? Mert így kevésbé áttekinthető, ha módosítani kell, akkor problémás, ha tovább kell cizellálni a lekérdezést, nehezen kezelhetővé válik. Optimalizálás szempontjából is kérdéses, bár azt nem nagyon vizsgálgattam.


Az általam kreált megoldás:

pastebin [pont] com/hxk3jUhR


JOIN-nal is próbálkoztam, az sem tűnt kezelhetőbbnek.



2019. jan. 24. 00:27
 1/3 A kérdező kommentje:
* Megjegyzés: stackoverflow.com kérdéseket nézegettem, de a legtöbb kimerült abban, hogy egyetlen feltétel alapján választja ki a legmegfelelőbb recordot, mondjuk csak a pontszám alapján, és nem számol azzal, hogy userenként több record esetén is lehet azonos pontszám. Találtam ilyen szempontból hibás válaszokat, meg hasonlóan komplex válaszokat is. Meg találtam olyan válaszokat, amik más adatbáziskezelőnél működnek, de MySQL esetén nem.
2019. jan. 24. 00:37
 2/3 anonim ***** válasza:

Nem tudom, ez segít-e:

[link]

[link]

stb.

2019. jan. 24. 10:23
Hasznos számodra ez a válasz?
 3/3 A kérdező kommentje:

Köszi, de…


Az első linken sok olyan megoldás van, ami adatbázisspecifikus és történetesen MySQL esetén nem működik.


A második link megoldásainál több probléma van:


1. Csak egy rendezési kritériumról szólnak. Többszörös rendezési kritériumra – főleg, ha szűrni is kell a rekordokat valamilyen feltétel mentén – nem lehet néhány adattal, egyszerűen kiterjeszteni. A lekérdezés az én adatbázisomban így nézne ki


2. Ha két azonos rekord van, akkor vagy mindkettőt beveszi a lekérdezésbe eredményébe.


Pl.:


SELECT A.Page, A.DateStamp as FirstViewed, A.IpAddress

FROM LogTable A

LEFT JOIN LogTable B

ON A.Page = B.Page

AND A.DateStamp > B.DateStamp

WHERE B.DateStamp is null


Ha most a következő van az adatbázisban:


20/06/2011 ; 192.168.0.1 ; home

20/06/2011 ; 192.168.0.2 ; home


akkor a fenti lekérdezésben két olyan record is lesz, amiben page='home'


~ ~ ~


Ha ennek nyomán építkezek, akkor ez lesz belőle:


SELECT t1.*

FROM testresult t1

LEFT JOIN testresult t2

ON (t1.uid = t2.uid) AND (t1.testid=t2.testid) AND (NOT t2.cheat) AND (

(t1.point<t2.point) OR

((t1.point=t2.point) AND (IF(t1.endtime=0,CURRENT_TIMESTAMP(),t1.endtime)-t1.starttime > IF(t2.endtime=0,CURRENT_TIMESTAMP(),t2.endtime)-t2.starttime)) OR

((t1.point=t2.point) AND (IF(t1.endtime=0,CURRENT_TIMESTAMP(),t1.endtime)-t1.starttime = IF(t2.endtime=0,CURRENT_TIMESTAMP(),t2.endtime)-t2.starttime) AND (t1.starttime>t2.starttime))

)

WHERE (t2.point IS NULL) AND (t1.testid=123) AND (NOT t1.cheat)

ORDER BY point desc, IF(t1.endtime=0,CURRENT_TIMESTAMP(),t1.endtime)-t1.starttime, t1.starttime, id


Ez sem sokkal szebb…


Egyesítsd a két táblát, ha a t1 pontja kisebb, mint a t2 pontja,

vagy ha azonos, de a kitöltési idő nagyobb, mint a másiknál,

vagy ha ez is azonos, akkor ha a kezdés dátuma nagyobb…


~ ~ ~


Bár itt most megihletődtem, mert lehetne származtatni egy szöveget, amiben benne vannak a rendezés szempontjából figyelembeveendő értékek. Mondjuk:

- feltételezzük, hogy egy teszt maximum 9999 pontos,

- feltételezzük, hogy a kitöltési idő maximum 999 999 999 másodperc,

- nem érdekel minket, hogy mi történik 2286. november 11. után,

- nem érdekel minket, mi lesz az 1 milliárdodik tesztkitöltés után,

akkor:


CONCAT(1000+point, 1000*1000*1000 + IF(endtime=0,CURRENT_TIMESTAMP(),endtime)-starttime, 10*1000*1000*1000+starttime, 1000*1000*1000+id)


Ez már egy értékben egyesíti a három sorrendezési feltételt, és kicsivel átláthatóbb lekérdezést ad. De még mindig el tudnék képzelni átláthatóbb megoldást, a sorrendiséget két helyen is bele kell írni, a szűrési kritériumot úgyszintén.

2019. jan. 24. 11:40

Kapcsolódó kérdések:




Minden jog fenntartva © 2024, www.gyakorikerdesek.hu
GYIK | Szabályzat | Jogi nyilatkozat | Adatvédelem | Cookie beállítások | WebMinute Kft. | Facebook | Kapcsolat: info(kukac)gyakorikerdesek.hu

A weboldalon megjelenő anyagok nem minősülnek szerkesztői tartalomnak, előzetes ellenőrzésen nem esnek át, az üzemeltető véleményét nem tükrözik.
Ha kifogással szeretne élni valamely tartalommal kapcsolatban, kérjük jelezze e-mailes elérhetőségünkön!