EDIT: Added the first SQL query.
A section of my website has two dropdown menus. All the options in both are populated using SQL queries. Dropdown#1 is a list of class sections (like A1 for example). Once the professor selects a section, Dropdown#2 is populated with the student ID's (like 1234567 for example).
Student information is found in table 1. Among this information is the 'professorName' column. In order to associate the student with a class section, I need to match 'professorName' column with an identical column found in table 2, because class sections are only found in table 2.
Till here everything works great, because at the end of my query I put ORDER BY student ID. However, two of the class sections are associated to two different professors. In order to deal with this issue, I used the following code to loop through each professor name.
$from site = $_POST['section'];
$query = ("SELECT professorName FROM Table 2 WHERE classSection='$fromsite'");
$NumberofProfessorNames = $objMSSQL->getAffectedRows();
echo $NumberofProfessorNames;
for ($j=0; $j<$NumberofProfessorNames; $j++)
{
$section= $query[$j][professorName];
$output = $objMSSQL->getTable("SELECT DISTINCT StudentID from table1 WHERE professorName='$section' ORDER BY StudentID");
for ($i=0; $i<$objMSSQL->getAffectedRows(); $i++)
{
echo "<option value='".$output[$i][studentID]."'>".$output[$i][studentID]."</option>";
}
}
The problem is that for the only two sections where this is even necessary (because there are two professorNames), since it is looping like this, it is ending up ordered like this in the dropdown#2:
1234567
2345678
3456789
4567890
1234123
2345765
3456999
4567000
My limited experience in programming is keeping me from understanding how I can fix this seemingly simple issue.
Thank you for your help.
Rather than loop over the professors and query table1 for each, join table1 and table2 in the second query and only query the database once. For example:
$query = [... FROM Table2...];
$NumberofProfessorNames = $objMSSQL->getAffectedRows();
echo $NumberofProfessorNames;
$output = $objMSSQL->getTable("
SELECT DISTINCT StudentID
from table1
join table2
on ...
WHERE [the same clause you used in $query]
ORDER BY StudentID"
);
for ($i=0; $i<$objMSSQL->getAffectedRows(); $i++)
{
echo "<option value='".$output[$i][studentID]."'>".$output[$i][studentID]."</option>";
}
It's more elegant (and almost certainly more efficient) than generating a WHERE IN clause.
Yu can do it this way:
$section = "('";
for ($j=0; $j<$NumberofProfessorNames; $j++)
{
$section.= $query[$j][professorName] . "','";
}
$section = substr($section, 0, -3) . ')'; //$section contains ('prof1','prof2')
$output = $objMSSQL->getTable("SELECT DISTINCT StudentID from table1 WHERE professorName IN $section ORDER BY StudentID");
for ($i=0; $i<$objMSSQL->getAffectedRows(); $i++)
{
echo "<option value='".$output[$i][studentID]."'>".$output[$i][studentID]."</option>";
}
that is querying for all your professors in just one sql with IN() syntax.
UPDATE: I've just noted you use sql server instead of mysql, so I've changed the IN() syntax a bit and change the link to the sql server help docs.
It sounds like your tables aren't normalized. Good form would have a sections table, a students table, and a professors table. Information in each table should be specific to the table's topic.
students
student_id
student_last_name
student_first_name
student_address
etc
sections
section_id
section_name - multiple sections can tend to have the same name but differing content
section_description
section_year - sections can change from year to year
faculty
faculty_id
faculty_name - this is not a key field, more than one person can have the same name.
faculty_address
faculty_type - adjunct, fulltime, etc.
You would then have relational tables so you can associate professors with sections and students with sections.
faculty_2_sections
f2s_id
faculty_id
section_id
student_2_sections
s2s_id
student_id
section_id
This makes it super simple because if a student is logged in, then you already have their student id. If it's a professor, you already have their faculty_id
If you're pulling for students, your sql might look like this:
$sql = "select * from students s,sections sc,faculty f,faculty_2_sections f2s,student_2_sections s2s where student_id='$student_id' and s2s.student_id=s.student_id and s2s.section_id=sc.section_id and f2s.faculty_id=f.faculty_id and f2s.section_id=s2s.section_id";
If you're pulling for faculty you would do this:
$sql = "select * from students s,sections sc,faculty f,faculty_2_sections f2s,student_2_sections s2s where faculty_id='$faculty_id' and f2s.faculty_id=f.faculty_id and f2s.section_id=s2s.section_id and s2s.section_id=sc.section_id and s2s.student_id=s.student_id";
You can then pull a list of sections to populate the section_ids pull-down to only show students or faculty for a specific section.
Related
I am currently working on a school time-table management system wherein the student dashboard, Time-table is displayed according to the current day, but when displaying the data, two duplicates come out of nowhere.
Initially, I joined the tables 'students' and 'timetable' through an 'INNER JOIN', but the problem with it was duplicates being placed simultaneously which was later fixed with 'RIGHT JOIN'.
Now, the duplicates display after the entire table has been printed.
HERE IS THE CODE BELOW:-
<?php
require_once("connection.php");
function information($subject, $time, $day, $teacher){
$element = "
<tr>
<td>$subject</td>
<td>$time</td>
<td>$day</td>
<td>$teacher</td>
</tr>
";
echo $element;
}
function getData(){
global $conn;
$sql = '
SELECT DISTINCT students.roll_no
, students.class_room
, timetable.subject_name
, timetable.time_code
, timetable.day_otw
, timetable.teacher_name
FROM students
RIGHT
JOIN timetable
ON students.class_room = timetable.room_no;
';
$result = mysqli_query($conn, $sql);
$mydate=getdate(date("U"));
if(mysqli_num_rows($result) > 0){
while($row=mysqli_fetch_assoc($result)){
if($mydate['weekday'] == $row['day_otw']){
information($row['subject_name'], $row['time_code'], $row['day_otw'], $row['teacher_name']);
}
}
} else {
echo "
<h1 class=\"check-data\">Sorry, You haven't been assigned a class yet!</h1>
<h3 class=\"ask\">Please contact your teacher/supervisor for more information.</h3>
";
}
}
getData();
?>
SO BASICALLY THE ENTIRE TABLE IS BEING DUPLICATED TWICE.
Outer joining makes no sense here. You neither want students without a timetable entry (LEFT OUTER JOIN), nor timetable entries without students (RIGHT OUTER JOIN). Then you are using DISTINCT because you are fightig duplicates, which is a bad idea, because thus you don't examine where the duplicates stem from. There should be no duplicates in the first place, so probably there is something wrong with the join criteria or data. You should fix this instead of looking for inappropriate workarounds.
Your join criteria is that the student is in the same room as the timetable entry. Why is the student associated a room? Do all the student's classes take place in the same room? In that case, yes, a class room would mean a class and then the tables would be related by the class (room). But if that were the case, you would get no duplicates.
I assume there to be something else to relate students with the timetable. Look for a student ID in the timetable. Something like:
SELECT s.roll_no, s.class_room, t.subject_name, t.time_code, t.day_otw, t.teacher_name
FROM students s
JOIN timetable t ON s.student_id = t.student_id
ORDER BY s.student_id, t.day_otw, t.time_code;
Or maybe there is something like an additional class ID, both student and timetable belong to? (The room in both tables would look strange then, however.) Maybe you don't know the database well enough. If this is the case, ask somebody who does.
From your screen, you only need to display timetable data, so there is no point to link to student table
Please consider using the following instead of your right join
SELECT
timetable.subject_name, timetable.time_code,
timetable.day_otw, timetable.teacher_name
FROM timetable
As per the title, I'm running the following SQL query:
$sql = "SELECT `Policy Area`, SUM(`Sum Approved`) as `Sum Approved`
FROM Contracts GROUP BY `Policy Area`";
$result = $conn->query($sql);
if ($result->num_rows > 0) {
while($row = $result->fetch_assoc()) {
echo "Policy Area:". $row["Policy Area"]. " Sum: ". $row["Sum Approved"]."<br>";
}
} else {
echo "0 results";
}
Simple enough, and I'm going to be coding it up so it essentially creates a bar chat showing percentage spend by policy area. However, the Policy Area is stored in the Contracts Table as an ID, which itself relates to another Table where the actual name is.
Obviously, I would much rather have the full name as opposed to the ID, but what's the best way of accomplishing?
Is it simply a case of building the required extra SQL queries to the Policy Area table into the while loop?
You can do a join to the other table (assumed to be named Policy here) and include that in the rollup:
select c.PolicyArea, p.PolicyName, SUM(c.SumApproved) as SumApproved
from Contracts inner join Policy on c.PolicyArea = p.PolicyArea
GROUP by c.PolicyArea, p.PolicyName
This will give you 1 resultset with columns of "Policy Area", "Policy Name", and "SumApproved".
I'm making up the other table name and fields where the Policy name is decoded from the Id, as well as the JOIN, but you get the idea.
I have updated my original post based on what I learned from your comments below. It is a much simpler process than I originally thought.
require '../database.php';
$pdo = Database::connect();
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$sql = "SELECT * FROM Orders WHERE id = 430";
$q = $pdo->prepare($sql);
$q->execute(array($id));
$data = $q->fetch(PDO::FETCH_ASSOC);
echo 'Order Num: ' . $data['id'] . '<br>';
$sql = "SELECT * FROM Order_items
JOIN Parts ON Parts.id = Order_Items.part_id
WHERE Order_Items.orders_id = 430";
$q = $pdo->prepare($sql);
$q->execute(array($line_item_id));
$data = $q->fetch(PDO::FETCH_ASSOC);
while ($data = $q->fetch(PDO::FETCH_ASSOC))
{
echo '- ' . $data['part_num'] . $data['qty'] . "<br>";
}
Database::disconnect();
Unfortunately, only my first query is producing results. The second query is producing the following ERROR LOG: "Base table or view not found: 1146 Table 'Order_items' doesn't exist" but I am expecting the following results.
Expected Results from Query 1:
Order Num: 430
Expected Results from Query 2:
- Screws 400
- Plates 35
- Clips 37
- Poles 7
- Zip ties 45
Now that I understand where you are coming from, let's explain a couple of things.
1.PDO and mysqli are two ways of accessing the database; they essentially do the same things, but the notation is different.
2.Arrays are variables with multiple "compartments". Most typical array has the compartments identified by a numerical index, like:
$array[0] = 'OR12345'; //order number
$array[1] = '2017-03-15'; //order date
$array[2] = 23; //id of a person/customer placing the order
etc. But this would require us to remember which number index means what. So in PHP there are associative arrays, which allow using text strings as indexes, and are used for fetching SQL query results.
3.The statement
$data = $q->fetch(PDO::FETCH_ASSOC)
or
$row = $result->fetch_assoc()
do exactly the same thing: put a record (row) from a query into an array, using field names as indexes. This way it's easy to use the data, because you can use field names (with a little bit around them) for displaying or manipulating the field values.
4.The
while ($row = $result->fetch_assoc())
does two things. It checks if there is a row still to fetch from the query results. and while there is one - it puts it into the array $row for you to use, and repeats (all the stuff between { and }).
So you fetch the row, display the results in whatever form you want, and then loop to fetch another row. If there are no more rows to fetch - the loop ends.
5.You should avoid using commas in the FROM clause in a query. This notation can be used only if the fields joining the tables are obvious (named the same), but it is bad practice anyway. The joins between tables should be specified explicitly. In the first query you want the header only, and there is no additional table needed in your example, so you should have just
SELECT *
FROM Orders
WHERE Orders.Order_ID = 12345
whereas in the second query I understand you have a table Parts, which contains descriptions of various parts that can be ordered? If so, then the second query should have:
SELECT *
FROM Order_items
JOIN Parts ON Parts.ID = Order_Items.Part_ID
WHEERE Order_Items.Order_ID = 12345
If in your Orders table you had a field for the ID of the supplier Supplier_ID, pointing to a Suppliers table, and an ID of the person placing the order Customer_ID, pointing to a Customers table, then the first query would look like this:
SELECT *
FROM Orders
JOIN Suppliers ON Suppliers.ID = Orders.Supplier_ID
JOIN Customers ON Customers.ID = Orders.Customer_ID
WHERE Orders.Order_ID = 12345
Hope this is enough for you to learn further on your own :).
I have a table that is being displayed in a form, which looks like this: http://puu.sh/5VBBv.png
The end of the table, City, is displaying an ID of a city, which is supposed to be linked to another table (http://puu.sh/5VBIG.png).
What I've done for my INNER JOIN query is:
mysql_query("SELECT * FROM cities INNER JOIN people ON people.cityid = cities.id") or die(mysql_error());`
and I'm trying to output it into a table:
echo "<td>" . $row['cityid'] . "</td>";
My issue is that I'm not quite sure how to actually display the cityid that corresponds to the city name. I've tried using ['name'] and other values as the value to output in the table, and I can't find any solution for this anywhere so far. I'm just learning joins, so I don't exactly have any knowledge on what I could be doing wrong. Is there anything immensely obvious?
First your use of * in the join query could be ambiguous. If cities had a name column and people had a name column, you won't know which one you're getting. Second you can do this a couple ways. I think you're trying to get a city id from a city name. If that's correct you can either make and ajax call and query it directly or define an array as follow:
$res = mysql_query("...");
$city_ids = Array();
while ($ary = mysql_fetch_assoc($res)) {
city_ids[$ary['name']] = Ary['id'];
}
Then when you get the name, you just loop up $ary['name'].
Selecting from people and joining cities makes more sense. Then select the fields you need.
SELECT people.*, cities.name as city_name FROM people JOIN cities ON people.cityid = cities.id
Then, echo $row['city_name']
Select ID from cities city, people person where person.ID = city.ID
You are selecting the ID from both Cities and People, and joining on the ID
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