Autor Zpráva
BunnyBugs
Profil *
Zdravím,
potřeboval bych poradit s následujícím. Potřeboval bych vypsat z DB data, aby vypadala nějak takhle:



V DB mám vytvořeny tyto tabulky a pole (uvádím jen ta důležitá dle obrázku):
1) tabulka akce = id, jmeno, vzdalenost, ...
2) tabulka uzivatele = id, jmeno, ...
3) tabulka prehled = (toto je tabulka spojující ty dvě předchozí, tedy zúčastněné uživatele na jednotlivých akcích)

Vůbec nevím, jakým dotazem z DB data vytáhnout a jakým způsobem je pak vypsat do té podoby dle obrázku. Výsledné řazení by mělo být od nejvyššího součtu vzdálenosti, resp. sloupec km.
Hledal jsem tady dlouho a hodně toho přečetl, ale mám z toho trochu zamotanou hlavu a potřeboval bych to dostat do co nejjednoduššího tvaru.

Předem díky za všechny rady.
Alphard
Profil
Kdyby to mělo být za každou cenu na úrovni databáze (hádám MySQL), hledejte „pivot table“. Ale zvlášť pro tak velký dynamický počet sloupců bych se do toho nepouštěl.

1. Jednodušší varianta s ukládáním do pole
Vytáhněte si ta data samostatně (jen ta potřebná) a uložte do pole. Důležité je dát data z tabulky prehled do dvourozměrného pole ve tvaru $summary[$idUser][$idAction] = true (false tam nebude, není ani v databázi).
Výpis pak přes zanořený cyklus
foreach ($users as $user) {
  foreach ($actions a $action) {
    if (isset($summary[$user->id][$action->id])) {
      echo $action->distance;
    }
  }
}

2. složitější varianta bez ukládání
Trochu složitější varianta by byla bez ukládání do pole. Data z tabulky prehled by musela být seřazena tak, aby odpovídala výpisu v cyklu (order by user-distance, action-date) a místo ověřování existence prvku v poli by se kontrolovalo, jestli aktuální prvek ve frontě odpovídá aktuálnímu poli.
// foreach viz výše
if ($current->user == $user->id && $current->action == $action->id) {
  echo $action->distance;
  $current = $summary->queue->fetch();
}
BunnyBugs
Profil *
Jsem asi natvrdlý, nebo jsem to nějak nepobral :(
Nešlo by ta jednodušší varianta prosím vypsat v kompletním kódu i s výběrem z DB (dosazení do těch proměnných)?, nevím totiž vůbec, kam co dosadit :(
Díky moc.
Alphard
Profil
[#3] BunnyBugs
Výběr z databáze psát nebudu, to je jednoduché. Celý algoritmus je jen o tom uvědomit si, jak se jednotlivé buňky skládají.

Vytvoříme si akce
$actions = [
  0 => ['Akce 1', 100],
  1 => ['Akce 2', 50],
  2 => ['Akce 3', 70],
];

podobně uživatele
$users = [
  0 => ['Hana'],
  1 => ['Tereza'],
  2 => ['Zuzana'],
];

a na závěr mapu, kdo se účastnil které akce
$summary = [];
$summary[0][0] = true; // Hana se účastnila všech
$summary[0][1] = true;
$summary[0][2] = true;
$summary[1][0] = true; // Tereza jen první a druhé
$summary[1][1] = true;
$summary[2][1] = true; // Zuzanadruhé a třetí
$summary[2][2] = true;

A teď to stačí vypsat
// tohle je jen pomůcka pro zarovnání textu na určitou délku, jestli tomu nerozumíte, není to zásadní
$length = 8;
$formatCell = function($str) use ($length) { return str_pad($str, $length, ' ', STR_PAD_LEFT); };

// Záhlaví tabulky
echo '| '.$formatCell('').' | ';
foreach ($actions as $action) {
  echo $formatCell($action[0]);
  echo ' |';
}
echo PHP_EOL;

foreach ($users as $idUser => $user) {
  // Jména uživatelů
  echo '| '.$formatCell($user[0]).' | ';

  // Vypsání kartézského součinu do tabulky
  foreach ($actions as $idAction => $action) {
    if (isset($summary[$idUser][$idAction])) {
      echo $formatCell($action[1]);
    } else {
      echo $formatCell('-');
    }
    echo ' |';
  }
  echo PHP_EOL;
}

Výsledek
|          |   Akce 1 |  Akce 2 |  Akce 3 |
|     Hana |      100 |      50 |      70 |
|   Tereza |      100 |      50 |       - |
|   Zuzana |        - |      50 |      70 |

Já to píši takhle, abych to mohl spouštět v konzoli, vy si zřejmě moji tabulku nahradíte za html.
BunnyBugs
Profil *
Jsem z toho trochu jelen :( Chápu to tak, že proměnné $actions, $users, $summary musím vytvořit ručně, nebo to lze vygenerovat příkazem while?
Něco jako:
$vysledek = mysql_query("SELECT `akce`, `jmeno`, `km` FROM `tabulka` WHERE `sezona` = '$vyberSezonu'");
        while ($rowvysledek = mysql_fetch_array($vysledek)) {
            $actions[] = $rowvysledek['akce'];
            $users[] = $rowvysledek['jmeno'];
            }
Pak netuším, jak vytvořit proměnnou $summary
nightfish
Profil
BunnyBugs:
$actions = array();
$users = array();
$summary = array();
$vysledek = mysql_query("SELECT `akce`, `jmeno`, `km` FROM `tabulka` WHERE `sezona` = '$vyberSezonu'");
while ($row = mysql_fetch_array($vysledek)) {
  $actions[] = $row['akce'];
  $users[] = $row['jmeno'];
  $summary[$row['jmeno']][$row['akce']] = $row['km'];
}
$actions = array_unique($actions); // aby každá akce byla v poli jenom jednou
$users = array_unique($users); // aby každý člověk byl v poli jenom jednou

echo "<table>";
echo "<tr><th>Jméno \ Akce</th>";
foreach ($actions as $a) echo "<th>", $a, "</th>";
echo "</tr>";
foreach ($users as $u) {
  echo "<tr><th>", $u, "</th>";
  foreach ($actions as $a) {
    echo "<td>";
    if (isset($summary[$u][$a]))
      echo $summary[$u][$a];
    else
      echo "-";
    echo "</td>";
  }
  echo "</tr>";
}
echo "</table>";
Kcko
Profil
nightfish:
Drobná optimalizace ;-)

  $actions[$row['akce']] = TRUE ;
  $users[$row['jmeno']] = TRUE;

Pak ten unique nebude potřeba a bude to rychlejší o fous.
Alphard
Profil
Jen pro doplnění, moje původní představa, když jsem psal [#4], bylo použití 3 jednoduchých selectů (s ohledem na http://php.vrana.cz/srovnani-dotazu-do-zavislych-tabulek.php, join není vždy lepší řešení).
Řešení [#6] předpokládá join na úrovni databáze, protože tabulka `tabulka` v této podobě neexistuje (dle [#1], což je podle mě dobrý návrh).
BunnyBugs
Profil *
nightfish:
Jojo, takhle je to celkem logické, ale jak v posledním příspěvku píše Alphard, `tabulka` v tomto formátu neexistuje. Mám DB navrhnutou jako N:N, kde `tabulka` je pouze jen spojení jiných tabulek, která obsahuje jen `id_jmeno` z tabulky `uzivatele`, `id_akce` z tabulky `akce` a `km` by mělo být načítáno z tabulky `mista` dle `id_mista`.
Mělo by tedy jít nějak zkombinovat volání tří SELECT, jak uvádí Alphard.
Dle vypsaného kódu podle nightfish bych musel do slučovací tabulky přepisovat údaje z ostatních tabulek, což absolutně ztrácí význam návrhu správné DB, nebo alespoň tak bych to chápal.


Abych to zjednodušil a zpřehlednil, tak uvedu jednotlivé tabulky s potřebnými hodnotami:

1) Je pro registrované uživatele s názvem `uzivatele` a pole `id`, `jmeno`, ...
2) Je pro místa akcí s názvem `mista` a pole `id`, `nazev`, `km`, ...
3) Se už vytváří na jednotlivé akce i pomocí tabulky `mista`, její název je `akce` a pole `id`, `id_misto`, `datum_konani`, ...
4) A poslední tabulka je pak závislá na všech, kde jsou pak jednotliví přihlášení k daným akcím s názvem `prehled` a pole `id`, `id_akce`, `id_uzivatel`, ...

A z takto navržené DB bych právě potřeboval dostat výsledek, jak uvádím v příspěvku [#1] BunnyBugs
Nevím, jestli mám DB navrženu dobře, ale takhle jsem to ze všelijakých doporučení vyčetl, že by to nějak takhle mělo být.
Alphard
Profil
Moje odpověď [#4] je kompletní a ten kód by měl být spustitelný.

Ty 3 selecty na naplnení polí stačí v té nejjednodušší možné podobě:
$actions = [];
$actionsSelect = $db->query('select * from actions where sezona = xxx order by yyy');
while ($action = $actionsSelect->fetch()) {
  $actions[] = $action;
}

Podobně users a summary takhle:
$summary = [];
while ($s = $summarySelect->fetch()) {
  $summary [$s->idUser][$s->idAction] = true;
}
BunnyBugs
Profil *
Alphard:
Asi jsem fakt jouda, nefunguje mi to :(
$db->query a ->fetch nefungují, asi jiná databáze, já používám MySQL.
Pokud toto nahradím mysql_query a mysql_fetch_array, vypíše mi to chybu v $sumary ... = true; jako neznámou.

Asi to budu muset dělat pořád ručně, protože to nezmáknu.
Funguje mi varianta od nightfish, ale to bych musel zase ty hodnoty nacpat do jedné tabulky a navíc mi to nevrací ten první sloupec, kde bych potřeboval celkový součet km.
Alphard
Profil
BunnyBugs:
Podstatné jsou algoritmy, když používáš jinou knihovnu, neber to doslova. Přepsat to pro mysql extenzi samozřejmě možné je. Nevím, jaká chyba tam vznikla, pojmenování proměnných je třeba uzpůsobit konkrétní situaci.
BunnyBugs
Profil *
Tak jsem to tedy definitivně vyřešil takhle (dle nightfish):

$actions = array();
$users = array();
$summary = array();
$result = mysql_query("SELECT `dat_zapasu`, `jmeno`, `vzdalenost`, `zkratka` ".
"FROM `vyjezdy`, `prehled`, `tymy` ".
"WHERE vyjezdy.ident = `prehled`.`vyjezd_ident` ".
"AND vyjezdy.kdo = tymy.nazev ".
"AND vyjezdy.sezona = '2014-15' ".
"ORDER BY `dat_zapasu`, `jmeno`");

while ($row = mysql_fetch_array($result)) {
    $actions[] = $row['dat_zapasu'];
    $users[] = $row['jmeno'];
    $summary[$row['jmeno']][$row['dat_zapasu']] = $row['vzdalenost'];
}

$actions = array_unique($actions); // aby každá akce byla v poli jenom jednou
$users = array_unique($users); // aby každý člověk byl v poli jenom jednou

echo "<table>";
echo "<tr><th>Jméno \ Akce</th>";
foreach ($actions as $a) echo "<th>", $a , "</th>";
echo "</tr>";
foreach ($users as $u) {
  echo "<tr><th>", $u, "</th>";
  foreach ($actions as $a) {
    echo "<td>";
    if (isset($summary[$u][$a]))
      echo $summary[$u][$a];
    else
      echo "-";
    echo "</td>";
  }
  echo "</tr>";
}
echo "</table>";

Je to celé funkční, jen bych potřeboval ještě poradit, jak k tomu přiřadit sloupec se součtem, nejlépe hned za jméno?
A pak bych potřeboval doplnit k datu zápasu ještě zkratku týmu, tedy k nadpisům sloupců.

Děkuji moc.
BunnyBugs
Profil *
Ještě bych doplnil, že druhý výpis mi generuje celkový součet km a řadí dle jména, kdo má ujeto nejvíce. Tedy takto:

$VyjezdomerTop = mysql_query("SELECT `jmeno`, SUM(`vzdalenost`) AS `soucet` ".
                                                    "FROM `prehled`, `tymy`, `vyjezdy` ".
                                                    "WHERE `vyjezdy`.`ident` = `prehled`.`vyjezd_ident` ".
                                                    "AND `vyjezdy`.`kdo` = `tymy`.`zkratka` ".
                                                    "AND `vyjezdy`.`sezona` = '$SezonaVyjezdomerTop' ".
                                                    "AND `vyjezdy`.`dat_zapasu` < '$AktDatum' ".
                                                    "GROUP BY `jmeno` ".
                                                    "ORDER BY `soucet` DESC ".
                                                    "LIMIT 10");

                    $poradi = $posledni_poradi = 1;
                    $posledni_vzdalenost = null;

                    echo '<table>'.
                            '<tr><th>Poř.</th><th>Jméno/Nick</th><th>km</th></tr>';
                    
                    while ($rowVyjezdomerTop = mysql_fetch_array($VyjezdomerTop)){
    
                    if ($rowVyjezdomerTop['soucet'] !== $posledni_vzdalenost){
                        $posledni_vzdalenost = $rowVyjezdomerTop['soucet'];
                        $posledni_poradi = $poradi;
                        }
    
                        echo '<tr><td>'.$posledni_poradi.'</td><td class="left">'.$rowVyjezdomerTop['jmeno'].'</td><td class="right">'.$rowVyjezdomerTop['soucet'].'</td></tr>';

                    $poradi++;
                        } 
                    echo '</table>';

Poradí mi prosím někdo, jak toto spojit do jediného výpisu? případně jestli i mám dobře volené Selecty?
Děkuji předem.
BunnyBugs
Profil *
Tak jsem to vyřešil asi takhle:

$actions = array();
            $users = array();
            $summary = array();

            $result = mysql_query("SELECT `dat_zapasu`, `jmeno`, `vzdalenost` ".
                                    "FROM `vyjezdy`, `prehled`, `tymy` ".
                                    "WHERE vyjezdy.ident = `prehled`.`vyjezd_ident` ".
                                    "AND vyjezdy.kdo_zkratka = tymy.zkratka ".
                                    "AND vyjezdy.sezona = '$GetVyjezdomerPrehled' ".
                                    "AND `vyjezdy`.`dat_zapasu` < '$AktDatum' ".
                                    "AND prehled.vyjezdomer = 'ano' ".
                                    "ORDER BY `dat_zapasu`, `jmeno`");

            while ($row = mysql_fetch_array($result)) {
                $actions[] = $row['dat_zapasu'];
                $users[] = $row['jmeno'];
                $summary[$row['jmeno']][$row['dat_zapasu']] = $row['vzdalenost'];
                }

            $actions = array_unique($actions); // aby každá akce byla v poli jenom jednou
            $users = array_unique($users); // aby každý člověk byl v poli jenom jednou

            echo "<tr><th>Jméno \ Akce</th><th>Celkem</th>";

            foreach ($actions as $a) echo "<th>", date("d.m.", strtotime($a)) , "</th>";
                echo "</tr>";

            foreach ($users as $u) {
  
                $fDB = mysql_query("SELECT `fanklub` FROM `prehled` WHERE `jmeno` = '$u'");
                $fCol = mysql_fetch_array($fDB);
                $f = $fCol['fanklub'];
                
                if ($f == "ano"){
                    echo '<tr><th class="fanklub">', $u, "</th>"; 
                    } else {
                        echo '<tr><th>', $u, "</th>";
                        }
                
                echo '<th style="text-align: center;">';

                $souDB = mysql_query("SELECT SUM(`vzdalenost`) AS `soucet` ".
                                        "FROM `prehled`, `tymy`, `vyjezdy` ".
                                        "WHERE `vyjezdy`.`ident` = `prehled`.`vyjezd_ident` ".
                                        "AND `vyjezdy`.`kdo_zkratka` = `tymy`.`zkratka` ".
                                        "AND `vyjezdy`.`sezona` = '$GetVyjezdomerPrehled' ".
                                        "AND `vyjezdy`.`dat_zapasu` < '$AktDatum' ".
                                        "AND `jmeno` = '$u' ");
                    
                $sou = mysql_fetch_array($souDB);
                    echo $sou['soucet'];
                    echo '</th>';            
            
                foreach ($actions as $a) {
                    echo "<td>";
                            
                    if (isset($summary[$u][$a])) {
                        echo $summary[$u][$a];
                        } else {
                            echo "-";
                            }
                    
                    echo "</td>";
                    }
                echo "</tr>";
                }
                echo "</table>";

Nevím, jestli je to úplně správně, ale funguje to.
Resp. řadí se mi to na ose x podle data akce, tj. správně, ale sloupce se mi neřadí podle nejvíce najetých km :( to už nevím, jak na to, zkoušel jsem toho dost, ale vždy to vyhodilo nějakou chybu :(

Vaše odpověď

Mohlo by se hodit

Odkud se sem odkazuje


Prosím používejte diakritiku a interpunkci.

Ochrana proti spamu. Napište prosím číslo dvě-sta čtyřicet-sedm: