Výpis neduplicitních hodnot z databáze

ForestCZE
Člen | 209
+
0
-

Ahoj, mám tuto metodu:

public function getTournamentAchievements(int $team): array
{
	return $this->database->getTournamentAchievements()->where('team_id', $team)->fetchAll();
}

Potřebuji aby to nevypisovalo stejné hodnoty. Dle dokumentace se má použít tohle:

$this->database->getTournamentAchievements()->where('team_id', $team)->count("DISTINCT tournament_id")->fetchAll()

to ale vrací počet odlišných hodnot, což nechci, potřebuju ty odlišné hodnoty vrátit.

Pak mě ještě napadlo použít select:

$this->database->getTournamentAchievements()->where('team_id', $team)->select("DISTINCT tournament_id")->fetchAll()

tam je ale zase problém, že to nevytáhne ostatní sloupce a musel bych je vypsat všechny. Jak to má být správně? Děkuji předem za pomoc.

Editoval ForestCZE (2. 2. 2021 17:40)

uestla
Backer | 799
+
0
-

Ahoj,

metoda getTournamentAchievements() ti vrací instanci Nette\Database\Table\Selection?
Zní to, jako bys potřeboval GROUP BY, což se u Selection zapíše takto:

$this->database->getTournamentAchievements()
	->where('team_id', $team)
	->group('tournament_id, sloupec_2, sloupec_3, ...')
	->fetchAll();
ForestCZE
Člen | 209
+
0
-

uestla napsal(a):

Ahoj,

metoda getTournamentAchievements() ti vrací instanci Nette\Database\Table\Selection?
Zní to, jako bys potřeboval GROUP BY, což se u Selection zapíše takto:

$this->database->getTournamentAchievements()
	->where('team_id', $team)
	->group('tournament_id, sloupec_2, sloupec_3, ...')
	->fetchAll();

Nejsem si jistý, zda GROUP BY je to, co potřebuju. Výsledek zde.

uestla
Backer | 799
+
0
-

V klauzuli GROUP BY by měly právě figurovat všechny sloupce, které chceš mít ve výsledku jako unikátní (podle screenshotu tam máš jen tournament_id).

CZechBoY
Člen | 3608
+
0
-

Pripadne pouzit select distinct

ForestCZE
Člen | 209
+
0
-

CZechBoY napsal(a):

Pripadne pouzit select distinct

To jsem zkoušel

$this->database->getTournamentAchievements()->where('team_id', $team)->select("DISTINCT tournament_id")->fetchAll()

ale vytáhne to pouze sloupec tournament_id. To je musím všechny vypsat?

David Matějka
Moderator | 6445
+
+1
-

imho mysql nepodporuje distinct on jako třeba postgres, ale jen distinct na úrovni celého řádku. Takže řešením je použít to group by


edit:

i když tady je to možná fakt ten případ celého řádku, takže distinct s vyjmenováním všech sloupců to řeší :)

ForestCZE
Člen | 209
+
0
-

Nějak to pořád nemohu doladit k dokonalosti.

Mám tuto metodu:

public function getTournamentAchievements(int $team): array
{
	return $this->database->getTournamentAchievements()->where('team_id', $team)->select('DISTINCT tournament_id, position, points')->fetchAll();
}

Tabulka vypadá takto. Potřebuju filtrovat podle sloupce team_id a vypsat jen první záznam od každého podle tournament_id(dle screenu 1. a 3. záznam), proto DISTINCT. Zároveň ale potřebuji selectnout i id, abych s tím záznamem mohl pracovat. Jakmile ale id přidám do selectu, tak opět vidím všechny záznamy. Jak na to? Díky.

Editoval ForestCZE (9. 2. 2021 11:17)

David Matějka
Moderator | 6445
+
0
-

hm, v tom pripade by se ti hodilo to distinct on. v mysql se to musi hackovat treba takto se subquery

ale pak je jeste otazka, jestli mas spravne navrzenou strukturu databaze. proc je v te tabulce vice zaznamu, kdyz te zajima jen jeden?

ForestCZE
Člen | 209
+
0
-

David Matějka napsal(a):

hm, v tom pripade by se ti hodilo to distinct on. v mysql se to musi hackovat treba takto se subquery

ale pak je jeste otazka, jestli mas spravne navrzenou strukturu databaze. proc je v te tabulce vice zaznamu, kdyz te zajima jen jeden?

Protože v tomhle momentu se snažím vypsat úspěchy celého týmu, ale později budu chtít vypsat úspěchy uživatele na jeho profilu, kde mě budou zajímat všechny dle user_id. Je to špatně?

Editoval ForestCZE (9. 2. 2021 11:34)

David Matějka
Moderator | 6445
+
0
-

a ty uspechy celeho tymu jsou stejne pro vsechny ty uzivatele? jestli jo, tak bys v tehle tabulce nemel mit ty uzivatele, ale jen team, tournament, points, position. a pak mit spojovaci (m:n) tabulku mezi touhle a uzivatelema

ForestCZE
Člen | 209
+
0
-

David Matějka napsal(a):

a ty uspechy celeho tymu jsou stejne pro vsechny ty uzivatele? jestli jo, tak bys v tehle tabulce nemel mit ty uzivatele, ale jen team, tournament, points, position. a pak mit spojovaci (m:n) tabulku mezi touhle a uzivatelema

Nakonec se mi podařilo rozjet GROUP BY, takže Nette samo vyselectuje, co potřebuji :)

Nyní však nastane problém, když se snažím přidat stránkování.

Metoda v modelu:

public function getTournamentAchievementsByTeam(int $team): Selection
{
	return $this->database->getTournamentAchievements()->where('team_id', $team)->group('tournament_id')->order('id DESC');
}

Presenter:

private $tournamentAchievements;
private $achievementsLastPage = 0;

public function actionTeam($id, $tab, $page = 1): void
{
	$this->tournamentAchievements = $this->userManager->getTournamentAchievementsByTeam(intval($id))->page($page, 1, $this->achievementsLastPage);
}

public function renderTeam($id, $tab, $page = 1): void
{
	$this->template->tournamentAchievements = $this->tournamentAchievements;
    $this->template->id = $id;
    $this->template->tab = $tab;
    $this->template->page = $page;
    $this->template->lastPage = $this->achievementsLastPage;
}

Latte:

{foreach $tournamentAchievements as $tournamentAchievement}
	//výpis dat
{/foreach}

// Stránky

{if $page > 1}
    <a class="link" n:href="User:team id => $id, tab => $tab, page => 1">První</a>
    &nbsp;|&nbsp;
    <a class="link" n:href="User:team id => $id, tab => $tab, page => $page-1">Předchozí</a>
    &nbsp;|&nbsp;
{/if}

Strana {$page} z {$lastPage == 0 ? 1 : $lastPage}

{if $page < $lastPage}
    &nbsp;|&nbsp;
    <a class="link" n:href="User:team id => $id, tab => $tab, page => $page+1">Další</a>
    &nbsp;|&nbsp;
    <a class="link" n:href="User:team id => $id, tab => $tab, page => $lastPage">Poslední</a>
{/if}

V databázi jsou 4 záznamy, z toho se vypíšou dva dle GROUP BY. Na test jsem dal, že chci jeden záznam na stránku. To znamená, že bych měl mít dvě stránky po jednom záznamu. Jenže mně to ukazuje, že jsou ty stránky 4 jako by to nebralo to GROUP a na dvou stránkách jsou ty záznamy, které tam mají být a na dalších dvou stránkách není žádný záznam. Co dělám špatně? Díky.

Editoval ForestCZE (10. 2. 2021 4:39)

ForestCZE
Člen | 209
+
0
-

Stále je to nevyřešeno. Pokud se zbavím stránkovacího systému, funguje to normálně. Ať se na to dívám jakkoliv ze všech stran, nevidím chybu. Jde tam pouze o jednu metodu page volanou na Selection z DB a o její parametry. Vnitřně řeší vše oproti původnímu použití objektu Paginator ve starších verzích Nette. Netuším, co může být špatně.

Editoval ForestCZE (12. 2. 2021 7:15)

David Matějka
Moderator | 6445
+
+2
-

Ano, strankovani s query, ktery ma group by fungovat nebude. Jedine treba pres sub-query, coz myslim pres ndb explorer neudelas.
Problem by vyresila ta uprava databaze, jak jsem navrhoval.

V tournament_achievement budes mit sloupecky id, tournament_id, team_id, position a points a user budes mit v tournament_achievement_user, kde budes mit jen tournament_achievement_id a user_id

ForestCZE
Člen | 209
+
0
-

David Matějka napsal(a):

Ano, strankovani s query, ktery ma group by fungovat nebude. Jedine treba pres sub-query, coz myslim pres ndb explorer neudelas.
Problem by vyresila ta uprava databaze, jak jsem navrhoval.

V tournament_achievement budes mit sloupecky id, tournament_id, team_id, position a points a user budes mit v tournament_achievement_user, kde budes mit jen tournament_achievement_id a user_id

Až teď rozumím tomu, jak jsi to myslel. Zkusím to a případně ještě napíšu. Díky :)

ForestCZE
Člen | 209
+
0
-

@DavidMatějka

Mám teda tabulky tournaments, tournament_achievements a tournament_achievements_users

Vytáhnu si všechny úspěchy toho uživatele z tabulky tournament_achievements_users

public function getTournamentAchievementsByUser(int $userID): Selection
{
	return $this->database->getTournamentAchievementsUsers()->where('user_id', $userID)->order('id DESC');
}

Poté výpis v šabloně… Abych mohl vypsat body a umístění, tak se odkazuji na tabulku tournament_achievements podle sloupce tournament_achievement

{foreach $tournamentUserAchievements as $tournamentUserAchievement}
	<tr>
		<td>{* Zde potřebuji vypsat název turnaje *}</td>
		<td>
		{switch $tournamentUserAchievement->ref('tournament_achievements', 'tournament_achievement')->position} {* Reference *}
		{case 1}
		1. <i class="fas fa-trophy gold"></i>
		{case 2}
		2. <i class="fas fa-trophy silver"></i>
		{case 3}
		3. <i class="fas fa-trophy bronz"></i>
		{/switch}
		</td>
		<td>{$tournamentUserAchievement->ref('tournament_achievements', 'tournament_achievement')->points}</td> {* Reference *}
		<td>-</td>
	</tr>
{/foreach}

Jenom si nejsem jistý, jak to je, když potřebuji vypsat i název turnaje. Jaké je správné řešení?

  1. Mám se z tournament_achievements_users odkázat na tournament_achievements a z tournament_achievements na tournaments? Pokud ano, jak?

nebo

  1. Mám do tournament_achievements_users přidat sloupec, kde bude id tournamentu a rovnou z této tabulky se odkázat na tournaments?

Editoval ForestCZE (12. 2. 2021 16:14)

David Matějka
Moderator | 6445
+
+1
-

Mám do tournament_achievements_users přidat sloupec, kde bude id tournamentu a rovnou z této tabulky se odkázat na tournaments?

ne, tim by sis rozbil normalizaci databaze

Mám se z tournament_achievements_users odkázat na tournament_achievements a z tournament_achievements na tournaments? Pokud ano, jak?

v podstate ano, na achievementu zavolas ->ref(...), respektive jen ->tournament a pokud mas spravne fk, tak se spojovaci klice a cilova tabulka dohleda. Stejne tak muzes pri spravnych FK nahradit konstrukci

$tournamentUserAchievement->ref('tournament_achievements', 'tournament_achievement')->position

za

$tournamentUserAchievement->tournament_achievement->position

ALE

bude lepsi, kdyz vubec nebudes selectovat z te tournament_achievements_users, ale pouze ji pouzijes pro filtrovani. zhruba nasledovne

$db->table('tournament_achievements')->where(':tournament_achievements_users.user_id', $userId)