MySQL query searching (advanced) - php

I have a course page, this page is setup to display the details (easy), then who is teaching it first, second, third, and forth period of each semester. The problem with that is, my teachers data is handled in one giant chain.
Bobby: 1-1-1-1-1-1-1-1
Tina: 20-20-20-10-1-1-1-1
Joey: 20-1-1-1-49-432-10-19
What I want to do is find a course: 20, when all of the information is shown a search through teachers would be made to find out who is teaching course 20 and what period?
Ex.
Course id: 20
Period 1: Joey, Tina
Period 2: Tina
Period 3: Tina
I want to get every teacher teaching that course in one search but given the obstacle of the capact data which may be a problem.
Teacher Table:
id / name / link / course (1-1-1-1-1-1-1-1)
Course Table:
id / name / code / grade / level
Teachers Course contains the id's from course Table

While re-structuring your db might be the best answer, I thought I'd post a straight php solution that works with your current structure and presumptively the rest of your code.
//I set up variables to contain your expected search results so I could test
$search_course = '20';
$search_results = array(
'Bobby' => '1-1-1-1-1-1-1-1',
'Tina' => '20-20-20-10-1-1-1-1',
'Joey' => '20-1-1-1-49-432-10-19'
);
//explode the course strings into arrays and store with teacher names so
//you can loop through them later
foreach($search_results as $teacher=>$string_courses){
$array_courses = explode('-',$string_courses);
$search_results[$teacher] = $array_courses;
}
//Match course you are searching for to the elements in your array
//Create a result array with period and matching teachers
foreach($search_results as $teacher=>$courses){
foreach($courses as $period => $course){
if($course == $search_course){
$results[$period][] = $teacher;
}
}
}
//Loop through your result array and show the results
//I expect you'll have different html for this
foreach($results as $period => $teachers){
echo 'Period: ';
echo $period+1;
echo implode(',',$teachers);
echo '<br>';
}
The printed results match the list you wanted in your OP

Create a new table
Something like this:
CREATE TABLE `TeacherToPeriod` (
`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`TeacherID` INT NOT NULL ,
`CourseID` INT NOT NULL ,
`Period` INT NOT NULL
) ENGINE = MYISAM ;
Insert the data
Here is some PHP-Code:
foreach($lines as $line){
$line_data = split($line, ': ');
$teacher = $line_data[0];
// SELECT your $teacher_id from the database
$courses = split($line_data[0], '-');
$i = 0;
foreach($courses as $course_id){
$i++;
$sql = "INSERT INTO `TeacherToPeriod` (`TeacherID` ,`CourseID` ,`Period`) ";
$sql.= "VALUES ($teacher_id, $course_id, $i);"
mysql_query($sql);
}
}
Select the data you want
SELECT * FROM `TeacherToPeriod` WHERE `CourseID` = 20 ORDER BY `Period` ASC;

You should change the structure of your db, instead of storing a string of all the periods, you should have an additional table with three columns: teacher,course,period and have a separate row in this table for each course that a teacher is teaching. Then determining who is teaching what course would simply be a matter of querying that table by course id and then sorting by period. e.g:
SELECT teacher_id, course_id, period FROM course_info WHERE course_id = 20
ORDER BY period;

Related

calculation within table row by row

I have a table in database as followings:
locationname postcode locationlatitude locationlongitude
--------------------------------------------------------
1. gsfs 2322. 352353. 35235235
2. gsfs 2322. 352443. 352353
3. gsfs 2322. 352353. 35235235
.
.
I want to calculate the distance between each location to all locations in the table(many to many). I have been able to compute one-to-many and one-to-one calculation but I couldn't figure out how to calculate distance from every location to every location. Do I have to use nested loop or not? Should I have to fetch all the data into an array first? Can you please help me out with this. I don't know whether my question is clear or not.
I have tried but I couldn't execute it due to errors and stupid mistakes.
$sql = "SELECT id, locationname, locationlatitude, locationlongitude, postcode FROM distancetable";
$result = $conn->query($sql);
$i=0;
$j=0;
while($row = mysql_fetch_assoc($result)) {
for ($row=0; $row <num_rows ; $row++) {
$lat1=$row[$i]["locationlatitude"];
$long1=$row[$i]["locationlongitude"];
$suburb1=$row[$i]["locationname"];
$lat2=$row[$j]["locationlatitude"];
$long2=$row[$j]["locationlongitude"];
$suburb2=$row[$j]["locationname"];
$distance=Round(ACOS(SIN(PI()*$long1/180)*SIN(PI()*$long2/180)+COS(PI()*$long1/180)*COS(PI()*$long2/180)*COS(PI()*$lat1/180-PI()*$lat2/180))*6371,3);
echo nl2br (" \n ");
echo $row[id]. ". Distance between " .$suburb1. " to " .$suburb2. "=" .$distance;
$i++;
$j++;
So I made a table like this:
create table latlongdata (
id int auto_increment primary key not null,
locationname varchar(35) not null,
postcode char(5) not null,
locationlatitude numeric(6, 3) not null,
locationlongitude numeric(6,3) not null
);
then I added some sample lat/long values like so:
insert into latlongdata (locationname, postcode, locationlatitude, locationlongitude)
VALUES
('Fenwick, MI', '48834',43.142,-85.049),
('Andover, MN', '55304', 45.255, -93.287),
('Minneapolis, MN', '55422', 45.015, -93.340),
('Lydia, SC', '29079', 34.296, -81.113);
then I made a query like so:
select
a.postcode as FromPostCode,
b.postcode as ToPostCode,
round(acos(sin(pi()*a.locationlongitude/180)*sin(pi()*b.locationlongitude/180)+cos(pi()*b.locationlongitude/180)*cos(pi()*a.locationlatitude/180-pi()*b.locationlatitude/180))*6731, 3) as distance
from latlongdata a
inner join latlongdata b
where b.id > a.id;
...which should be close to what you want, but I think there might be something wrong with your math since the results didn't make a lot of sense. But that should be how you do it.
Note the self-join with the inequality operator on the SELECT statement. This will prevent you from calculating B -> A after you've already calculated A -> B. Fair warning: With a large table, you should expect the result set for this to be... large. It might be better to directly insert the results into a new table (assuming you have sufficient storage space) than try to select out the results.

PHP - Searching for multiple rows with numbers/commas

I've made a user group and a user table in my database, called test and user_test. Every user has a field called groups which countains at least one number, but could also contain multiple numbers, for example 1,2,3. The user group table exists of id and group_name.
What I've been trying to do for so long now is to get data from all groups that this user is assigned to. For example, if one user is assigned to groups 1,2,3 (as its shown in the database), it will print out the name of each group with those id. So perhaps it'd print out group 1 group 2 group 3.
$user_test = $this->mysqli->query("SELECT user_id,groups FROM user_test WHERE user_id = '1'");
while($user_test_fetch = $user_test->fetch_array()) {
$groups = $user_test_fetch["groups"];
}
if(strlen($groups) > 1) { // user has more than 1 group
// ???
} else { // user does not have more than 1 group
$search = "id = '".$groups . "'";
}
$group_data = $this->mysqli->query("SELECT * FROM test WHERE ".$search."");
while($group_data_fetch = $group_data->fetch_array()) {
echo $group_data_fetch["group_name"];
}
Or if you have any other way you'd do this task, please feel free to show me! I'm just simply trying to learn how to do this task (preferably, as efficient as possible).
Pretty simple.
If it is stored in the 1,2,3 format. And assuming a single one is called 1 and id is an INT or BIGINT (otherwise this query will slow down if it is a VARCHAR)
Change
if(strlen($groups) > 1) { // user has more than 1 group
// ???
} else { // user does not have more than 1 group
$search = "id = '".$groups . "'";
}
to
$search = "id IN (".$groups.");
this single line will work with a single group or a set of groups, as long as it is separated by a comma (Because SQL loves commas)

Join table where first table primary key is contained multiple times in second table

I have a little doubts.
Is it possible to done this with mysql or i have to do another query while after i get result from user table.
I have table users
id name
I have table phonenumbers
phonenumberid userid and phone
A user can have multiple phone numbers which will be stored in phonenumbers table with foreign key userid.
Example.
If the user will be on edit personal info page and there should be display all phone numbers which he have inserted to delete them to edit etc...
So how can i get all phonenumers in one query (WHERE users.id = 1);
Or i should select first basic informations in the users table and then make another separated query to get and store in array userphonenumbers or push in the user table result array.
I hope that we will get with good examples.
EDIT :
Here is example query
SELECT users.id, phonenumbers.userid, phone, name
FROM users
LEFT JOIN phonenumbers ON phonenumbers.userid = users.id
WHERE users.id = 1;
I have 2 phonenumbers in phonenumbers table with foreignkey(userid) = 1 But when I make the query I got duplicate rows with the same name and when I do the foreach loop I will get duplicates.
I want them to be grouped like for userid = 1 have phones 545454 and 324398493
Result Example
id name phone userid
1 John 4112258 1
1 John 5698745 1
If user have 10 phones, 10 additional rows will be added and in the loop all thoose rows will be repeated, but its not necessary, will be acted like multiple users
With relational algebra based approach you can not get from a select performing the Cartesian product of two tables by connecting the user id that you provide an answer for every customer a single line with a number of variables potentially phone numbers.
If you want to get a result like this the easiest way is to use a procedure that carries out a query on just user and then recursively for each user perform a subquery on telephone numbers on this result can intervene to format the result as you want
You may find this useful?
connection.php
function db(){
return new mysqli('host', 'username', 'password', 'database');
}
page.php
include_once 'connection.php'; $db = db(); $a = array();
if($uq = $db->query('SELECT id,name FROM users')){
if($uq->num_rows > 0){
while($u = $uq->fetch_object()){
$u->phones = array();
if($q = $db->query("SELECT phonenumberid,phone FROM phonenumbers WHERE userid={$u->id}")){
if($q->num_rows > 0){
while($o = $q->fetch_object()){
$u->phones[] = $o;
}
}
$q->free();
}
$a[] = $u;
}
}
$uq->free();
}
print_r($a);
$db->close();
I can't see where your SQL result is wrong. It is doing what it is supposed to do. It sounds like you want it to look like a human readable report. You have to do more than the SQL, if that is the case. Is that what you mean?
Modified from what I originally modified from somewhere on SO ($obj was passed to my view here):
<?PHP if (!empty($obj)):
$previous_heading = false;
?>
some_markup like <table>
<?PHP foreach ($obj as $key => $value): ?>
<tr>
<?PHP if (!$previous_heading || $value['user.id'] != $previous_heading): ?>
Do markup stuff with the heading like display the name of the user
<td>
<?PHP $value['my_column'];
</td>
<?PHP end if; ?>
..more markup...
<td>
<?PHP $value['phone_number'];
</td>
</tr>
<?PHP
//do other stuff that is not related to your heading, "John," like display phone numbers.
$previous_heading = $value['user.id'];
end foreach;
?>
</table>
...ending markup tags....

Computing an average from different tables

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

Summing a field from all tables in a database

I have a MySQL database called "bookfeather." It contains 56 tables. Each table has the following structure:
id site votes_up votes_down
The value for "site" is a book title. The value for "votes_up" is an integer. Sometimes a unique value for "site" appears in more than one table.
For each unique value "site" in the entire database, I would like to sum "votes_up" from all 56 tables. Then I would like to print the top 25 values for "site" ranked by total "votes_up".
How can I do this in PHP?
Thanks in advance,
John
You can do something like this (warning: Extremely poor SQL ahead)
select site, sum(votes_up) votes_up
from (
select site, votes_up from table_1
UNION
select site, votes_up from table_2
UNION
...
UNION
select site, votes_up from table_56
) group by site order by sum(votes_up) desc limit 25
But, as Dav asked, does your data have to be like this? There are much more efficient ways of storing this kind of data.
Edit: You just mentioned in a comment that you expect there to be more than 56 tables in the future -- I would look into MySQL limits on how many tables you can UNION before going forward with this kind of SQL.
Here's a PHP code snip that should get it done.
I have not tested it so it might have some typos and stuff, make sure you replace DB_NAME
$result = mysql_query("SHOW TABLES");
$tables = array();
while ($row = mysql_fetch_assoc($result)) {
$tables[] = '`'.$row["Tables_in_DB_NAME"].'`';
}
$subQuery = "SELECT site, votes_up FROM ".implode(" UNION ALL SELECT site, votes_up FROM ",$tables);
// Create one query that gets the data you need
$sqlStr = "SELECT site, sum(votes_up) sumVotesUp
FROM (
".$subQuery." ) subQuery
GROUP BY site ORDER BY sum(votes_up) DESC LIMIT 25";
$result = mysql_query($sqlStr);
$arr = array();
while ($row = mysql_fetch_assoc($result)) {
$arr[] = $row["site"]." - ".$row["sumVotesUp"];
}
print_r($arr)
The UNION part of Ian Clelland answer can be generated using a statement like the following. The table INFORMATION_SCHEMA.COLUMNS has a column TABLE_NAME to get all tables.
select * from information_schema.columns
where table_schema not like 'informat%'
and column_name like 'VOTES_UP'
Join all inner SELECT with UNION ALL instead of UNION. UNION is doing an implicit DISTINCT (on oracle).
The basic idea would be to iterate over all your tables (using a SQL SHOW TABLES statement or similar) in PHP, then for every table, iterate over the rows (SELECT site,votes_up FROM $table). Then, for every row, check the site against an array that you're building with sites as keys and votes up as values. If the site is already in the array, increment its votes appropriately; otherwise, add it.
Vaguely PHP-like pseudocode:
// Build an empty array for use later
$votes_array = empty_array();
// Get all the tables and iterate over them
$tables = query("SHOW TABLES");
for($table in $tables) {
$rows = query("SELECT site,votes_up FROM $table");
// Iterate over the rows in each table
for($row in $rows) {
$site = $row['site'];
$votes = $row['votes_up'];
// If the site is already in the array, increment votes; otherwise, add it
if(exists_in_array($site, $votes_array)) {
$votes_array[$site] += $votes;
} else {
insert_into_array($site => $votes);
}
}
}
// Get the sites and votes as lists, and print out the top 25
$sorted_sites = array_keys($votes_array);
$sorted_votes = array_values($votes_array);
for($i = 0; $i < 25; $i++) {
print "Site " . $sorted_sites[$i] . " has " . $sorted_votes[$i] . " votes";
}
"I allow users to add tables to the database." - I hope all your users are benevolent and trustworthy and capable. Do you worry about people dropping or truncating tables, creating incorrect new tables that break your code, or other things like that? What kind of security do you have when users can log right into your database and change the schema?
Here's a tutorial on relational database normalization. Maybe it'll help.
Just in case someone else that comes after you wants to find what this could have looked like, here's a single table that could do what you want:
create database bookfeather;
create user bookfeather identified by 'bookfeather';
grant all on bookfeather.* to 'bookfeather'#'%';
use bookfeather;
create table if not exists book
(
id int not null auto_increment,
title varchar(255) not null default '',
upvotes integer not null default 0,
downvotes integer not null default 0,
primary key(id),
unique(title)
);
You'd vote a title up or down with an UPDATE:
update book set upvotes = upvotes + 1 where id = ?
Adding a new book is as easy as adding another row:
insert into book(title) values('grails in action')
I'd strongly urge that you reconsider.

Categories