ugrás a tartalomhoz

Sebesség: count vagy új mező?

therest · 2011. Jún. 7. (K), 15.08
Sziasztok ismét!

Ha adott két táblám, az egyik usereket tartalmaz, a másik pedig a userek által birtokolt objektumokat. A második tábla uid oszlopon keresztül van kapcsolva az első táblához. A userek adminisztrációjánál, szükséges, hogy az adatbázisból való lekérdezés során a birtokolt objektumok számát is megkapjam. Mi itt az elegánsabb, és gyorasbb megoldás?

1. A user táblába felvinni, egy oszlopot, ahol tárolom az aktuális user által birtokolt elemek számát. Ezt az egyszerűbb megvalósítani, a lekérdezés, is gyors gondolom, hiszen elég egyetlen táblához hozzápiszkálni, ugyanakkor plusz adat minden sorban.

2. A lekérdezésben joinnal, és counttal megoldani. Nyilván ez lassabb, de nincs plusz adat. Milyen PHP-n keresztüli query-vel lehetne ezt szépen lekérni?

Köszi!
 
1

2 lekérdezés

solkprog · 2011. Jún. 7. (K), 15.14
valószínűleg join-t helyett két lekérdezés gyorsabb lenne (elsőben megszerezed az uid, másikban meg felhasználod). Én ezt a megoldást javaslom, és ha idővel azt veszed észre hogy az alkalmazásodnak ez az egyik szűk keresztmetszete, akkor tovább optimalizálsz rajta. (pl úgy ahogy mondtad felbontod a normálformát egy új oszloppal)
2

A következőt írtam

therest · 2011. Jún. 7. (K), 15.23
A következőt írtam ehhez:

$query="SELECT u.*, COUNT(ou.id) AS uoCount FROM `users` AS u
 LEFT JOIN `userobjects` AS ou ON ou.uid=u.id 
GROUP BY u.id";
Mennyire jó megközelítése ez a joinos verziónak?
Mennyivel lehet gyorsabb a két lekérdezéssel?
3

Miért nem méred le a

Hidvégi Gábor · 2011. Jún. 7. (K), 16.00
Miért nem méred le a lekérdezések sebességét? Akkor megtudod, mi a különbség. Egyébként, ha jól van indexelve a két tábla, akkor szerintem gyorsabb egy join, mint két külön lekérdezés, mert ez utóbbi esetben vissza kell küldeni a php-nak, ott új lekérdezést kell indítani, és hát a php nem a sebességéről híres.

$ido1 = microtime(true);
$eredmeny = mysql_query($sql);
print microtime(true) - $ido1;
4

Köszi az eddigieket, közben

therest · 2011. Jún. 7. (K), 16.55
Köszi az eddigieket, közben felmerült egy újabb kérdés.
Ha a joinos változatot használom, akkor a WHERE tudok hivatkozni valahogy az elemszámra?

A fenti lekérdezésnél uoCount a userhez kapcsolódó elemek száma a rekordban, de WHERE ágban, hogyan tudnék erre az adatra hivatkozni?

Ha például csak a tíznél több objektumot birtokló usereket akarom lekérdezni?
5

Ha a joinos változatot

kuka · 2011. Jún. 7. (K), 17.03
Ha a joinos változatot használom, akkor a WHERE tudok hivatkozni valahogy az elemszámra?
Sehogy. Az a having feltételbe kell kerüljön.
6

Indexek + HAVING

vbence · 2011. Jún. 7. (K), 17.10
Azt egy szó nélkül biztosra veszem, hogy az objektumok tábládban a user_id vagy hasonló mezőre létezik index.

Ebben az esetben a MySQL az adatok érintése nélkül meg tud válaszolni különböző aggregált függvényeket. Az egyes indexhez tartozó elemszám (vagyis COUNT) pedig nyilvánvalóan tárolva van a metaadatban, úgyhogy ennek a sebessége miatt ne aggódj.

Ha szűrni szeretnél aggregált kifejezésre, akkor a HAVING lesz a barátod. Próbáld elkéerülni ha nem feltétlenül szükséges. Pl listázzuk azokat a usereket akikhez tíznél több objektum tartozik.
SELECT u.name AS name,
    COUNT(o.user_id) AS num
FROM user AS u
    LEFT JOIN object AS o ON o.user_id=u.id
GROUP BY u.id
HAVING num > 10
Ellenőrző kérdés: miért o.user_id-ket számoltuk, miért nem mondjuk u.id-ket?
7

Ezért szeretek itt kérdezni.

therest · 2011. Jún. 8. (Sze), 17.24
Ezért szeretek itt kérdezni. :) Kapok tömör lényegre törő válaszokat mint kukáé, és bővebb infót is, mint Bence hozzászólása.

Egyébként a query stringet mindig az alapján állítom össze, hogy milyen szűrési adatok szükségesek, szóval csak akkor kerül be valami, ha tényleg szükség van rá, így a having sem része alapértelmezésben.

Az objektumok táblában egyenlőre nincs index a uid oszlopon. De vbence kommentje enyhén azt sugallja, hogy ajánlott hogy legyen. :)

Ellenőrző kérdés: miért o.user_id-ket számoltuk, miért nem mondjuk u.id-ket? <-ezt nem nagyon értem, nyilván azért nem az u.id-t mert nem a userek száma kell, hanem az birtokolt objektumok száma. Ezért amit írtál példa az nem azt csinálja ami nekem kell, mert ha az o.uid-t számolom, az egy lesz mindig (mivel egy adott objektum, csak egy userhez tartozhat).
8

Nem egészen

vbence · 2011. Jún. 9. (Cs), 09.49
GROUP BY után a COUNT azt a számot adja hány (nem null) érték van az adott mezőben az adott groupban. Vagyis alapjában véve bármelyik mezőre meenthe a count. A fontos szempont itt a JOIN működése, vagyis hogy az eredménytáblában szerepelni fognak azok a userek is, akikhez egyáltalán nem tartozik rekord a második táblában. (Ebben az esetben null értékkel lesznek a második táblából származó értékek feltöltve).

Ha nem joint használunk hanem egyszerű where-t, akkor nem áll fenn ez a probléma vagyis a következő két lekérdezés ugyanazt az eredményt adja:
SELECT u.name AS name,  
    COUNT(o.user_id) AS num  
FROM user AS u,  
    object AS o
WHERE
    o.user_id = u.id
GROUP BY u.id  
HAVING num > 10
SELECT u.name AS name,  
    COUNT(u.id) AS num  
FROM user AS u,  
    object AS o
WHERE
    o.user_id = u.id
GROUP BY u.id  
HAVING num > 10