Výpis neduplicitních hodnot z databáze

- ForestCZE
- Člen | 209
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
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
uestla napsal(a):
Ahoj,
metoda
getTournamentAchievements()ti vrací instanciNette\Database\Table\Selection?
Zní to, jako bys potřebovalGROUP 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.

- David Matějka
- Moderator | 6445
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
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
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
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
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
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>
|
<a class="link" n:href="User:team id => $id, tab => $tab, page => $page-1">Předchozí</a>
|
{/if}
Strana {$page} z {$lastPage == 0 ? 1 : $lastPage}
{if $page < $lastPage}
|
<a class="link" n:href="User:team id => $id, tab => $tab, page => $page+1">Další</a>
|
<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
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
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
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
@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í?
- Mám se z
tournament_achievements_usersodkázat natournament_achievementsa ztournament_achievementsnatournaments? Pokud ano, jak?
nebo
- Mám do
tournament_achievements_userspřidat sloupec, kde bude id tournamentu a rovnou z této tabulky se odkázat natournaments?
Editoval ForestCZE (12. 2. 2021 16:14)

- David Matějka
- Moderator | 6445
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)