简体   繁体   中英

Need to combine info from multiple MySql tables to diaply in html table

I am having difficulty finding the one best way to do something. I have three tables in mysql, this has to do with a gym app im building, one is for muscle exercises, one is for cardio exercises, the other is to house users results from each workout daily. Every day a workout is created based on 0 to 1 cardio exercises and 0 - however many muscle exercises the personal trainer decided. At the end of the workout the user puts their results for each exercise into a form and submits it. results are just a text field. I have a database that creates a row for every individual result that consists of

username,exercise type (muscle,cardio),exercise name, exercise result, date

so an example would be

John,muscle,bench press, 225lb max out, 2012-08-21
John,cardio,rowing,12 miles, 2012-08-21

I then want to take this info and display it in a standard table per the date chosen. So there are multiple users per date and each user can have multiple answers. The headers would be

username,cardio, muscle 1, muscle 2 ect.. ( shows as many as their are for that day ) then the results going down and filling in the table.

Aside from making dozens of select calls I am trying to figure out a way to do this. I am going thinking of trying to create some sort or array and use that. Any ideas?

I'd go with the array. The problem is in dispatching the muscle/cardio results.

To format in HTML and get the headers, let's say we have first the cardio, then the muscle.

So you SELECT all rows of that date, ordering by username and cardio/muscle.

Then:

$users = array();
$maxes = array('cardio' => 0, 'muscle' => 0);

foreach($row = ...)
{
    list($user, $com, $name, $result) = $row;
    if (!isset($users[$user]))
        $userData = array('cardio' => array(), 'muscle' => array());
    else
        $userData = $users[$user];
    $userData[$com][] = array($name, $result);
    if (count($userData[$com]) > $maxes[$com])
        $maxes[$com] = count($userData[$com]);
    $users[$user] = $userData;
}

Now you have something like:

'John': {
     'cardio': [
        [ 'rowing', '12 miles' ],
        [ 'running', '2 miles' ],
     ],
     'muscle': [ ]
},
'Jack': {
     'cardio': [ ],
     'muscle': [ 'bench', '300lbs' ],
}

To format this in HTML, we have saved the maximum number of cardio of any user, and the maximum number of muscle. So a table of 1 + MaxC + MaxM columns will hold all users, ordered:

Name  Cardio 1    Cardio 2   Muscle 1

John  Rowing      Running    -
      12 miles    2 miles
Jack  -           -          Bench
                             300lbs

And to build it:

// First the header
print "<tr>";
print "<th>Name</th>";
for ($i = 0; $i < $maxes['cardio']; $i++)
    print "<th>Cardio " . ($i+1) . "</th>";
for ($i = 0; $i < $maxes['muscle']; $i++)
    print "<th>Muscle ($i+1)</th>";
print "</tr>";

foreach($users as $user => $info)
{
    print "<tr><td>$user</td>";
    foreach(array('cardio','muscle') as $key)
    {
        $data = $info[$key];
        for ($i = 0; $i < $maxes[$key]; $i++)
        {
            print "<td>";
            if (isset($data[$i]))
            {
                list($name, $result) = $data[$i];
                print "$name<br />$result";
            }
            else print '-';
            print "</td>";  
        } 
    }
    print "</tr>";
 }

I would recommend 4 tables for a 3NF setup:

user (username PK)
exercise_type (type PK)
exercise_name (name PK)
exercise_entry (id PK, username FK, date, exercise_type FK, exercise_name FK, result)

You would generate the exercise entries in exercise_entry and leave the result NULL . This way the user knows that there is a value expected.

On the PHP side you would simply fetch the data from the exercise_entry table based on the filter parameters.

Edit 1

If there is a 1:1 relationship between an exercise type and name, a 3 table approach would work.

user (username PK)
exercise_name (name PK, type)
exercise_entry (id PK, username FK, date, exercise_name FK, result)

Firstly, it may be unnecessary to have separate tables for each of the three categories of exercise: perhaps a single exercises table would suffice, with a column to indicate whether each record is a muscle exercise or a cardiovascular one?

On to your question: what you are seeking to do is pivot your data, which is a feature that the MySQL developers have deliberately not included natively (as they feel it is more suited to the presentation, rather than the database, layer of an application); other RDBMS may have more native support for such an operation. However, it is still possible to achieve what you seek by grouping your data and aggregating the results with MySQL's GROUP_CONCAT() function:

SELECT   username,
         GROUP_CONCAT(IF(exercise='bench press',result,NULL)) AS `bench press`,
         GROUP_CONCAT(IF(exercise='rowing'     ,result,NULL)) AS `rowing`,
         -- etc.
FROM     results
WHERE    date = '2012-08-21'
GROUP BY username

If the list of exercises is dynamic, you can generate a string containing this SQL statement and then prepare and execute a statement from that string:

SELECT CONCAT('
   SELECT   username,
', GROUP_CONCAT(
     'GROUP_CONCAT(IF(exercise=',QUOTE(name),',result,NULL))'
   ,    ' AS `',REPLACE(name,'`','``'),'`'
   )
,' FROM     results
   WHERE    date = ?
   GROUP BY username
') FROM exercises INTO @sql;
PREPARE stmt FROM @sql;
EXECUTE stmt USING '2012-08-21';
DEALLOCATE PREPARE stmt;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM