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
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
}
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;
}
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>";
}
?>
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. Also sid should become student_id so people that reads your code understand that its a foreign key for the students table.
Related
EDIT: I've just thought I could run this as one query with something like this:
SELECT * FROM clubs
JOIN match_request_archive
ON (SELECT COUNT (clubs.id) AS requests FROM match_request_archive)
I appreciate though my syntax is horrendous I've just whipped this up and will tweak it now.
I have a basic script for displaying data in a table. I've got a problem I can't solve which is I need to run one simple query to show all the data to the user. But in the last column I need to run a separate query to get the result.
Basically the first column in my table has the unique ID of a Rugby Club on the database. In a separate table on the database is a list of all requests made by clubs to players. I want to display how many requests each club has made in the last column of my script below which means running this query:
SELECT * FROM match_request_archive WHERE club_id LIKE "$row['id']"
The $row['id'] is from the PHP script below:
<?php
include 'credentials.php';
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
$sql = "SELECT * FROM clubs";
$result = $conn->query($sql);
if ($result->num_rows > 0) {
while($row = $result->fetch_assoc())
{
echo "
<tr>
<td>" . $row['id'] . "</td>
<td>" . $row['club_name'] . "</td>
<td>" . $row['club_captain'] . "</td>
<td>" . $row['club_captain_number'] . "</td>
<td>" . $row['club_captain_email'] . "</td>
<td>TBC</td>
</tr>
";
}
} else {
echo "<tr><td>0 results</td></tr>";
}
$conn->close();
?>
To solve this I tried embedding a second script in the echo command but it's my understanding you can't do this and in any case it did cause a fatal error. I can run this query separately and have it displayed in a different table, my problem is I need this to be all in one table. I hope that all makes sense.
Executing query in loop is bad practice because it has very slow performance.
So you should try get all data by one query if it possible:
SELECT *, COUNT(*) FROM clubs
LEFT JOIN match_request_archive
ON match_request_archive.club_id = clubs.id
GROUP BY clubs.id
This is my code with my output
<?php
$data = mysql_query("select * from request, users where request.user_id = users.id and request.user_id = $user_id") or die(mysql_error());
Print "<table border cellpadding=3>";
while($info = mysql_fetch_array( $data ))
{
Print "<tr class='my_loans_tr'>";
print "<td class='detail'>" .$info['loan_id'] . "</td> ";
Print " <td class='admin_amount'>".$info['amount'] . "</td> ";
Print " <td class='admin_points'>".$info['points'] . "</td> ";
Print " <td class='admin_date'>".$info['req_date'] . " </td>";
Print " <td class='admin_status'>".$info['status'] . " </td>";
Print " <td class='admin_cancelled'>".$info['cancelled_loan'] . " </td></tr>";
Print "</tr>";
}
Print "</table>";
?>
output
The loan id is within the database, there is a second table named collected, that has info for each loan id (3 rows minimum of info based on same loan id)
How can I get each id from the query, click on it, get the current id and declare it as a value so I can execute another query which is,
select * from collected where loan id = "$loan_id clicked";
I have searched for javascript and jquery but I am not able to get it working with the post method, what is the best way to do this?
Hi in the below i want to show Consultation Charges values for that i took the td.But in that td not displying anything even td also not showing.
After executing this query i want to find the no. of rows based on the rows i want to display the data.
My expected output:
Bill Particular Bill Sub Particular Doctor Date Dis. Amt.
Consultation Charges:
all the values based on no of rows.
php
<table width="100%">
<th>Bill Particular</th>
<th>Bill Sub Particular</th>
<th>Doctor</th>
<th>Date</th>
<th>Dis. Amt.</th>
<th>Charge</th>
<th>No. of Times</th>
<th>Amount</th>
</table>
<tr><th colspan=2>Consultation Charges:</th>
<?php
$div_options = array();
$sql = "SELECT ibp.ipd_bp_id, ibp.bp_id, bp.bp_name, ibp.bsp_id, bsp.bsp_name, ibp.doctor_id, ab.employee_name doctor, ibp.date date, ibp.amount charge, ibp.discount_amount discount, ibp.no_of_time, (ibp.no_of_time * ibp.amount) total_amount
FROM bill_particular_master bp
INNER JOIN ipd_bill_particular ibp ON ibp.bp_id = bp.bp_id
LEFT OUTER JOIN bill_sub_particular bsp ON bsp.bsp_id = ibp.bsp_id
LEFT OUTER JOIN address_book ab ON ab.employee_id = ibp.doctor_id
WHERE ibp.ipd_reg_no = '$ipd_no'
AND bsp.consultant =1
AND bsp.package = 0
AND bsp.admission = 0
AND bp.bp_name != 'Scan Charges'
AND bp.bp_name !='Procedure'";
$sth = $dbh->query($sql);
//$row=$dbh->fetch();
$i=1;
while($row=$sth->fetch(PDO::FETCH_ASSOC)){
$sub_arr['bp_name'] = $row['bp_name'];
$sub_arr['bsp_name'] = $row['bsp_name'];
echo "<tr>
<td>Here is the text - " . $sub_arr['bp_name'] . "</td>
<td>The ID of the text is - " .$sub_arr['bsp_name'] . "</td>";
if($i !== 0) {
echo "<td>The ID of the previous entry is - " .$sub_arr['bp_name'] . "</td>";
}
else {
echo "<td> </td>";
}
echo "</tr>";
$i = $row['bp_name'];
}
?>
</tr>
echo "<td>The ID of the previous entry is - " . $row['bp_name'] . "</td>";
You have there variable thats not even defined in your code.
I have a table "data" with a column "name" "state" and a few more
name state
peter MN
john NY
jay NY
sam CO
jack TX
jill NO
I want to calculate the number of entries per state and want my output as follows for example:
NY: 125
MN: 21
CO: 17
TX: 10
NO: 59
etc...
I have a query like this
$stmt = $db->query("SELECT state, COUNT(*) FROM `data` GROUP BY state;");
$nums = $stmt->rowCount();
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)){
echo "<tr>
<td>" . $row["state"] . "</td>
<td>$nums</td>
</tr>";
}
This displays every state in my table but does not return the corresponding number of entries for that state. This only returns the number of states i.e. 50. How can I display the number of entries per state?
You seem not to be referring to the column which has the count. Try aliasing it an referencing the alias in your PHP code:
$stmt = $db->query("SELECT state, COUNT(*) cnt FROM `data` GROUP BY state;");
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
echo "<tr>
<td>" . $row["state"] . ":</td>
<td>" . $row["cnt"] . "</td>
</tr>";
}
$stmt = $db->query("SELECT COUNT(name) as occurances,state FROM `data` GROUP BY state;");
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)){
echo "<tr>
<td>" . $row["occurances"] . "</td>
<td>" . $row["state"] . "</td>
</tr>";
}
Try this version,select the names only
I'm creating a page where it gets contact details from the forum on registered members. I've hit a little problem trying to retrieve the custom fields. For University and flights a drop down menu is used so that users can select from a specific list. However the forum uses 3 different table to store the data.
This is a small part of the tables
db mock up http://www.emuas.co.uk/images/db_mock_up.png
at the moment I get the uni for the uni query and a flight for the flight query, but the result is unrelated to the user_id. Not sure how to change that though.
$user = "SELECT *
FROM profile_fields_data
JOIN users
ON profile_fields_data.user_id = users.user_id
ORDER BY users.username";
$uni = "SELECT *
FROM profile_fields_data
JOIN users
ON profile_fields_data.user_id = users.user_id
JOIN profile_fields_lang
ON profile_fields_data.pf_university = profile_fields_lang.option_id
WHERE field_id =4
ORDER BY users.username";
$flight = "SELECT *
FROM profile_fields_data
JOIN users
ON profile_fields_data.user_id = users.user_id
JOIN profile_fields_lang
ON profile_fields_data.pf_flight = profile_fields_lang.option_id
WHERE field_id =3
ORDER BY users.username";
$userdata = $db->sql_query($user);
$unidata = $db->sql_query($uni);
$flightdata = $db->sql_query($flight);
while($data = $db->sql_fetchrow($userdata))
{
$datauni = $db->sql_fetchrow($unidata);
$dataflight = $db->sql_fetchrow($flightdata);
echo "<tr>
<td> <a href='http://emuas.co.uk/forum/memberlist.php?mode=viewprofile&u=" . $data['user_id'] . "'>" . $data['username'] . "</a></td>
<td> <a href='mailto:" . $data['user_email'] . "'>" . $data['user_email'] . "</a> </td>
<td>" . $data['pf_contact_number'] . "</td>
<td>" . $data['user_birthday'] . "</td>
<td>" . $data['pf_service_number'] . "</td>
<td>" . $datauni['lang_value'] . "</td>
<td>" . $dataflight['lang_value'] . "</td>
<td>" . $data['pf_secondary_duty'] . "</td>
</tr>";
Many thanks
Found the problem. When the forum got the data for option_id it added 1 to the value. I;ve changed it to take this into account