Nette\Database ignoruje řádky?
- thunderbuff
- Člen | 164
Mám zajímavý problém se selection.
<?php
$selection = $this->connection()->table($this->getTableName())
->select(
self::CONTACTLISTS_PAIR_TABLE . '.status, ' .
$this->getTableName() . '.*'
)
->where(self::CONTACTLISTS_PAIR_TABLE . ":contactlist_id", $ids);
?>
Laděnka mi vypíše, že to vytvoří query:
<script>
SELECT `contact_contactlist`.`status`, `contact`.*
FROM `contact`
INNER JOIN `contact_contactlist` ON `contact`.`id` = `contact_contactlist`.`contact_id`
WHERE (`contact_contactlist`.`contactlist_id` IN (2, 3, 4, 6, 8))
</script>
To je správně. Pokud tu query vezmu a hodím do admineru, dostanu 2838 řádek. To je také správně.
Při volání $selection->count(‚*‘) dostanu také 2838 řádek.
ALE:
<?php
$counter = 0;
foreach ($selection as $activeRow) {
$counter++;
}
echo $counter;
// a nebo
$counter = 0;
while ($activeRow = $selection->fetch()) {
$counter++;
}
echo $counter;
?>
Vrátí oboje 946 řádek. Tušíte, kde je problém? Ty řádky jsou z části duplicitní, mají společnou hodnotu v některých sloupcích, ale žádné dva z nich nejsou úplně stejné. V selection ty „skoro-duplicitní“ řádky zmizí.
- ricco24
- Člen | 141
Presne na rovnaký problém som aktuálne narazil tiež. Keďže mi pripadalo že Nette\Database pokladá veľa dotazov (nevytvára join – viz. vlákno) chcel som to obísť nasledovným selectom:
$this->template->myCasesAttention = $this->context->caseRecord->findAll()
->where('owner_id', $this->getUser()->getId())
->where('case_status_id = ? OR modified < ?', 1, $now->modify('-5 days'))
->select('*, case_status.name AS case_status_name, case_priority.name AS case_priority_name, case_type.name AS case_type_name');
Vďaka čomu som chcel ušetriť 3 db dotazy. Keď zavolám na výsledok selectu ->count() vráti sa mi správne 11. Avšak keď iterujem cez výsledok pomocou foreach iterácia prebehne len cez 2 riadky.
Editoval ricco24 (20. 1. 2013 15:30)
- hrach
- Člen | 1838
@thunderbuff:
- Jasny, to je proste proto, ze to jakoby spatne pouzivas.
- Selection umi vybirat data jen z jedne tabulky. To ze
nahodou to jde „ohnout“ a pridat neco jako
select('dalsitabulka.id')
je sice fakt, ale vysledny resultset je grupovan dle primarniho klice primarni tabulky. - Volani
count(*)
spousti dalsi dotaz, ktery to pocita v db, tzn. jeste nezgroupovane. - Spravne pouziti bude neco jako
$contacts = $connection->table('contact')->where('contact_contactlist:contactlist_id', $ids);
foreach ($contacts as $contact) {
echo $contact->name;
foreach ($contact->related('contact_contactlist') as $clist) {
echo $clist->status;
}
}
- Takoveto reseni bude i prekvapive rychle, protoze prvni dotaz nesaha do datoveho souboru, ale pouze do indexu druhe tabulky.
@ricco24
- Snad jsi z vyse uvedeneho pochopil, proc to tak je.
- Tvuj priklad sem vlozeny je koncepcne pro Nette\Database spatne, zminovane chovani na odkazovenm vlakne je v poradku.
- ricco24
- Člen | 141
@hrach:
Tým že ten príklad je pre Nette\Database koncepčne zle myslíš využívanie
toho ->select() ? Čiže koncepčne je select navrhnutý len na oklieštenie
výberu stĺpcov z primárnej tabuľky ?
hrach napsal(a):
- Selection umi vybirat data jen z jedne tabulky. To ze nahodou to jde „ohnout“ a pridat neco jako
select('dalsitabulka.id')
je sice fakt, ale vysledny resultset je grupovan dle primarniho klice primarni tabulky.
Ak sa to výsledok grupuje podľa primárneho kľúča primárnej tabuľky (ktorý je unikátny) tak by som mal vo výsledku predsa dostať všetky riadky primárnej tabuľky nie ?
Editoval ricco24 (20. 1. 2013 19:23)
- ricco24
- Člen | 141
$this->template->myCasesAttention = $this->context->caseRecord->findAll()
->where('owner_id', $this->getUser()->getId())
->where('case_status_id = ? OR modified < ?', 1, $now->modify('-5 days'))
->select('*, case_record.id, case_status.name AS case_status_name, case_priority.name AS case_priority_name, case_type.name AS case_type_name');
Týmto spôsobom sa budú výsledky zobrazovať korektne aj pri iterácii. Stačí ak sa vytiahne primárny kľúč primárnej tabuľky do select-u.
@hrach:
Ako píšeš je to ohnutie funkčnosti Selection no pri výpisoch kde robím
viacej relačných volaní na databázu ušetrím značný počet db
dotazov.
Nevýhoda je že v budúcnosti sa môže správanie selection v tomto ohlade zmeniť.
Editoval ricco24 (20. 1. 2013 20:40)
- hrach
- Člen | 1838
Čím komplexnější join, tím větší čas na získání dat. Malé, jednotabulkové dotazy na indexované sloupce jsou rychlé. Včetně joinu, pokud probíhá přes index. Pokud ovšem v joinu sahám na data další tabulky, už se zpomaluje a radikálně se zvyšuje pravdědobnost potřeby dalších optimalizací, aby neprobíhal filesort, temporary table, atp.
Očividně sháníš nějakej stavěč dotazu. Radím ti, nech pak Nette\Database raděj být. Nesplní očekávání a akorát ti pak bude házet klacky pod nohy. Nette\Database je prostě na jiný způsob práce. Kdo ho pochopí, ten už nic jiného nechce.
- petr.pavel
- Člen | 535
Mám dojem, že u malého počtu řádků se indexy při joinu (MySQL) nepoužijí, i když jsou správně definované. Protože serveru nestojí za to se s nimi párat.
Ještě bych doplnil, že když už fakt potřebuji pomocí NDB joinovat něco složitějšího, tak předem v databázi definuji pohled a NDB pouštím na něj.