how to calculate the sum of grades for each student - php

I have one table for the grades of exams, and another for students (the students table will always have 10 students only).
I want to output an array in Json format for students details with the sum of grades of each by semester in array of array.
expected output
[
{
"id": "1", -> this is the semester ID
"student1": {"id": "1","name": "student name", "bio": "50", "chem": "50", "math": "60", "total grades": "160"},
"student2": {"id": "2","name": "secondstudent name", "bio": "60", "chem": "60", "math": "50", "total grades": "170"},
}
]
here is my tables structure
tbStudents
id , name
1 , student name
2 , secondstudent name
3 , thirdstudent name
tbGrades
id , student , semster , bio , chem , math , total
1 , 1 , 1 , 50 , 50 , 60 , 160
2 , 2 , 1 , 30 , 40 , 20 , 90
3 , 2 , 1 , 30 , 20 , 30 , 80
challenge
How to calculate the SUM of each bio, chem, math, total for each student by semester, cause the student might have multiple grades in the same semester
what i've tried
$sql = "SELECT grade.id g_id, grade.semester g_semester, grade.bio g_bio, grade.chem g_chem, grade.math g_math, grade.total g_total, student.id s_id, student.name s_name FROM tbGrades AS grade INNER JOIN tbStudents AS student ON student.id = grade.student";
try {
$db = new db();
$db = $db->connect();
$stmt = $db->prepare($sql);
$stmt->execute();
$grade = $stmt->fetchAll(PDO::FETCH_OBJ);
$db = null;
if(empty($grade)) {
$response->getBody()->write
('
{
"error":
{
"status":"400",
"message":"Invalid Request"
}
}');
} else {
foreach($grade as $value) {
$array_resp[]=[
'id' => $value->g_id,
'student1' => ['id'=>$value->s_id, 'name'=>$value->s_name, 'bio'=>$value->g_bio, 'chem'=>$value->g_chem, 'math'=>$value->g_math, 'total grades'=>$value->g_total],
];
}
$response->getBody()->write(json_encode($array_resp));
}
} catch(PDOException $e) {
$response->getBody()->write
('
{
"error":
{
"message":'. $e->getMessage() .'
}
}');
}

You can do it easily using MySQL GROUP BY clause.
SELECT
grade.id g_id,
grade.semester g_semester,
SUM(grade.bio) g_bio,
SUM(grade.chem) g_chem,
SUM(grade.math) g_math,
SUM(grade.total) g_total,
student.id s_id,
student.name s_name
FROM
tbGrades AS grade
INNER JOIN
tbStudents AS student
ON
student.id = grade.student
GROUP BY
student.id,
grade.semester

Related

display 2 id's in json response

subjects.id is overriding users.id in the JSON response whenever i add subjects.idto the select in the query.
How can i show my both users.id and subject.id in the response
$sql = "SELECT users.id,users.name,users.date,subjects.id FROM tb_user AS users INNER JOIN
tb_subjects AS subjects ON users.id = subjects.userid WHERE users.id = '$userid'";
try {
$db = new db();
$db = $db->connect();
$stmt = $db->prepare($sql);
$stmt->execute();
$user = $stmt->fetch(PDO::FETCH_OBJ);
$db = null;
if(empty($user)) {
$response->getBody()->write
('
{
"error":
{
"message":"Invalid"
}
}');
} else {
$response->getBody()->write(json_encode($user));
}
} catch(PDOException $e) {}
current output
{
"id": "1",
"name": "joe",
"date": "2017-07-22 18:37:37"
}
expected output
{
"id": "1",
"name": "joe",
"subjectid": "4",
"date": "2017-07-22 18:37:37"
}
To get around the problem of a result set with two id columns, give the subject id column an alias of subjectid:
SELECT
users.id,
users.name,
users.date,
subjects.id AS subjectid
FROM tb_user AS users
INNER JOIN tb_subjects AS subjects
ON users.id = subjects.userid
WHERE users.id = '$userid'
Most databases seem to tolerate a result set which has two columns by the same name. But this would fail if it were to happen in a subquery in which you tried to also select that duplicate column. It looks like PHP is just deciding to choose one of the id columns though the best thing to do here is to fix the duplicate name problem in the query.
Edit:
If you wanted to get the latest subject, as indicated by its time column, you could slightly modify your query to use another join:
SELECT
users.id,
users.name,
users.date,
s1.id AS subjectid
FROM tb_user AS users
INNER JOIN tb_subjects s1
ON users.id = s1.userid
INNER JOIN
(
SELECT userid, MAX(time) AS max_time
FROM tb_subjects
GROUP BY userid
) s2
ON s1.userid = s2.userid AND
s1.time = s2.max_time
WHERE users.id = '$userid'
The subquery in the new join finds the latest time for each user. But this still does not give us the subject id which we actually want. To get this, we can access the first tb_subjects table, which however has been reduced after this new join to only records having the most recent message time. One caveat here: if you had a tie for most recent message my query would return all such ties. We could workaround this, but it would be more work.

how to make array of array by looping result from a query

I have a survey about games, and I have one table for the games data, and another for people's answers.
I want to output an array in Json format for answers with each of the 3 favorite games name and their year in an array of array.
expected output
[
{
"id": "1",
"username": "userX",
"g1": {"name": "game1", "year": "1991"},
"g2": {"name": "game2", "year": "1992"},
"g3": {"name": "game3", "year": "1993"},
}
]
what i've tried
$sql = "SELECT * FROM tbAnswers AS answer INNER JOIN tbgames AS game ON answer.g1 = game.id";
try {
$db = new db();
$db = $db->connect();
$stmt = $db->prepare($sql);
$stmt->execute();
$answer = $stmt->fetchAll(PDO::FETCH_OBJ);
$db = null;
if(empty($answer)) {
$response->getBody()->write
('
{
"error":
{
"status":"400",
"message":"Invalid Request"
}
}');
} else {
$response->getBody()->write(json_encode($answer));
}
} catch(PDOException $e) {
$response->getBody()->write
('
{
"error":
{
"message":'. $e->getMessage() .'
}
}');
}
the current output
[
{
"id": "1",
"username": "userX",
"name": "game1",
"year": "1991"
}
]
I think i should do a foreach somewhere in else to go through each game and echo the result of it based on the id from answers, but i am not sure how to apply it
where to place to foreach
how to select and get the results based on each game id
how to do it in json format
i'm sure it's not how i am doing it, this is how i am trying to echo the data in else
echo"[";
echo"\n{";
echo"\n";
echo '"id:"'.' "'.$answer[0]->id.'",';
echo"\n";
echo"}\n";
echo"]";
here are my tables structure
tbGames
id , name , year
1 , 'game1' , '1991'
2 , 'game2' , '1992'
3 , 'game3' , '1993'
4 , 'game4' , '1994'
tbAnswers
id , name , g1 , g2 , g3
1 , userX , 1 , 2 , 3
2 , userY , 3 , 1 , 4
3 , userZ , 1 , 1 , 2
4 , userW , 2 , 3 , 4
Using this query:
$sql = "SELECT answer.id a_id, answer.name a_name, game1.id g1_id, game1.name g1_name, game1.year g1_year, game2.id g2_id, game2.name g2_name, game2.year g2_year, game3.id g3_id, game3.name g3_name, game3.year g3_year FROM tbAnswers AS answer INNER JOIN tbgames AS game1 ON answer.g1 = game1.id INNER JOIN tbgames AS game2 ON answer.g2 = game2.id INNER JOIN tbgames AS game3 ON answer.g3 = game3.id";
you should change your else statement content to:
} else {
foreach($answer as $value) {
$array_resp[]=[
'id' => $value->a_id,
'username' => $value->a_name,
'g1' => ['name'=>$value->g1_name, 'year'=>$value->g1_year],
'g2' => ['name'=>$value->g2_name, 'year'=>$value->g2_year],
'g3' => ['name'=>$value->g3_name, 'year'=>$value->g3_year],
];
}
$response->getBody()->write(json_encode($array_resp));
}

Fill array from query with mysqli_fetch_assoc()

I had this and everything worked fine (I had a generic table builder, but now I am having to stray from that):
while ($x = mysqli_fetch_assoc($result))
{
$fields[] = $x['Field'];
}
Now I have something similar to this:
$result = mysqli_query($con, 'SELECT r.id AS ID, CONCAT(g.fname, g.lname) AS Name, r.apple AS Apple,
r.dog AS Dog, DATEDIFF(r.Dog, r.Apple) AS Days,
r.total_price AS "Total Price", u.name AS Name, r.in AS "In",
r.out AS "Out", r.time_in AS "Time In", r.time_out AS "Time Out",
CONCAT(c.fname,c.lname) AS Charlie, r.here AS "Apple",
r.leave AS "Dog"
FROM really r, georgia g, unit u, charlie c
WHERE g.id = r.georgia AND r.unit = u.id AND r.charlie = c.id
HAVING r.in = TRUE AND r.out = FALSE');
//fill fields array with fields from table in database
while ($x = mysqli_fetch_assoc($result))
{
$fields[] = $x['Field'];
}
I am now getting an error for the line $fields[] = $x['Field']; because of the word Field. Why? Because I now have a full query? How can I fix this without referencing each field name?
Because there is not a field named Field in you query result:
'SELECT r.id AS ID, CONCAT(g.fname, g.lname) AS Name, r.apple AS Apple,
r.dog AS Dog, DATEDIFF(r.Dog, r.Apple) AS Days,
r.total_price AS "Total Price", u.name AS Name, r.in AS "In",
r.out AS "Out", r.time_in AS "Time In", r.time_out AS "Time Out",
CONCAT(c.fname,c.lname) AS Charlie, r.here AS "Apple",
r.leave AS "Dog"
FROM really r, georgia g, unit u, charlie c
WHERE g.id = r.georgia AND r.unit = u.id AND r.charlie = c.id
HAVING r.in = TRUE AND r.out = FALSE'
There are some fields in your query result: ID, Name, Apple, etc.. You can try to fetch the these field as below, or change your query command.
while ($x = mysqli_fetch_assoc($result))
{
$fields[] = $x['ID'];
}

Drupal-based SQL query through PHP: count t1 join t2

I have two tables like so:
table {node}
`nid`, `uid`, `type`
1 1 basketball
2 1 basketball
3 1 football
4 2 football
5 2 basketball
table {strato_ticket}
`tid`, `author_uid`, `purpose`, `active`
1 1 'Ticket to a basketball game' TRUE
2 1 'Ticket to a football game' TRUE
3 2 'Ticket to a football game' FALSE
I'd like to generate a report that counts the number of each kind of node, and then counts the number of active tickets that each user has associated with that kind of node.
My solution uses a combination of SQL and PHP: I have a PHP loop for each kind of node that I'm interested in, which simplifies the SQL query, and translates from 'type' to 'purpose', eg
$node_types = array('basketball', 'football');
foreach($node_types as $node){
switch($type){
case 'basketball':
$purpose = array('Ticket to a basketball node');
break;
case 'football':
$purpose = array('Ticket to a football game');
break;
}
$where = " WHERE ({strato_ticket}.`purpose` = '"
.implode("' OR {strato_ticket}.`purpose` = '",$purpose)."')";
Finally I have the trouble spot, the SQL query. When I was just counting nodes owned by each user, it worked fine:
$query = "
SELECT uid, count( * ) AS nodes_owned
FROM {node} WHERE `type` = '$type'
GROUP BY uid ORDER BY nodes_owned DESC
";
$query = db_query($query);
output:
Now displaying info for basketball.
uid nodes_owned
1 2
2 1
Now displaying info for football.
uid nodes_owned
1 1
2 1
But now that I need to query against another table, strato_ticket, things get complicated, and my query is returning FALSE without throwing an error (I think).
$query = "
SELECT count(*) as tickets
FROM {strato_ticket} INNER JOIN (
SELECT node.uid, count( * ) AS nodes_owned
FROM {node} WHERE `type` = '$type'
GROUP BY uid
) AS {nodecount}
ON {strato_ticket}.`author_uid` = {nodecount}.`uid`
$where
GROUP BY nodecount.uid ORDER BY nodecount.nodes_owned DESC
";
$query = db_query($query);
I'm not very good with SQL and I'm not quite sure how it's broken. Could use a little help?
Ideally would like to see
uid nodes_owned tickets
//basketball
1 2 1
2 1 0
//football
1 1 1
2 1 0
Aside from the placeholders, which I can get to later, I think this solves it.
$form = array();
$node_types = array('basketball','football');
// if($user->uid == 1){
$form[$type][] = array('#value'=>"Showing how many of each node type each user owns.".'<br/>');
foreach($node_types as $type){
// Count the number of nodes each user owns of $type.
$form[$type][] = array('#value'=>"Now displaying info for $type".'s. <br/>');
switch($type){
case 'basketball':
$purpose = array('ticket to a basketball game', 'basketball');
break;
case 'football':
$purpose = array('ticket to a football game');
break;
}
$purpose = implode("', '", $purpose);
//#todo : Make a temporary table to query against so I'm not hitting node table multiple times.
$ticketquery = "
SELECT author_uid, purpose, COUNT( * ) AS invitees_accepted
FROM {strato_ticket}
WHERE purpose IN ('$purpose')
GROUP BY author_uid, `purpose`
";
$nodequery = "
SELECT node.uid, count( * ) AS nodes_owned, type
FROM {node}
WHERE `type` IN ('$type')
GROUP BY uid, type";
$query = "
SELECT * FROM
($nodequery) AS nt
JOIN
($ticketquery) AS tt
ON nt.uid = tt.author_uid
GROUP BY nt.uid ORDER BY nt.nodes_owned DESC
";
drupal_set_message('Query is <br/>'.$query);
//return;
$query = db_query($query);
$first = true;
while ($rec = db_fetch_object($query)){
if($first){
$form[$type][] = array('#value'=>"And the winner is: ".print_r($rec, true).'<br/>');
$first = false;
}
else {
$form[$type][] = array('#value'=>print_r($rec, true).'<br/>');
}
}
// }
}

Loop through multi dimension array to show grid in PHP

I have 3 tables. Apls, Hulls and AplsHulls.
Apls consists of id, name, date
Hulls consists of id, production_name
AplsHulls is a join table and consists of id, apl_id, hull_id, status.
Not every Hull is associated with each Apl. The ones that are are in the join table with a status (shipped,in production, etc.)
I need to display a report in a table/grid that has the following columns headers: Apl Name, Apl_Date then the hull production names as the remaining column titles. (if hull 7 isn't in the result set, it doesn't even get a column.
For the data I need to list the apl name, apl date, then loop across the remaining columns and fill in the status for the records in the join table. If the apl and hull aren't associated in the join table, then just fill the cell with "NA".
I have tried this a bunch of different ways and I can currently get the dynamic list of column headers for the hulls, I never seem to be able to get the data to loop across correctly.
Sample Data:
Apls Table
Id: 1, Name: X1-0000, Date: 1/1/2009
Id: 2, Name: BG-5480, Date: 2/22/2009
Id: 3, Name: HG-0000, Date: 2/27/2009
Hulls Table
Id: 1, Production_name: ProdA
Id: 2, Production_name: ProdB
Id: 3, Production_name: ProdC
Id: 4, Production_name: ProdD
AplsHulls Table
Id: 1, Apl_id: 1, Hull_id: 1, Status:Delivered
Id: 2, Apl_id: 1, Hull_id: 3, Status:Ordered
Id: 3, Apl_id: 2, Hull_id: 4, Status:Delivered
I need the table to show like this:
APL | Date | ProdA | ProdC | ProdD
X1-0000 | 01/01/2009 | Delivered | Ordered | NA
BG-5480 | 02/22/2009 | NA | NA | Delivered
Notice the column headers ignore ProdB since that record wasn't in the join table at all. Also it fills in NA for the columns that are in the join table but it may not have an association to in the join table.
It is very confusing, I know.
You can get the list of hulls you care about with a query like this:
select h.id, pname from hulls h join aplshulls on (h.id=hull_id)
You can (and probably should) stop reading this answer now and just use that get the columns you care about and then figure out how to put the data you have into the table.
But once you have that list of hulls you care about you can have your program write some evil sql to build the result for you. The code below assumes your DB library returns an array of rows for your sql query.
$hulls = query("select h.id, pname from hulls h join aplshulls on (h.id=hull_id)");
/* This should give a result like:
* $hulls = array(array('id'=>1,'pname'=>'proda'),
* array('id'=>3,'pname'=>'prodc'),
* array('id'=>4,'pname'=>'prodd'));
*/
$sql = "select name, mdate";
foreach ($hulls as $row) {
$sql .= ", ifnull({$row['pname']},'NA') {$row['pname']}";
}
$sql .= " from apls ";
foreach ($hulls as $row) {
$sql .= " left join (select apl_id, status as {$row['pname']} from hulls h \join aplshulls ah on (h.id=hull_id) where pname='{$row['pname']}') as {$row['pn\ame']} on ({$row['pname']}.apl_id=apls.id)";
}
$sql .= " where apls.id in (select distinct apl_id from aplshulls)";
$result = query($sql);
foreach ($result as $row) {
print "<tr>";
foreach ($row as $value) {
print "<td>$value</td>";
}
print "</tr>\n";
}
Replace the calls to query with your database query methods.
The resulting sql is:
select name, date,
ifnull(proda,'NA') proda, ifnull(prodc,'NA') prodc, ifnull(prodd,'NA') prodd
from apls
left join (select apl_id, status as proda
from hulls h join aplshulls ah on (h.id=hull_id)
where pname='proda') as proda on (proda.apl_id=apls.id)
left join (select apl_id, status as prodc
from hulls h join aplshulls ah on (h.id=hull_id)
where pname='prodc') as prodc on (prodc.apl_id=apls.id)
left join (select apl_id, status as prodd
from hulls h join aplshulls ah on (h.id=hull_id)
where pname='prodd') as prodd on (prodd.apl_id=apls.id)
where
apls.id in (select distinct apl_id from aplshulls);
There is probably a better way to build the query but this should work. It probably breaks down if the number of hulls is very large. Or if any of the involved tables is very large.
If your product names aren't legal in the sql you will need to map them to something else.
Assuming that you've pulled the table data into a set of arrays:
<?php
$aplToHullMap = array();
foreach( $AplsHulls as $row )
{
$aplID = $row[ 'apl_id' ];
$hullID = $row[ 'hull_id' ];
$status = $row[ 'status' ];
if( isset( $aplToHullMap[ $aplID ] ) )
$aplToHullMap[ $aplID ][ $hullID ] = $status;
else
$aplToHullMap[ $aplID ] = array( $hullID => $status );
}
?>
<table>
<tr>
<th>Apl Name</th>
<th>Apl Date</th>
<?php
foreach( $Hulls as $row )
echo( "<th>" . $row[ 'production_name' ] . "</th>\r\n" );
?>
</tr>
<?php
foreach( $Apls as $row )
{
?>
<tr>
<td><?php echo( $row[ 'name' ] ); ?></td>
<td><?php echo( $row[ 'date' ] ); ?></td>
<?php
$map = $aplToHullMap[ $row[ 'id' ] ];
foreach( $Hulls as $hull )
{
if( isset( $map[ $hull[ 'id' ] ] ) )
$status = $map[ $hull[ 'id' ] ];
else
$status = 'NA';
echo( "<td>" . $status . "</td>\r\n" );
}
?>
</tr>
<?php
}
?>
</table>

Categories