I am sorry for a verlo long question, just trying to explain in details. My formatting is not very good, sorry for that as well. I had a PHP/ MySQL App that essentially was not truly relational as I had one large table for all student scores. Among other things, I was able to calculate the average score for each subject, such that the average appeared alongside a student's score. Now I have since split the table up, to have a number of tables which I am successfully querying and creating School Report Cards as before. The hardship is that I can no longer calculate the avaerages for any subject.
Since I had one table with 5 subjects and each of the subjects had 2 tests, I queried for data and calculated the average as follows:
The one table (Columns):
id date name exam_no term term year eng_mid eng_end mat_mid mat_end phy_mid phy_end bio_mid bio_end che_mid che_end
The one query:
$query = "SELECT * FROM pupils_records2
WHERE grade='$grade' && class='$class' && year = '$year' && term ='$term'";
$result = mysqli_query($dbc, $query);
if (mysqli_num_rows($result) > 0) {
$num_rows=mysqli_num_rows($result);
while($row = mysqli_fetch_array($result)){
//English
$eng_pupils1{$row['fname']} = $row['eng_mid'];
$eng_pupils2{$row['fname']} = $row['eng_end'];
$mid=(array_values($eng_pupils1));
$end=(array_values($eng_pupils2));
$add = function($a, $b) { return $a + $b;};
$eng_total = array_map($add, $mid, $end);
foreach ($eng_total as $key => $value){
if ($value==''){
unset ($eng_total[$key]);
}
}
$eng_no=count($eng_total);
$eng_ave=array_sum($eng_total)/$eng_no;
$eng_ave=round($eng_ave,1);
//Mathematics
$mat_pupils1{$row['fname']} = $row['mat_mid'];
$mat_pupils2{$row['fname']} = $row['mat_end'];
$mid=(array_values($mat_pupils1));
$end=(array_values($mat_pupils2));
$add = function($a, $b) { return $a + $b;};
$mat_total = array_map($add, $mid, $end);
foreach ($mat_total as $key => $value){
if ($value==''){
unset ($mat_total[$key]);
}
}
print_r($mat_total);
$mat_no=count($mat_total);
echo '<br />';
print_r($mat_no);
$mat_ave=array_sum($mat_total)/$mat_no;
$mat_ave=round($mat_ave,1);
}
}
//Biology
etc
I split the table into separate tables and have names in a separate table, not needed for calculating avaerages, so I will not show it here. Each subject table tajkes the following form:
id date exam_no term year grade class test*
*Test would be eng_mid or eng_end or mat_mid etc.
Because I had only one query which returned 10 rows (5 subjects each with two tests: e.g. eng_mid (English Mit exam), eng_end (english end of term test), I was able to capture all rows in one call and pack each subject into an array, and then work out the class average, with the help of array_map. It may not be elegant, but it worked very well. Now, I have each test in it's own table.
I was trying to write a joint so as to get a signle resultset but the query fails. The columns as like:
I know that the database design is not anything to be proud off, but coming from a huge single table, this is a massive step (worthy a pat on the shoulder).
What I wish to do is to be able to query all my data and calculate class averages (about 30 students in each class). I tried to use separate queries but I ran into a wall, in that previously I would use the WHILE conditional as shown after the query for it to pull all rows and create an array from which I could get desired results. Now several queries just makes me confused as to how I can archieve the same results since a join is not working. Also I am having a separate $row variable, and that throws me further off balance!
Is it even possible to do averages as I did on my infamous one table (from the dark side) or is my table design so messed up, what I want just isn't humanly possible?
Please any help will be deeply appreciated.
Try using union. It would be something like
select grade, test from math
union all
select grade, test from english
union all
....
Also, in my opinion, better design would be to have table exams something like that (warning, pseudo-DML):
id int primary key,
student_id int foreign key students
subject_id int foreign key subjects
exam_type_id int foreign key exam_types
grade int(????)
exam_types table would be just midterm and final, but you'll be able to easily support more types in future, if required.
subjects table will store all kinds of subjects you have (at this time there will be only five of them: math, eng, phy, etc.
The averaging query would be as simple as (yes, you can actually do aggregation in the query itself)
select student_id, avg(grade)
from exams
group by student_id
Related
this is my first question. Apologies if I don't conform with the required rules. I have tried my best and
Problem--
I have a problem with a project am working on. It is medical based. I have these three tables that I need to get reports from
DB structure--
diseaseTable which has columns
diseaseID | diseaseName
diseaseID stores an integer i.e 1, 3, 4,..
dieseaseName stores name of disease e.g Hypertension, Malaria, ..e.tc
I also have patientsTable which has these key columns
patientID - which is an autoincrement int
patientName - name of patient
created_at - date and time of registration of the patient
Lastly there is treatment table which contains
treatmentID -autoincrement int
patient - contains the id of the patient which is a foreing key of patientsTable
created_at and updated_at for timestamps
diseaseTreated (which is my column of interest) Stores values in json comma separated fields.
I have already checked all results from https://stackoverflow.com/search?q=mysql+comma+separated+fields but none seems to solve my problem. I have checked on ways to restructure mysql schema but i would have too many tables to store the diseases diagnised from each patient. In addition one patient might be diagnosed with multiple diseases and a disease can be added any time. I might have almost 100 of them. If ID of Hypertension is 1 and arthritis is 2 a patient with both will have the
diseaseTreated field with "1","2" in the treatment table
Solution Expected--
I need to get reports for first time a specific disease has been treated for the first time or second time and more i.e revisit for treatment of the same disease (if i wanted to know how many cases of hypertension are for the first time, and how many cases of hypertension are recurrent)
Example - New cases report
Disease | New Cases
Hypertension | 4
Example - Recurrent cases report
Disease | New Cases
Hypertension | 1
Arthritis | 2
Am using Laravel I have attempted a lot of solutions but this is what i find too close to get what I need: Others I have attempted was looping through different array results i have queried from the database by using joins but I havent posted them because they have yielded nothing important.
$diseaseID
$countedDiseaseCases = DB::select('select * from treatmentTable where JSON_CONTAINS(diseases, \'[\"'.$diseaseID.'\"]\')')
This one would get me the rows with a specific disease and by using php count() on the result I get an int as 3 which is OK but I haven't got to put it as I expect filter and differentiate new and recurrent cases.
Thanks,
Any suggestion will be highly appreciated
If the deseaseTreated column is json, couldn't you add an isNew key and when the patient comes in for a second visit, flip that key to false or something similar?
Now when you want to make the report you can get counts on all of them.
//example deseaseTreated value
{
"deseases": [
{
"deseaseID":"Value",
"isNew":Boolean,
...
},
{...}
]
}
$records = DB::select('select * from treatment');
$diseases = DB::select('select * from diseases');
foreach ($diseases as $disease) {
$disease['count_new'] = 0;
}
foreach ($records as $record) {
$json = json_decode($record['diseaseTreated']);
foreach ($json['diseases'] as disease_index) {
if ($disease_index['isNew']) {
foreach ($diseases as &$d) {
if ($d['diseaseID'] == $disease_index['diseaseID']) {
$d['count_new']++;
}
}
}
}
I've read a million complicated questions on sorting arrays. I have something super simple, but I'm just not able to wrap my head around it.
I have a table in my db that has scores for games. The column labels are team1_score and team_2 score. I realize that if all the scores were in one column, I could sort them with my SQL query, but they're not.
I need to know how to fetch the results from those columns, and sort them highest to lowest and ideally assign those to variables such as $first_place and $second_place
I'm sorry I'm a noob and I've done a lot of research before coming on here, so please be gentle.
So, I have something like this...
My program keeps track of scores at a kids camp and there are multiple camps. Each row has id, camp_name, camp_logo and then goes into team1_name, team1_logo, team1_score and so on through 10 teams. Ideally, I'd like to have the query fetch all those scores, and output them in Descending order with something like First Place: xxx points (team name) (team logo)
I can sort the scores with this...
$query = "SELECT team1_score, team2_score FROM camps ";
$scores = mysqli_query($connection,$query);
$scores_array = mysqli_fetch_assoc($scores);
arsort($scores_array);
foreach ($scores_array as $key => $value) {
echo "score - [" . $key . "] = " . $value . "\n";
}
but I don't know how to associate the name and logo with those keys. I hope that makes sense.
This can actually done by SQL. You could use greatest and least to get the top and bottom scores, respectively, and then also sort by them:
SELECT GREATEST(team1_score, team2_score),
LEAST(team1_score, team2_score)
FROM camps
ORDER BY 1 DESC, 2 DESC
I am in the beginning of building a simple absence script for teachers to administrate students.
I need to populate the same list of students in 5 tables. One table for each day of the current week (monday to friday).
I have the following code at the moment
List students SQL query:
SELECT s.student_id, s.student_firstname, s.student_lastname,
a.student_absence_startdate, a.student_absence_enddate, a.student_absence_type
FROM students s
LEFT JOIN studentabsence a ON a.student_id = s.student_id
WHERE a.student_absence_startdate
IS NULL OR
'2012-06-20'
BETWEEN
a.student_absence_startdate AND a.student_absence_enddate
This query selects all students and joins another table wich is a linking table that holds the absence information for students.
I the populate the code 5 times like this:
<?php
$data = array();
while ($row = mysql_fetch_array($list_students)) {
$data[] = $row;
}
?>
<table>
<?php
foreach($data as $row){
$class = "";
if ($row['student_absence_type'] == 2) {$class = " style='background:#f00;'";}
else if ($row['student_absence_type'] == 3) {$class = " style='background:#8A2BE2;'";}
echo "<tr><td$class>";
echo "<a class='ajaxlink' href='#' rel='pages/ajaxAbsence.php?student_id=".$row['student_id']."'>".$row['student_firstname']." ".$row['student_lastname']."</a>";
echo "</td></tr>\n";
}
?>
</table> <!-- Repeat -->
My question is how I should manage the date functions.
As you can see in my SQL query I have hardcoded the dates. I need it to automatically select the date of wich table the students are listed.
for example:
table > Monday (2012-07-23)
the user John Doe has absence information in this span, based on the above query. Let's change the background of <td>
table > Tuesday (2012-07-24)
the user John Doe has no absence information in this span. Just list him.
etc...
I am not expecting you to write my code. I am just curious on how to think. I am fairly new to programming.
The most flagrant error I find in your thoughts is that you don't need those five tables. You'll end up repeating and messing data.
You need different tables, from what I've read.
Students
ID, Name, etc..
Classes
class_id, name,
Teachers
Id_teacher, name, etc
TeachersInClass (so you can now which teachers gave a specific class)
id, id_teacher, id_class
Absences
id, id_student, id_class, date
This leaves you a more robust database, and you can play arroud with multiple associations like, which teachers had the most absences in a year... etc etc.
EDIT: Forgot to answer your "real" question.
You can use the PHP DateTime class to manipulate your dates. It's easy to use, and the php.net has a lot of good examples to give you a boost start
I have a table that has patient information (name, dob, ssn, etc.) and a table that has lists of medications that they take. (aspirin, claritin, etc.) The tables are related by a unique id from the patient table. So, it's easy enough to pull all of Mary Smith's medications.
But, what I need to do is to show a paginated list of patients that shows their name, other stuff from the patient table and has a column with a line-separated list of their medications. Roughly, this:
If I do a simple left join, I get 3 repeated rows of Mary Smith with one medication per row.
The patient table can have thousands of records, so I don't want to do a query to get all the patients and then loop through and get their meds. And, because it's paginated based on patient, I can't figure out how to get the correct number of patients for the page, along with all their medications.
(The patients/medications thing is just a rough example of the data; so please don't suggest restructuring how the data is stored.)
GROUP_CONCAT to the rescue!
SELECT patients.first_name, patients.last_name, GROUP_CONCAT(prescriptions.medication SEPARATOR ", ") AS meds FROM patients LEFT JOIN prescriptions ON prescriptions.patient_id = patients.id GROUP BY patients.id;
You've got a few choices.
rowspan clauses with one drug per cell per user. You'd need to run two SQL queries to precalculate how big each user's span would have to be, or suck the query results into PHP and do the counting there.
Simple state machine - start a new row each time the user changes, then just keep adding more drug names seperated with <br /> while the user's name stays constant.
The second one's probably easiest:
$previous_name = null;
$first = true;
echo "<table";
while($row = mysql_fetch_assoc($results)) {
if ($row['name'] <> $previous_name) {
if (!$first) {
echo "</td></tr>"; // end previous row, if it's not the first row we've output
$first = false;
}
echo "<tr><td>$row[name]</td><td>"
$previous_name = $row['name'];
}
echo "$row['drug']<br />";
}
echo "</td></tr></table>";
I think what you are looking for is referred to a 'concation of subquery'.
Check http://forums.mysql.com/read.php?20,157425,157796#msg-157796 and http://mysql.bigresource.com/SELECT-CONCAT-Subquery-S5cIpzqO.html
Alright, so I have a table outputting data from a MySQL table in a while loop. Well one of the columns it outputs isn't stored statically in the table, instead it's the sum of how many times it appears in a different MySQL table.
Sorry I'm not sure this is easy to understand. Here's my code:
$query="SELECT * FROM list WHERE added='$addedby' ORDER BY time DESC";
$result=mysql_query($query);
while($row=mysql_fetch_array($result, MYSQL_ASSOC)){
$loghwid = $row['hwid'];
$sql="SELECT * FROM logs WHERE hwid='$loghwid' AND time < now() + interval 1 hour";
$query = mysql_query($sql) OR DIE(mysql_error());
$boots = mysql_num_rows($query);
//Display the table
}
The above is the code displaying the table.
As you can see it's grabbing data from two different MySQL tables. However I want to be able to ORDER BY $boots DESC. But as its a counting of a completely different table, I have no idea of how to go about doing that.
There is a JOIN operation that is intended to... well... join two different table together.
SELECT list.hwid, COUNT(log.hwid) AS boots
FROM list WHERE added='$addedby'
LEFT JOIN log ON list.hwid=log.hwid
GROUP BY list.hwid
ORDER BY boots
I'm not sure if ORDER BY boots in the last line will work like this in MySQL. If it doesn't, just put all but the last line in a subquery.
But the result of the query into an array indexed by $boots.
AKA:
while(..){
$boot = mysql_num_rows($query);
$results[$boot][] = $result_array;
}
ksort($results);
foreach($results as $array)
{
foreach($array as ....)
{
// display table
}
}
You can switch between ksort and krsort to switch the orders, but basically you are making an array that is keyed by the number in $boot, sorting that array by that number, and then traversing each group of records that have a specific $boot value.