Nette\Database ignoruje řádky?

Upozornění: Tohle vlákno je hodně staré a informace nemusí být platné pro současné Nette.
thunderbuff
Člen | 164
+
0
-

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
+
0
-

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)

enumag
Člen | 2118
+
0
-

Tohle je dost zvláštní, myslím že na to dokáže kvalifikovaně odpovědět asi jen @hrach. Jakou verzi Nette používáte?

thunderbuff
Člen | 164
+
0
-

2.0.8

hrach
Člen | 1834
+
0
-

@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.
thunderbuff
Člen | 164
+
0
-

@hrach: Díky za radu :-)

ricco24
Člen | 141
+
0
-

@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)

hrach
Člen | 1834
+
0
-

select je navrhnuty na vybirani groupovanych hodnot

ricco24
Člen | 141
+
0
-
$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 | 1834
+
0
-

Čí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.

enumag
Člen | 2118
+
0
-

Kdo ho pochopí, ten už nic jiného nechce.

Svatá pravda. :-)

@hrach: Tak mne napadá, mohl by debug panel NDB upozorňovat na dotazy, které nepoužívají indexy? Možná by to šlo vyčíst z explain.

Editoval enumag (20. 1. 2013 22:55)

thorewi
Člen | 84
+
0
-

enumag +1 (upozornovat na nepouzivani indexu v debug panelu)

petr.pavel
Člen | 535
+
0
-

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.

enumag
Člen | 2118
+
0
-

@petr.pavel: Ty pohledy jsem zkoušel, ale ztroskotal jsem na tom, že DiscoveredReflection si s nima neporadí.