avoiding queries in loops for gradebook - php

I am making a gradebook and while the code below works, it is awfully slow. The first Column for the table is each students name, and then each cell afterwords corresponds to a specific question and shows what the students answer was. Not each student will have answered the given question.
Right now I have a loop and for each user a loop inside of that loop for each question.
Each question has a unique id, in a separate table each answer has a corresponding user id and a corresponding question id.
I have looked for similar posts with no luck, I have only been working with php for a few months so any advice will be much appreciated.
Questions
| id | question | choice 1 | choice 2 | choice 3 | choice 4 | correctAnswer | categoryId |
Answers
| id | questionId | userId | studentAnswer |
Users
| id | firstName |
Thanks
//Select all questions from a specific category $mid defined by user
$questionQuery = mysql_query("SELECT * FROM questions WHERE mid = $mid");
$questionCount = mysql_num_rows($questionQuery);
//Blank space in table above users
echo "<tr><td></td>";
//Question Number, each category has a different number of questions
for ($q = 0; $q < $questionCount; $q++){
echo "<td>" . ($q+1) . "</td>";
}
echo "</tr>";
//Select all students for a given teacher
$userQuery = mysql_query("SELECT * FROM users WHERE teacherId = $userid ORDER BY `block`, `lastName`");
$userCount = mysql_num_rows($userQuery);
for($s = 0; $s < $userCount; $s++){
$currentUserId = mysql_result($userQuery, $s, 0);
//Echo student's name
echo "<tr class='user" . ($s%2) ."'><td>" . mysql_result($userQuery, $s, 1) . "</td>";
//Load current question id
for ($q = 0; $q < $questionCount; $q++){
$questionNum = mysql_result($questionQuery, $q, 0);
//Selects students answer from answers database for above question
$answerQuery = mysql_query("SElECT * FROM answers WHERE questionId = $questionNum AND userId = $currentUserId");
$answerCount = mysql_num_rows($answerQuery);
//If no answers move on to next cell
if($answerCount == 0){
echo "<td></td>";
}
else{
$currentAnswer = mysql_result($answerQuery, 0, 3);
$correctAnswer = mysql_result($questionQuery, $q, 6);
if ($currentAnswer == $correctAnswer){
echo "<td class='right'><div id='parent'>" . $currentAnswer . "</div></td>";
}
else{
echo "<td class='wrong'><div id='parent'>" . $currentAnswer . "</div></td>";
}
}
}
echo "</tr>";
}

Related

Show only one time a repeat record and all of the other different elements adding to it in PHP

I have a table called Orders with 2 attributes: id_order and id_product.
In this table, I save the list of the products bought adding this to a single id of order, something like this
id_order - id_product
1 - 1
1 - 2
1 - 5
1 - 6
2 - 4
2 - 8
2 - 14
3 - 1
I want to show this record in a table, BUT only showing one(1) time the id of order.
<?php
include "con.php";
$sql = "SELECT DISTINCT a.id_order , b.id_product FROM orders a JOIN orders b ON a.id_order=b.id_order";
$sentence1 = $con->prepare($sql);
$sentence1->execute();
$sentence1->bind_result($id, $product);
?>
<html>
<body>
<table border="3">
<tr>
<td>ID of order</td>
<td>ID of products from this order</td>
</tr>
<?php
while ($sentence1->fetch()) {
echo "<tr>";
echo "<td>" . $id . "</td>";
echo "<td>" . $product . "</td>";
echo "</tr>";
}
$sentence1->close();
?>
The problem is this SQL show repeatable times the number of id_order I want something like this:
id_order - id_product
1 - 1
- 2
- 5
- 6
____________
2 - 4
- 8
- 14
____________
3 - 1
The result I want is:
Another design:
While iterating over your result set in PHP, you can keep track of whether you encounter a new id value. If so, then display it in a table cell, otherwise do not display it.
$id_prev = NULL;
while ($sentencie->fetch()) {
echo "<tr>";
if ($id != $id_prev) {
echo "<td>" . $id . "</td>";
$id_prev = $id;
}
else {
echo "<td></td>"; // you could try adding spaces here for padding
}
echo "<td>" . $product . "</td>";
echo "</tr>";
}
As for the separator line, this is probably something which should be handled in your CSS/HTML layer.
Update:
It looks like your new requirement is to have the id table cells span multiple product rows. One option would be to use the rowspan attribute of the table cell. The trick here is that we need to iterate across an entire id before we know how many products there are.
$id_prev = NULL;
$products = array();
function displayRow($id, $products) {
for ($x = 0; $x < count($products); $x++) {
echo "<tr>";
if ($x == 0) {
echo "<td rowspan=\"" . count($products) . "\">" . $id . "</td>";
}
echo "<td>" . $products[$x] . "</td>";
echo "</tr>";
}
}
while ($sentencie->fetch()) {
if ($id != $id_prev) {
displayRow($id, $products);
$products = array();
$id_prev = $id;
}
array_push($products, $product);
}
// DON'T forget to call displayRow() one last time, to cover the final id
// appearing in your result set
displayRow($id_prev, $products);
Here sql command .
$sql = "SELECT DISTINCT id_order, GROUP_CONCAT(DISTINCT id_product ORDER BY id_product) AS child_id_list FROM orders group by id_order ORDER BY id_order";
$sentence1 = $con->prepare($sql);
$sentence1->execute();
$sentence1->bind_result($id, $product);

PHP, While loop not showing results

i am new to PHP and mysql, I am trying to build a table. However, I have a very interesting bug that i can't fix.
code:
//Initializing mysql queries
//-----------------------SELECTING GOALS------------
$sql= "SELECT * FROM goals";
$records = mysql_query($sql);
//-----------------------SELECTING SERVICES---------
$sql2= "SELECT * FROM services";
$records2 = mysql_query($sql2);
//----------------SELECTING THE JUNCTION----------
$sql3 = "SELECT services.sid AS sid, services.name, objectives.oid
FROM services, objectives, servo
WHERE servo.s_id = services.id AND servo.obj_id = objectives.id";
$records3 = mysql_query($sql3);
$sql4 = "SELECT oid, gid, statement, GROUP_CONCAT(DISTINCT gid) AS GOID
FROM goals, objectives, obgoals
WHERE obgoals.go_id = goals.id AND obgoals.ob_id = objectives.id
GROUP BY oid";
$records4 = mysql_query($sql4);
<?php
while ($product = mysql_fetch_assoc($records2)) {
echo "<tr>";
$sid = $product['sid'];
$service = $product['name'];
echo "<td><a href='objectives.php?sid=" . $sid . "&service=" . $service . "'>" . $product['sid'] . "</a> </td>";
echo "<td>".$product['name']."</td>";
echo "<td>";
while ($g4services = mysql_fetch_assoc($records)) {
echo $g4services['gid'];
}
echo"</td>" ;
}
?>
Basically, my table has 30 rows and 3 columns, the last column is supposed to print out values from a database, this part is done by this piece of code
while ($g4services = mysql_fetch_assoc($records)) {
echo $g4services['gid'];
}
However, instead of printing the results for each row, it only prints the results for the first row, basically the first while loop runs and creates the the table with the 30 rows, but the second while loop only prints values on first row only. Essentially this is happening:
|SID|Name Of Service| Objectives|
-------------------------------
|S1| Service 1 | make the best cars|
|s2| Service 2 | |
|s3| Service 3 | |
|s4| Service 5 | |
|s5| Service 5 | |
.....
.....
....
|s30| Service30 | |
for some reason, my objectives column is not populated by the while loop, it only works for the first row. If someone can help me work the while loop to print the values for every row, it will be huge help. I will greatly appreciate it. Thanks.
The problem is that you're reading all the results of $records during the first iteration of the outer while loop. On future iterations, there are no more rows left, so the inner while loop completes immediately.
You can read those results once into a variable outside the loop, and then show that variable during the loop:
$all_g4services = '';
while ($g4services = mysql_fetch_assoc($records)) {
$all_g4services .= $g4services['gid'];
}
while ($product = mysql_fetch_assoc($records2)) {
echo "<tr>";
$sid = $product['sid'];
$service = $product['name'];
echo "<td><a href='objectives.php?sid=" . $sid . "&service=" . $service . "'>" . $product['sid'] . "</a> </td>";
echo "<td>".$product['name']."</td>";
echo "<td>$all_g4services;</td>" ;
}
Your second while loop is not required. It is runnning for each row. Remove it. Then for each row you have to find the entry that matches and echo only that.
echo $g4services['gid']
I dont know how to correct it for you as I dont know the relationship between the two.

MySQL Table Correlation

I'm kinda a noob so I don't know if this is actually possible.
I have two field in my MySQL database that I want to correlate:
Level int(11)
Rank enum('Hobo','Shippai','NoLifer',Troublemaker','gangster') (and so it continues.)
I want so that level 1=Hobo, level 2=Shippai etc. etc.
Currently it looks like this:
Level: {$ir['level']}
And want it to look like this on the website:
Rank: Hobo
Thanks in advance for any help...
Edit:
Here's the code for the table:
";
$exp=(int)($ir['exp']/$ir['exp_needed']*100);
print "
Name: {$ir['username']}Crystals: {$cm}
Level: {$ir['level']}
Exp: {$exp}%
Money: $fm
Gang: ";
$qs=$db->query("SELECT * FROM gangs WHERE gangID={$ir['gang']}");
$rs=$db->fetch_row($qs);
if(!$db->num_rows($qs) )
{
print "No Gang";
}
else
{
print" {$rs['gangNAME']} ";
}
print "
Property: {$ir['hNAME']}
Days Old: {$ir['daysold']}
Health: {$ir['hp']}/{$ir['maxhp']}
Energy: {$ir['energy']}/{$ir['maxenergy']}
Brave: {$ir['brave']}/{$ir['maxbrave']}
Will: {$ir['will']}/{$ir['maxwill']}
";
Create a new table called rank_define inside have, id(int, primary key, auto increment), name(char20), add your named ranks so it looks like this:
id | name
1 | Hobo
2 | Shippai
In your code
$user_id = 1; // or whatever the users rank is
$query = mysql_query("SELECT `name` FROM rank_define WHERE id='$user_id'");
This will return a result set so you will want to do:
while ($row = mysql_fetch_array($result, MYSQL_ASSOC)) {
echo "rank: " . $row['name']; // will output rank: Hobo
}
I think this is what you mean, is it for a user account or something different? This is assuming no account and you are just searching for a result.
If it were to be part of a user account system you would want a table for users eg: id (auto inc, primary key, int), name, rank, (etc..)
In rank their rank is inserted from the rank_define table, so 1 = hobo, 2 = shippai etc. Then you would query that table
Updated answer:
Okay so you have the while loop.while ($row = mysql_fetch_array($query, MYSQL_NUM)) {.
It will go something like this:
<table>
<?php
while ($row = mysql_fetch_array($result, MYSQL_ASSOC)) {
echo "<tr>";
echo "<td>Name: " . $row['name'] . "</td>"; // will output their name / account whatever
echo "<td>Rank: " . $row['rank'] . "</td>"; // will output their rank
echo "</tr>";
}
?>
</table>

How do I display mysql table row as column

I have two tables, result and gp.
In the result table I have something like this:
|id||student_id ||course_code||grade||session ||level||semester|
|1 ||TR/2213234561||MAT111 ||A ||2009/2010||100 ||first |
|2 ||TR/2213234561||MAT112 ||B ||2009/2010||100 ||first |
|3 ||TR/2213234561||MAT113 ||C ||2009/2010||100 ||first |
|4 ||TR/2213234567||MAT111 ||D ||2009/2010||200 ||first |
|5 ||TR/2213234567||MAT112 ||C ||2009/2010||200 ||first |
|6 ||TR/2213234567||MAT113 ||C ||2009/2010||200 ||first |
Then gp table
|id||student_id ||session ||level||semester||gp |
|1 ||TR/2213234561||2009/2010||100 ||first ||4.2|
|2 ||TR/2213234567||2009/2010||100 ||first ||3.5|
|3 ||TR/2213234561||2010/2011||200 ||first ||4.2|
|4 ||TR/2213234567||2010/2011||200 ||first ||3.5|
What I want is like this:
|Matriculation||MAT111||MAT112||MAT113||MAT114||GP |
|TR/2213234561||A ||B ||D ||C ||4.2|
|TR/2213234567||C ||D ||E ||F ||3.5|
The course code are not constant - it depends on the course registered by the students
I have done this:
<?php
$rst1 = mysql_query("select distinct course_code from result ", $conn);
echo "<table callspacing='4'>";
echo "<tr>";
echo "<td> Matriculation Number </td>";
$c_code = array();
while ($row = mysql_fetch_array($rst1))
{
$c_code[] = $row['course_code'];
}
foreach($c_code as $c_code)
{
echo "<td>" .$c_code. "</td>";
}
$sql ="SELECT result.student_id,
MAX(CASE WHEN course_code = ' $c_code' THEN grade END) $c_code,
gp.CTC
FROM result
JOIN gp
ON gp.student_id = result.student_id
GROUP
BY student_id";
echo "<td> GP</td>";
$rst = mysql_query("$sql",$conn) or die(mysql_error());
while ($row = mysql_fetch_array($rst))
{
echo "</tr>";
echo "<tr>";
echo "<td>" .$row['student_id']. "</td>";
echo "<td>" .$row[$c_code]. "</td>";
}
echo "<td>" .$row[$c_code]. "</td>";
echo "<td>" .$row['CTC']. "</td>";
echo"</tr>";
echo "</table>";
?>
The first query was to get the course code, since the courses are not constants.
with that code, I got something like this:
|Matriculation||MAT111||MAT112||MAT113||MAT114||GP|
|TR/2213234561|
|TR/2213234567|
But I wanted
|Matriculation||MAT111||MAT112||MAT113||MAT114||GP |
|TR/2213234561||A ||B ||D ||C ||4.2|
|TR/2213234567||C ||D ||E ||F ||3.5|
Any suggestion or direction will be highly appreciated.
What you wish to do is known as "pivoting" your data and is something for which some other RDBMS have native support, but MySQL does not (by design, as the developers feel that such manipulations belong in the presentation layer).
However, you have a few options:
Construct a rather horrible MySQL query to perform the pivoting operation manually:
SELECT student_id AS Matriculation, MAT111, MAT112, gp AS GP
FROM gp
NATURAL JOIN (
SELECT student_id, grade AS MAT111
FROM result
WHERE course_code = 'MAT111'
) AS tMAT111
NATURAL JOIN (
SELECT student_id, grade AS MAT112
FROM result
WHERE course_code = 'MAT112'
) AS tMAT112
-- etc.
WHERE level = #level AND semester = #semester
If you choose to go down this path, you can make your life slightly easier by generating this query automatically, using either a looping construct in PHP or a prepared statement in MySQL.
Here is one way that you could do that in PHP:
Obtain a list of courses:
$dbh = new PDO('mysql:dbname=testdb;host=127.0.0.1', $user, $password);
$qry = $dbh->query("SELECT DISTINCT course_code FROM result [WHERE ...]");
$courses = $qry->fetchAll(PDO::FETCH_COLUMN, 0);
Loop over the results, constructing the above SQL:
mb_regex_encoding($charset);
$columns = mb_ereg_replace('`', '``', $courses);
$sql = "
SELECT student_id AS Matriculation, `".implode("`,`", $columns)."`, gp AS GP
FROM gp";
foreach ($columns as $column) $sql .= "
NATURAL JOIN (
SELECT student_id, grade AS `$column`
FROM result
WHERE course_code = ?
) AS `t$column`";
$sql .= "
WHERE level = ? AND semester = ?";
Execute the SQL, passing in the array of courses as parameters:
$qry = $dbh->prepare($sql);
$params = $courses;
array_push($params, $level, $semester);
$qry->execute($params);
Output the results:
echo "<table>";
echo "<tr>";
for ($i = 0; $i < $qry->columnCount(); $i++) {
$meta = $qry->getcolumnMeta($i);
echo "<th scope='col'>" . htmlentities($meta['name']) . "</th>";
}
echo "</tr>";
while ($row = $qry->fetch(PDO::FETCH_NUM)) {
echo "<tr>";
foreach ($row as $field) echo "<td>" . htmlentities($field) . "</td>"
echo "</tr>";
}
echo "</table>";
Do the above as a one-off operation so that the structure of your MySQL database is changed to more closely reflect this desired layout (easy once table is converted, but may impact other uses of the database):
CREATE TABLE StudentGrades (PRIMARY KEY('Matriculation'))
SELECT student_id AS Matriculation, MAT111, MAT112, gp AS GP
-- etc. as above
Alternatively, you can create a VIEW which is a sort of "virtual table" structured in this way based on the underlying table.
Pivot the data manually in PHP (relatively tedious).

Echo variable once

How can i display variable only once in the table.
<?php
$sql = mysql_query("SELECT * FROM babydata");
while ($row = mysql_fetch_array($sql)) {
for ($i = 1; $i <= 72; $i++) {
if ($i == $weeknumber) {
echo "<tr><td width='40'>Week $i</td>";
echo "<td width='500' >" . count($row[$menucompare]) . "</td></tr>";
}
}
}
?>
this code display like this:
--------------
week4 | 1
-------------
week4 | 1
-------------
But i want to display weeknumber only once and count($row[$menucompare]) will be counted 2 in week 4 . not 1 and 1 .
Like this:
--------------
week4 | 2
---------------
Seems like you want to output the amount of tuples in babydata for a certain week. You can just filter out any tuples which dont belohnt to the $weeknumber in your query.
// TODO: Assert, that $weeknumber is an integer, to not be prune to SQL injection.
$weeknumber = (int)(($currentdate - $birthday) / (7 * 24 * 60 * 60)) + 1;
// Select the amount of tuples in babydata for the desired $weeknumber.
$result = mysql_query("SELECT count(*) FROM babydata ".
"WHERE week = $weeknumber");
// There is only one tuple with one column that contains the amount as number.
$row = mysql_fetch_row($result);
// Output the week and the amount of data.
echo "<tr><td width='40'>Week $weeknumber</td>" ;
echo "<td width='500' >".$row[0]."</td></tr>";
No need for loops.
To output all weeks and their respective amount of data:
// Select the amount of tuples in babydata for all weeks.
$result = mysql_query("SELECT week, count(*) FROM babydata ".
"GROUP BY week");
// For all weeks:
while ($row = mysql_fetch_row($result))
{
// Output the week and the amount of data.
echo "<tr><td width='40'>Week ".$row[0]."</td>" ;
echo "<td width='500' >".$row[1]."</td></tr>";
}
This assumes that you have a column week in your table babydata that contains just a number. This outputs only weeks, that have at least one tuple.
You can do that directly in the SQL. Warning: I didn't actually tested this.
SELECT week, count(week) FROM babydata GROUP BY week;
This will directly return a result like
--------------
week4 | 2
week5 | 3
--------------
Just replace week with the actual name of your week field, and adapt the PHP to handle the new result structure. Something along these lines:
$sql= mysql_query("SELECT * FROM babydata");
while($row = mysql_fetch_array($sql))
{
echo "<tr><td width='40'>Week ".$row[0]."</td>" ;
echo "<td width='500' >".$row[1]."</td></tr>";
}

Categories