问题
I am new to phpmysqli. Here is what I have and what am trying to achieve: I will update this based on the recommendations; Database sample data
I want to display the data on one page with separate tables for each student based on their sid. This is what I have tried so far;
<?php
include_once 'dbcon.php';
$results = $MySQLiconn->query('SELECT * FROM activitybook');
$students = [];
foreach ( $results->fetch_array() as $activity ) {
$students[$activity['sid']][] = $activity;
}
foreach($students as $sid=>$activities) {
foreach($activities as $activity) {
echo
"<table><tr>
<th>SID</th>
<th>Date</th>
<th>FName</th>
<th>LName</th>
<th>activity</th>
<th>time</th>
<th>score</th>
</tr>
<tr>
<td>" . $sid . "</td>
<td>" . $activity['fname'] . "</td>
<td>" . $activity['lname'] . "</td>
<td>" . $activity['activity'] . "</td>
<td>" . $activity['atime'] . "</td>
<td>" . $activity['ascore'] . "</td>
</tr></table>";
}
}
?>
This is what I get
What am trying to achieve is separate tables for each sid
.
This is the sample of what I want to archive
回答1:
You will need to "group" your result set data based on the sid
value. As you iterate, check if you are changing groups or not.
I've added some refinements as well.
- Name the columns in your SELECT clause so that you only retrieve exactly what you need.
- Fetch an associative array, not a combination of indexed and associative elements.
- Assign a temporary variable to help you determine if you are continuing a
sid
group or starting a new one (or if it is the first iteration, don't write</table>
. implode()
helps to remove a lot of the code bloat.
Code:
$res = $conn->query("SELECT sid, fname, lname, activity, atime, ascore FROM activitybook ORDER BY sid");
$tmp = null;
$colheads = ['SID', 'FName', 'LName', 'activity', 'time', 'score'];
while ($row = $res->fetch_assoc()) { // don't produce more elements per row than needed
if ($tmp != $row['sid']) { // determine if a new group / sid value
if ($tmp !== null) {
echo '</table><br>'; // close the previous table
}
echo '<table border=1><tr><th>' , implode('</th><th>', $colheads) , '</th></tr>'; // start a new table & add headings
}
echo '<tr><td>' , implode('</td><td>', $row) , '</td></tr>'; // display the row data
$tmp = $row['sid']; // DUH, I FORGOT TO UPDATE $tmp!
}
if ($tmp !== null) {
echo '</table>'; // close the final table -- so long as there was at least one row in the result set
}
回答2:
Requirements: Process a stream of ordered student records. The stream consists of Groups of Student Records. Each group is identified by at 'sid' column.
- Each StudentGroup is wrapped in an HTML table
- Each StudentRecord is one row of the table
What I like to do is structure the code according to the data. The data is structured as follows:
An Iteration of StudentRecordGroup
Each StudentRecordGroup consists of a Sequence of:
- Process Start of Group
- Iterate through the records belonging to the group
- Process the end of the group
Notice there is no conditional statement anywhere in this process!
Now, how do we structure the code to do this. You cannot use a foreach
loop! That only does the read at the end of the loop.
We have two loops:
- The outer loop that processes All the groups of records.
- The inner loop that process One complete group.
The technique to do this is called 'read ahead'. And is just what it says. You read the first record before starting the outer loop.
Source:
Working Code at Pastebin.com
Output:
SID Date FName LName activity time score
2 John Ppap 12 56 56
2 John Ppap 23 23 23
SID Date FName LName activity time score
3 Mito Mmito 34 12 12
3 Mito Mmito 45 45 45
SID Date FName LName activity time score
4 Uba Uuba 56 78 100
The code:
<?php // 53020396/how-to-display-grouped-data-in-separate-tables-with-a-php-loop
/* ---------------------------------------------------------------------------
* The input stream consists of an Ordered Iteration of:
* A collection of Individual Records for each Student (StudentRecordGoup)
*
* Each StudentRecordGoup consists of a Sequence of:
* Start of Group
* Iteration of Student records belonging to the group
* End of Group
*
* Notice: There is no 'IF' statement anywhere in the control logic for a group!
*
* Please note: There is exactly one place in the code to assign any appropriate action!
* i.e. The structure of the code exactly matched the structure of the data. :)
*
* This makes it easier to debug, maintain and amend?
*
* To do this we need 'state' information. Specifically that a record is part
* of the current 'student record group' group or not. How do we do this?
*
* We always need a record to test! Including at the start! We never read a record
* and then decide what to do with it. We always know where we are in the data
* structure and either the current record belongs to the group or not.
*
* We need to use a technique called 'Read Ahead'. Literally, There is always
* a record to process. You don't have to test it to know where you are.
* Once you process it then you immediately read the next record from the input.
* i.e. You end up reading a new record NOT JUST AT THE WND OF THE LOOP!
* You cannot use 'foreach' loops.
*
* We have to include Initialisation to setup code and any termination code.
*
* I will put all the 'action place holders' in functions. That way it avoids
* obscuring the high-level logic.
*/
// Get the ordered student detail records
$pdo = getPdoConnection();
$pdoResultSet = prepareAndExecuteQuery($pdo);
// Process all the students Record Groups - 'read ahead' of the row
$curStudentRecord = $pdoResultSet->fetch(); // is assoc array
while ($curStudentRecord !== false) { // process the student record groups
// process one complete student group...
$curStudentRecordGroupId = $curStudentRecord['sid'];
startStudendRecordGroup($curStudentRecordGroupId);
while ( $curStudentRecord !== false // check record belongs to the current group
&& $curStudentRecord['sid'] === $curStudentRecordGroupId) {
processStudentRecord($curStudentRecord);
$curStudentRecord = $pdoResultSet->fetch(); // read the next record
}
endStudendRecordGroup($curStudentRecordGroupId); // ignore the current record
// that is the next group!
}
// all groups have been processed
exit;
/* --------------------------------------------------------------------------
* Student record group processing
*/
function startStudendRecordGroup($curStudentRecordGroupId)
{
echo "<!-- new group: sid = $curStudentRecordGroupId -->";
echo "<table><tr><th>SID</th><th>Date</th><th>FName</th><th>LName</th>
<th>activity</th><th>time</th><th>score</th></tr>";
}
function processStudentRecord($curStudentRecord)
{
echo "<!-- group record: sid = {$curStudentRecord['sid']} -->";
echo "<tr>
<td>" . $curStudentRecord['sid'] . "</td>
<td>" . $curStudentRecord['fname'] . "</td>
<td>" . $curStudentRecord['lname'] . "</td>
<td>" . $curStudentRecord['col3'] . "</td>
<td>" . $curStudentRecord['col4'] . "</td>
<td>" . $curStudentRecord['col5'] . "</td>
</tr>";
}
function endStudendRecordGroup($curStudentRecordGroupId)
{
echo "<!-- end group: sid = $curStudentRecordGroupId -->";
echo "</table>";
}
/* --------------------------------------------------------------------------
* Database access
*/
// Execute query and return 'resultset'
function prepareAndExecuteQuery(\PDO $pdo)
{
$sql = 'SELECT id, sid, fname, lname, col3, col4, col5
FROM activity
ORDER BY sid, id';
$stmt = $pdo->prepare($sql);
$allOk = $stmt->execute();
return $stmt;
}
// DB Connection
function getPdoConnection()
{
$opt = array(
\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION,
\PDO::ATTR_DEFAULT_FETCH_MODE => \PDO::FETCH_ASSOC,
\PDO::ATTR_EMULATE_PREPARES => false,
);
$pdo = new \PDO('mysql:host=localhost;dbname=notmydb;', 'notme', 'notme', $opt);
return $pdo;
}
回答3:
You can create a separate array of student ids and each time in loop check if that id exists in array than don't create a new table else create a new table. Use like this:
<?php
$res = $conn->query("SELECT * FROM activitybook");
$sId = [];
echo "<table>";
while($row=$res->fetch_array())
{
if (count($sId) == 0) {
$sId[] = $row['sid'];
}
if (!in_array($row['sid'], $sId)) {
$sId[] = $row['sid'];
echo "</table><table>";
}
echo
"<tr>
<th>SID</th>
<th>Date</th>
<th>FName</th>
<th>LName</th>
<th>activity</th>
<th>time</th>
<th>score</th>
</tr>
<tr>
<td>" . $row['sid'] . "</td>
<td>" . $row['fname'] . "</td>
<td>" . $row['lname'] . "</td>
<td>" . $row['activity'] . "</td>
<td>" . $row['atime'] . "</td>
<td>" . $row['ascore'] . "</td>
</tr>";
}
?>
回答4:
Group your results by SID then loop on it:
$results = $conn->query('SELECT * FROM activitybook');
$students = []
foreach ( $results->fetch_array() as $activity ) {
$students[$activity['sid']][] = $activity;
}
foreach($students as $sid=>$activities) {
foreach($activities as $activity) {
echo
"<table><tr>
<th>SID</th>
<th>Date</th>
<th>FName</th>
<th>LName</th>
<th>activity</th>
<th>time</th>
<th>score</th>
</tr>
<tr>
<td>" . $sid . "</td>
<td>" . $activity['fname'] . "</td>
<td>" . $activity['lname'] . "</td>
<td>" . $activity['activity'] . "</td>
<td>" . $activity['atime'] . "</td>
<td>" . $activity['ascore'] . "</td>
</tr></table>";
}
}
Some tips for you:
- Use variables with meaning names. What's inside your
$row
? Activities right? So name it$activity
instead. And avoid abbreviations like$res
. - The same is valid for your database columns.
aid
is not a good practice. Use just id for the primary key. Alsosid
should becomestudent_id
so people that reads your code understand that its a foreign key for the students table.
来源:https://stackoverflow.com/questions/53020396/how-to-display-grouped-data-in-separate-tables-with-a-php-loop