Dotaz s GROUP BY který varcí téměř co potřebuji:
SELECT * FROM tabulka GROUP BY content_id, action ORDER BY date DESC LIMIT 15
Problém
Dotaz v podstatě vrací data tak aby tam nebyla duplicita obsah/akce bez ohledu na to kdo akci udelal. Nicmene pokud tam budou tyto akce od uzivatele A pozdeji od uzivatele B a pozdeji od uzivatele C, s timto group by to vrati prvniho kdo tu akci udelal, tedy uzivatele A a ostatni vynecha, jenze jak je videt diky ORDER BY date DESC ja potrebuji vratit posledniho uzivatele ktery akci ucinil, nikoliv prvniho...
Moje řešení
Dělám dotaz bez GROUP BY, v php vyřadim duplicitní uživatele k záznamu a takhle opakuji dokud nevyberu 15 neduplicitních řádků.
ANO, toto řešení sice funguje, ale je uplně dementní, jenže jsem teď strašně přepracovanej a z nějakého důvodu prostě nemůžu vymyslet dotaz, který to vybere podle mé potřeby. Tak se tu obracím na vás.
DISTINCT a DISTINCTROW je v podstate GROUP BY akorat ze k tomu navic nemuzu vybrat libovolne sloupce, jen ty co jsou v DISTINCT ... takze se obavam ze to nepomuze :(
---------- Příspěvek doplněn 17.12.2011 v 09:04 ----------
Tak po delsi dobe hledani a hrani jsem dosel k nasledujicimu dotazu ktery dela presne to co popisuji, nektere databaze to maji jako DISTINCT ON () ovsem to mysql nepodporuje, obchazi se to takto:
Kód:
SELECT s.*
FROM tabulka As s
INNER JOIN (SELECT max(date) As lastdate, content_id, action FROM table GROUP BY content_id,action) AS ms
ON (ms.lastdate = s.date AND ms.content_id = s.content_id AND ms.action = s.action)
ORDER BY s.date DESC
Naposledy upravil Aleš Jiříček : 17.12.2011 v 09:04
SELECT t1.*
FROM `table` AS t1
LEFT JOIN `table` AS t2
ON t1.content_id = t2.content_id
AND t1.action = t2.action
AND t1.lastdate < t2.lastdate
WHERE t2.lastdate IS NULL
Výborně díky, ta optimalizace se bude hodně hodit, sice sem to uz trochu optimalizoval pres where id>(*subquerty na vyber nejvetsiho id*)-1000 kdy sem to an tabulce s milionem zaznamu srazil ze 3 sekund na 30 milisekund, ale ten left join bude ais lepsi a mozna to optimalizuje jeste vic :)
---------- Příspěvek doplněn 17.12.2011 v 11:14 ----------
Jo tak varianta s left joinem strhla server, vysledek vraci spravne, ale jak se na stackoverflow nekdo zminoval, ten left join proste funguje rychlejc jen na malo zaznamech, jakmile je to ten milion tak jeho rychlost velice rapidne klesa, zustanu u optimalizovaneho subquery a 30ti milisekund :)
Naposledy upravil Aleš Jiříček : 17.12.2011 v 11:22
nevim, zkus si to otestovat, ja to zkousel a na vysledek z left joinu jsem se pro jistotu vubec nedostal (proste to zasere mysql uplne do sracek a musim to retsratovat) a ano :) mam spravne indexy...
btw i subquery bez optimalizace je na te milionove tabulce na 2 sekundy (s optimalizaci 30ms), coz je porad mnohem rychlejsi nez ten left join co to uplne zasekne (ale funguje kdyz ma tabulka min zaznamu, ale rychlost hodne klesa s pribyvajicimi tisici zaznamu)
---------- Příspěvek doplněn 17.12.2011 v 12:10 ----------
jinak zkousel jsem i jina reseni z onoho tematu, zatim z toho porad vychazi nejlip ten subquery co sem uvadel tady... protoze ty ostatni sice funguji dobre na male tabulce, ale kdyz to dam na milionovou (a na te se to taky bude provozovat) tak jde vykon tech dotazu vetsinou do haje (jinak receno ani po 3 minutach nevraci vysledek = restartuji mysql) nebo se k subquery ani nepriblizuji (trvaji desitky sekund)...
Naposledy upravil Aleš Jiříček : 17.12.2011 v 12:10
jo ja ten explain kontroloval a prave ze tam neni nic spatne, podle explain by clovek dokonce rekl ze subquery proste musi byt pomalejsi, ale s mym prekvapenim to tak neni... jeste se nabizi potencialne spatne nastaveni mysql (nemam to nijak vyladene pro velke vykony) coz muze zpusobovat, ze se left join provadi pres tabulky z disku nebo co ja vim... ale porad tam visi otaznik proc subquery jede bez problemu a left join v podstate odstavi server (resp. zbaere si pro sebe cele jedno jadro procesoru), kazdopadne je to tak no... dotaz na 30ms je uplne v pohode, takze to tak hold necham :)
Takto používat group by je smrtící. Možná MySQL popisuje nějaké pravidlo, jak si domyslí, co si uživatel myslí, ale v normálním módu má nahlásit chybu. Zadání by mělo obsahovat celý script tabulky včetně indexů, i když tam indexy "jsou", je otázka, zda ty pravé.
Jak vlastně zní zadání?
zajímá nás a) posledních 15 dokumentů na kterých udělal někdo nějakou akci, nebo b) posledních 15 akcí na některých dokumentech?
a) select document_id,max(datum)as datum from tabulka group by document_id order by max(datum) desc limit 15
b) select document_idakce,max(datum)as datum from tabulka group by document_id,akce order by max(datum) desc limit 15
K tomuto základu se pak přidají zbylé informace pomocí JOIN (zásadně NE! LEFT JOIN)
Pokud jsou správné indexy, není online nic rychlejšího.
Pak zbývá otázka, jak moc jsou důležité tyto údaje, jestli to nestačí zjišťovat jednou za hodinu nebo za den...
informace se zjistuji presne jednou za dve sekundy... byo by vhodne mit dotaz maximalne optimalizovany, nicmene to uz jsem v podstate udelal, 30ms na neoc rpes milion zaznamove tabulce mi prijde dost v poradku
Zajima nas 15 poslednich akci na dokumentech, ovsem bez duplicit (pokud dva uzivatele udelaji stejnou akci na stejnem dokumentu, zobrazi se opuze akce toho kdo ji udelal posledni), v podstate to popisuje dotaz ktery jsem poslal hned v mem druhem prispevku - ten totiz vraci presne pozadovana data...
nicmene... tvuj dotaz trpi uplne stejnou veci - vraci prvni uzivatele co akci provedli, nikoliv posledni :) takze ej to nepouzitelne :) zustanu u moji verze, pokud tedy nemas navrh jak tvuj dotaz zmenit aby vracel poslendiho nikoliv prvniho uzivatele co akci provedl
martinuv left join sice vraic spravna data ale vykon toho dotazu dosahuje vykonu houpaciho kone
Ve svém příspěvku jsem především brojil proti nestandardnímu group by. Osobně to považuji za chybu stejného kalibru jako neinicializovaná proměnná.
ano, b) byl můj favorit, ale uživatele je třeba získat jinak:
select document_id,action,max(date)as datum,
(select max(user_id) from tabulka tt where tt.document_id=t.document_id and tt.action=t.action and tt.date=max(t.datum))as uzivatel
from tabulka t group by document_id,action order by max(datum) desc limit 15
Je to prakticky ekvivalent Vašeho příkazu, jen doufám v (zanedbatelně) rychlejší vykonání.
V teoretickém krizovém scénáři (uživatel zaznamená více stejných akcí nad dokumetem v jednom časově měřitelném intervalu) můžete získat duplicity - předpokládáte unique(document_id,action,date)
Touto poznámkou brojím proti používání datetime jako klíčového údaje
jj v dotazu jsem uz datetime vymenil za ID zaznamu vcera, protoze se datum nijak nemeni a ID tak koresponduje s nim, jen tam nejsou ty duplicity datetimu ...
jinak vas dotaz vraci data prakticky stejne rychle/pomalu (jak se to vezme) jako dotaz co jsem napsal nahore a po optimalizaci kterou delam to v podstate taky kolisa kolem 30ti milisekund, takze ono je asi jedno jaky pouziju :) kazdopadne diky za venovany cas, dnesa vidim cely problem ponekud cisteji :))