Opakující se SQL select a nekonkrétní výběr sloupců

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

Zdravím,

mám tento kód:

{foreach $rooms as $room}
  {foreach $room->related('reservation_room') as $reservation_room}
    <span class="names" n:inner-foreach="$reservation_room->related('reservation_room_guest') as $reservation_room_guest">
      {? $guest = $reservation_room_guest->guest}
      {$guest->surname}{sep}, {/sep}
    </span>
  {/foreach}
{/foreach}

a při každém průchodu se mi provede tento dotaz:

SELECT `id`, `surname`, `firstname`, `company_id`, `email`, `street`, `zip`, `city`, `country_id`
FROM `guest`
WHERE (`id` IN (51, 155, 58, 160, 103, 124, 187, 161, 84, 237, 209, 226, 128, 235, 246, 247, 236,
238, 239, 241, 242, 243, 240, 248, 249, 250, 245, 170, 252, 258, 259, 251, 262, 263, 260, 211, 144,
56, 142, 76, 97, 199, 64, 134, 89, 126, 147, 95, 120, 201, 140, 111, 203, 130, 60, 78, 93, 136, 223,
62, 72, 80, 91, 132, 205, 165, 54, 157, 99, 117, 163, 198, 87, 207, 232, 66, 173, 52, 70, 105, 224,
138, 68, 171, 149, 175, 268, 269, 270, 153, 271, 253, 214, 227, 261, 282, 283, 265, 264, 254, 255,
273, 278, 233, 234, 280, 286, 256, 288, 298, 275, 297, 219, 295, 299, 301, 300, 302, 303, 304, 305,
306, 307, 308, 190, 309, 310, 229, 312, 313, 316, 311, 315, 314, 317, 181, 318, 319, 296, 267, 179,
177, 218, 320, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 185, 244, 279, 281, 230, 257, 231,
276, 335, 291, 292, 337, 338, 339, 342, 343, 336, 351, 284, 350, 346, 345, 349, 285, 347, 228, 289,
354, 355, 340, 322, 348, 290, 352, 353, 294, 293))
  1. Neměl by se tento dotaz provést jen jednou?
  2. Něměl by se vybírat jen sloupec surname, když žádný jiný nepoužívám?

Mám nejnovější verzi Nette z Githubu (dnes stáhlou). Myslím ale, že se to dělalo i se staršími verzemi.

Editoval thorewi (24. 1. 2013 22:20)

hrach
Člen | 1838
+
0
-
  1. Ano. Tezko ale z toho rict, co se tam deje.
  2. Zrejme ne. Cache obsahuje prunik, pokud je k ni klic stejny, a ten zrejme v takto jednoduchem dotazu zavyslem jen na sloupci id asi stejny je. Doporucuji cilene tento dotaz optimalizovat ->select('...')
thorewi
Člen | 84
+
0
-
  1. No nefunguje mi to nikde, nejenom v tomto konkrétním případě, ale v jiných případech i úplně jiných projektech. Může mi někdo potvrdit, že když má v šabloně foreach ve foreachi a v druhém foreachi má related, tak se mu ten select provede jen jednou (s posledním verzí Nette z Githubu)?
thorewi
Člen | 84
+
0
-

Ok tak pridavam nove info:

U tohoto kodu se projevuji oba dvy vyse zminene problemy:

{foreach $rooms as $room}
    {foreach $room->related('reservation_room') as $reservation_room}
        {foreach $reservation_room->related('reservation_room_guest')->select('guest.surname') as $reservation_room_guest}
            {$reservation_room_guest->id}
        {/foreach}
    {/foreach}
{/foreach}

Pokud ale pridam (jak podotkl Hrach) ->select(‚guest.surname‘), oba dva problemy jsou vyreseny:

{foreach $rooms as $room}
    {foreach $room->related('reservation_room') as $reservation_room}
        {foreach $reservation_room->related('reservation_room_guest')->select('guest.surname') as $guest}
            {$guest->surname}
        {/foreach}
    {/foreach}
{/foreach}

Díky za radu.

thorewi
Člen | 84
+
0
-

Jen podotknu že to neni mozna uplne adekvatni reseni, protoze upraveny dotaz mi dela tento SQL select:

SELECT `reservation_room_guest`.`reservation_room_id`, `guest`.`surname`
FROM `reservation_room_guest`
LEFT JOIN `guest` ON `reservation_room_guest`.`guest_id` = `guest`.`id`
WHERE (`reservation_room_guest`.`reservation_room_id` IN (389, 390, 391, 392, 393, 394, 395, 396,
397, 398, 399, 400, 401, 402, 403, 404, 405, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417,
418, 419, 420, 421, 422, 423, 424, 425, 426, 427, 428, 429, 430, 431, 432, 433, 434, 436, 438, 439,
440, 441, 442, 443, 444, 445, 447, 448, 449, 450, 451, 453, 454, 462, 463, 464, 465, 466, 467, 478,
479, 480, 481, 482, 483, 484, 485, 486, 487, 488, 489, 490, 492, 493, 494, 495, 496, 497, 498, 499,
500, 502, 503, 504, 505, 506, 507, 508, 509, 510, 511, 512, 513, 514, 515, 516, 517, 518, 519, 520,
521, 522, 524, 525, 526, 527, 532, 533, 534, 535, 536, 537, 538, 540, 541, 542, 544, 546, 547, 549,
550, 551, 552, 553, 554, 555, 556, 557, 558, 559, 560, 561, 562, 563, 564, 565, 566, 567, 568, 569,
570, 571, 572, 573, 574, 575, 576, 577, 578, 579, 580, 581, 582, 583, 584, 585, 586, 587, 588, 589,
590, 591, 592, 593, 594, 595, 596, 597, 598, 599, 600, 601, 602, 604, 605, 606, 607, 608, 609, 610,
611, 612, 613, 614, 618, 619, 620, 621, 622, 623, 630, 631, 632, 633, 634, 635, 636, 638, 639, 642,
643, 644, 645, 646, 647, 648, 649, 650, 651, 652, 653, 654))

zatímco bych byl radějí za ten

SELECT `id`, `surname`, `firstname`, `company_id`, `email`, `street`, `zip`, `city`, `country_id`
FROM `guest`
WHERE (`id` IN (51, 155, 58, 160, 103, 124, 187, 161, 84, 237, 209, 226, 128, 235, 246, 247, 236,
238, 239, 241, 242, 243, 240, 248, 249, 250, 245, 170, 252, 258, 259, 251, 262, 263, 260, 211, 144,
56, 142, 76, 97, 199, 64, 134, 89, 126, 147, 95, 120, 201, 140, 111, 203, 130, 60, 78, 93, 136, 223,
62, 72, 80, 91, 132, 205, 165, 54, 157, 99, 117, 163, 198, 87, 207, 232, 66, 173, 52, 70, 105, 224,
138, 68, 171, 149, 175, 268, 269, 270, 153, 271, 253, 214, 227, 261, 282, 283, 265, 264, 254, 255,
273, 278, 233, 234, 280, 286, 256, 288, 298, 275, 297, 219, 295, 299, 301, 300, 302, 303, 304, 305,
306, 307, 308, 190, 309, 310, 229, 312, 313, 316, 311, 315, 314, 317, 181, 318, 319, 296, 267, 179,
177, 218, 320, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 185, 244, 279, 281, 230, 257, 231,
276, 335, 291, 292, 337, 338, 339, 342, 343, 336, 351, 284, 350, 346, 345, 349, 285, 347, 228, 289,
354, 355, 340, 322, 348, 290, 352, 353, 294, 293))

kdyby se ale provedl jen jednou :)

hrach
Člen | 1838
+
0
-

hmhm, chapu kde je problem, diky

thorewi
Člen | 84
+
0
-

Jeste se zeptam – bude se to nejak spravovat (v nejblizsi dobe)? Ja totiz ten prvni zpusob pouzivam ve vsech projektech a ptal jsem se ted kolegu a oni teda taky, tak jestli to mame vsude spravovat nebo mame cekat na fix (abychom to nemuseli za par dni zase vsechno vracet zpatky, abychom se vyhli LEFT JOINu)…

Dalsi vec je ta, ze ono totiz ne vsude to jde asi spravit. Pokud vyuzivam NiftyGrid, kde se jako zdroj dat predava Selection, ktery obcas ale nejde uplne vysperkovat, tak pak musim pro kazdy radek volat vlastni Selection, a to pak kdyz mam 100 polozek na stranku, tak misto 2 dotazu (jedno hlavniho a druheho groupnuteho do IN) mam 101 dotazu :/

Diky moc.

thorewi
Člen | 84
+
0
-

Tak jeste pripisu k tomuto tematu dalsi vec co jsem objevil protoze to s tim souvisi… Provedl jsem tedy opravu pomoci ->select(…) a mam tento kod:

$reservation_room->related('reservation_room_flag')->select('flag.id, flag.name, flag.suffix');

který mi provedl tento SQL dotaz:

SELECT `reservation_room_flag`.`reservation_room_id`, `flag`.`id`, `flag`.`name`, `flag`.`suffix`  FROM `reservation_room_flag`  LEFT JOIN `flag` ON `reservation_room_flag`.`flag_id` = `flag`.`id`  WHERE (`reservation_room_flag`.`reservation_room_id` IN (389, 390, ...))

SQL dotaz je naprosto spravny, ale pak kdyz jsem to tedy prohnal foreachem, tak mi tam chybeli data. Kdyz ale provedu:

$reservation_room->related('reservation_room_flag')->select('flag.id AS filename, flag.name, flag.suffix')

tak dostanu SQL dotaz:

SELECT `reservation_room_flag`.`reservation_room_id`, `flag`.`id` AS `filename`, `flag`.`name`, `flag`.`suffix`  FROM `reservation_room_flag`  LEFT JOIN `flag` ON `reservation_room_flag`.`flag_id` = `flag`.`id`  WHERE (`reservation_room_flag`.`reservation_room_id` IN (389, 390, ...))

a data mi nechybi… Oba SQL dotazy mi vraci stejny pocet vysledku, takze v nich to nebude – problem tedy bude nekde az pri zpracovani tech vysledku.

hrach
Člen | 1838
+
0
-
  1. S tim selectem jsi nepochopil, co sem chtel. Uvedenim selectu vypnes cache. Nechtel jsem join, ale toto:
{foreach $rooms as $room}
    {foreach $room->related('reservation_room') as $reservation_room}
        {foreach $reservation_room->related('reservation_room_guest')->select('reservation_room_id') as $rrg
            {$rrg->guest->surname}
        {/foreach}
    {/foreach}
{/foreach}
  1. nedari se mi nasimulovat, ze by se provadeli ve vicenasobnem related dalsi dotazy, muj testcase:
foreach ($connection->table('author') as $author) {
	echo "> $author->id<br>";
	foreach ($author->related('book') as $book) {
		echo ">> $book->id<br>";
		foreach ($book->related('book_tag') as $bookTag) {
			echo ">>> $bookTag->tag_id<br>";
			echo ">>>> $bookTag->tag->name<br>";
		}
	}
}

provede korektne jen 4 dotazy.

hrach
Člen | 1838
+
0
-

Ad 1) select, ja jsem to jeste cele zamotal, to o cem sem psal na vypnuti cache v tomto pripade nejde, protoze se k dane selection ani nedostanes. srr.

thorewi
Člen | 84
+
0
-

mam ted dost prace, ozvu se zacatkem unora a zkusime to rozlousknout…

hrach
Člen | 1838
+
0
-

Nejaky progress? Nebo mam zavrit issue?

thorewi
Člen | 84
+
0
-

Cau, omlouvam se, ozvu se o vikendu, zitra mame deadline jednoho projektu + pocitam s tim ze jeste v patek se bude na tom makat ale vikend uz by mel byt snad volnejsi tak dam dalsi echo.

hrach
Člen | 1838
+
0
-

V masteru je oprava bugu, ktery delal nejaky dotazy navic. Mozna je to tvuj pripad. Prosim zkoukni to. Jinak asi zavru ten issue.

michal.lohnisky
Člen | 64
+
0
-

Vyzkoušeli jsme, ale nic se nezměnilo, stále je zde mnoho selectů typu:

SELECT `id`, `surname`
FROM `guest`
WHERE (`id` IN (51, 155, ...

Používali jsme tento testovací příklad:

{foreach $rooms as $room}
	{foreach $room->related('reservation_room') as $reservation_room}
		{foreach $reservation_room->related('reservation_room_guest')->where('guest_id IS NOT NULL') as $reservation_room_guest}
			{var $guest = $reservation_room_guest->guest}
			{$guest->surname}
		{/foreach}
	{/foreach}
{/foreach}

Přikládám schéma databáze.

hrach
Člen | 1838
+
0
-

Prosim te, nemuzes prilozit schema databaze jako sql? Fakt se mi nechce prepisovat to z obrazku!

michal.lohnisky
Člen | 64
+
0
-

Jojo, napadlo mě to, už na to dělám. Ještě pár věcí pozkouším a pak to sem napíšu.

Editoval michal.lohnisky (18. 3. 2013 13:06)

michal.lohnisky
Člen | 64
+
0
-

Takže nakonec jsme došli ještě k jednomu zajímavemu zjištění: pokud v databázi promažu data a budu tam mít jen pár záznamů, vše funguje v pořádku; ale když použiješ tuto databázi, tak se generuje těch stejných dotazů více.

Tady máš kód do presenteru:

	public function renderDefault(){

		$connection = $this->context->database;

		foreach ($connection->table('room') as $room) {
			echo "> $room->id<br>";
			foreach ($room->related('reservation_room') as $reservation_room) {
				echo ">> $reservation_room->id<br>";
				foreach ($reservation_room->related('reservation_room_guest') as $reservation_room_guest) {
					echo ">>> $reservation_room_guest->id<br>";
					echo ">>>> {$reservation_room_guest->guest->surname}<br>";
				}
			}
		}
		$this->terminate();

	}
hrach
Člen | 1838
+
0
-

Opraveno v masteru. Diky moc za spolupraci. :)
https://github.com/…352c4e6f60ca

enumag
Člen | 2118
+
0
-

@hrach: Tohle by se asi dost špatně testovalo, ale osobně bych přidal alespoň krátký komentář do kódu proč se ty klíče porovnávají „takhle divně“, aby se v tom někdo jiný vyznal aniž by prováděl git blame.

hrach
Člen | 1838
+
0
-

No, je to vcelku logicke porovnani hodnot dvou poli. Alternativou je mozne tam dat sort. Ale to je od 2 radky vic, a asi ne rychlejsi.

http://stackoverflow.com/…-ignoring-th

enumag
Člen | 2118
+
0
-

@hrach: Na tom odkazu co jsi poslal je ještě tahle odpověď. Je tam popsané ve kterých případech bude pravděpodobně rychlejší než array_diff – zda je to případ NDB posuď raději ty.

Eda
Backer | 220
+
0
-

Hrachu, děkuju!
Tenhle bugfix znamenal v případě jedné z mých stránek skok z 250 queries na 25. Luxus! :-)