I am building a project with an organization chart using Codeigniter + MySQL + Active Record.
There are departments listed as organization tree, Staff for information of the persons, Staff Roles and Staff_Departments where i store the matching:
Department - Staff - Role
You can see the structure below:
Departments (parent_id is used to build the tree)
Staff (raw Staff information)
Staff Roles (the lowest the weight, the highest in hierarchy)
Staff Departments (In which department - Who - What Role)
In a later phase, a Staff will probably belong in 2 or more departments with different roles. That's why i used a separate table Staff_departments for many-to-many. In this case, let's keep it simple and assume that 1 Staff belongs to 1 Department.
What i am trying to do:
A Manager(role weight=0 || role_id=1) in a Department, can view the Staff(Employees AND Supervisors) who work in his Department AND all the Staff(Employees AND Supervisors) from the Departments that are Children of his Department. The depth of the tree is unknown.
A Supervisor can view the Staff(only Employees) who work only in his Department.
An Employee can only view himself.
For Supervisors and Employees, the process is simple, so i think i am ok with that. For Managers probably i have to do something recursive, but i'm struggling every time i'm starting writing some lines of code.
My idea is to have a function find_related_staff($staff_id) {} in my controller, which i will pass the ID of the Staff who is logged in and it will return an array with the IDs of his related Staff. The only thing i got, is the ID of the Staff who is logged in.
If Manager return IDs of Managers,Supervisors and Employees related in his Department AND Managers,Supervisors and Employees from
Child Departments of his Department.
If Supervisor return IDs of Supervisors and Employees related in his Department only.
If Employee return his ID
Any idea on how to achieve that?
Ok, I think that, in order to make things easier to understand, we need to break your problem into small pieces (and I'm only focus on the section that you say you really need help: the managers' recursion).
First, we get the current department associated with the user authenticated. As you've said, you only have the ID of the staff currently sign so we'll start with that. Let's say the user id is assigned to variable $user_id.
$user_department = $this->db->get_where('staff_departments', ['staff_id' => $user_id])->row();
Now that we have the department, we check to see what's the role of the user in that department. We'll add that info to the $user_department object:
$user_department->role = $this->db->get_where('staff_roles', ['role_id' => $user_department->role_id])->row();
Let's check the weight of the user's role, shall we? If it's 0, we know it's a manager on that department so we'll recursively find the nested departments and their staff information. As per your logic dictates, we can check, here, if the user is a supervisor, also, and escalate if necessary. Like this:
if ($user_department->role->role_weight <= 1) {
// the user is a supervisor OR a manager, but both those can see, at least, the current department's staff information
$user_department->staff = $this->db->get_where('staff_departments', ['department_id' => $user_department->department_id]);
// now is the user a manager? If so, let's find nested departments
if ($user_department->role->role_weight === 0) {
$user_department->childs = $this->getChildDepartmentsAndStaffOf($user_department->department_id);
}
}
As you may note, there's a function that'll be called recursively. It must be something along this lines:
public function getChildDepartmentsAndStaffOf($department_id)
{
$child_departments = $this->db->get_where('departments', ['parent_id' => $department_id]);
if (! $child_departments) {
return null;
}
foreach ($child_departments as &$department) {
$department->staff = $this->db->get_where('staff_departments', ['department_id' => $department->department_id]);
$department->childs = $this->getChildDepartmentsAndStaffOf($department->department_id);
}
return $child_departments;
}
Now, you have the structure you want. I know this might be refactored, but I think that is enough to get your answer and point you to the right path.
Hope I've helped a little.
Yes, to get it done you must use recursive procedures. (I'm using MySQL 5.6.19)
I have created some test data before the stored procedures:
Sample data based on your question requirements:
create table departments
(
id int not null primary key auto_increment,
parent_id int,
department_name varchar(100)
);
insert into departments (id,parent_id,department_name)
values
(1,0,'Test A'),
(2,1,'Test B'),
(3,2,'Test C');
create table staff
(
id int not null primary key auto_increment,
ip_address varchar(100),
username varchar(100)
);
insert into staff values
(1,'127.0.0.1','ats'),
(2,'127.0.0.1','admin'),
(3,'127.0.0.1','george'),
(4,'127.0.0.1','jhon')
;
create table staff_roles
(
role_id int not null primary key auto_increment,
role_name varchar(100),
role_height int
);
insert into staff_roles values
(1,'Manager',0),
(2,'Supervisor',1),
(3,'Employee',2)
;
create table staff_departments
(
staff_department_id int not null primary key auto_increment,
department_id int,
staff_id int,
role_id int
);
insert into staff_departments values
(1,1,2,1),
(2,2,1,2),
(3,3,3,3),
(4,3,4,3);
It's time to create the stored procedures:
find_related_staff is the procedure that receives the staff_id parameter, according to that value will find the role_id in staff_departments table.
The variable #result will accumulate the final result as comma separated values.
find_recursive is the procedure that search in child departments and get the staff_id into #result variable;
The procedure code:
delimiter $$
drop procedure if exists find_related_staff$$
create procedure find_related_staff(p_id int)
begin
declare p_role_id int;
declare p_department_id int;
declare p_return varchar(255) default '';
declare p_role varchar(100);
select d.role_id, d.department_id, r.role_name
into p_role_id,p_department_id, p_role
from staff_departments d
inner join staff_roles r on d.role_id = r.role_id
where d.staff_id = p_id
limit 1;
case p_role_id
when 3 then -- employee (return the same id)
set #result = p_id;
when 2 then -- supervisor
select group_concat(s.staff_id)
into #result
from staff_departments s
where
s.role_id = 3
and s.department_id in
( select d.id
from departments d
where d.parent_id = p_department_id )
and s.role_id <> p_id;
when 1 then -- manager (complex recursive query)
select coalesce(group_concat(s.staff_id),'')
into #result
from staff_departments s
where
s.department_id = p_department_id
and s.staff_id <> p_id;
-- here we go!
call find_recursive(p_department_id);
end case;
select #result as result, p_role as role;
end $$
delimiter ;
delimiter $$
drop procedure if exists find_recursive$$
create procedure find_recursive(p_dept_id int)
begin
declare done int default false;
declare p_department int default false;
declare tmp_result varchar(255) default '';
-- cursor for all depend departments
declare c_departments cursor for
select s.department_id
from staff_departments s
where
s.department_id in
( select d.id
from departments d
where d.parent_id = p_dept_id );
declare continue handler for not found set done = true;
-- getting current departmens
set tmp_result =
(select coalesce(group_concat(s.staff_id),'')
from staff_departments s
where
s.department_id in
( select d.id
from departments d
where d.parent_id = p_dept_id ));
if length(tmp_result) > 0 then
if length(#result) > 0 then
set #result = concat(#result,',',tmp_result);
else
set #result = tmp_result;
end if;
open c_departments;
read_loop: loop
fetch c_departments into p_department;
if done then
leave read_loop;
end if;
call find_recursive(p_department);
end loop;
close c_departments;
end if;
end $$
delimiter ;
Testing:
Important: The max deep in recursion is 0 as default, we must change that value:
SET max_sp_recursion_depth=255;
Now we have the bellow configuration on your staff_departments table:
+---------------------+---------------+----------+---------+
| staff_department_id | department_id | staff_id | role_id |
+---------------------+---------------+----------+---------+
| 1 | 1 | 2 | 1 |
| 2 | 2 | 1 | 2 |
| 3 | 3 | 3 | 3 |
| 4 | 3 | 4 | 3 |
+---------------------+---------------+----------+---------+
Running each case:
call find_related_staff(2);
+--------+---------+
| result | role |
+--------+---------+
| 1,3,4 | Manager |
+--------+---------+
call find_related_staff(1);
+--------+------------+
| result | role |
+--------+------------+
| 3,4 | Supervisor |
+--------+------------+
call find_related_staff(3);
+--------+----------+
| result | role |
+--------+----------+
| 3 | Employee |
+--------+----------+
call find_related_staff(4);
+--------+----------+
| result | role |
+--------+----------+
| 4 | Employee |
+--------+----------+
Enjoy!
I think the most powerfull schema for hierarchical data in a relational database is the transitive-closure-table.
Given your sample data for the departments table:
department_id | parent_id | department_name
--------------|-----------|----------------
1 | 0 | TEST A
2 | 1 | TEST B
3 | 2 | TEST C
Your closure table (let's just call it departments_tree) would be like:
super_id | sub_id
---------|-------
1 | 1
1 | 2
1 | 3
2 | 2
2 | 3
3 | 3
Read it as: super_id = superordinate department_id; sub_id = subordinate department_id.
Assuming the logged-in user is manager of department with department_id = 2, the query to get all "supervised" employees is:
SELECT DISTINCT s.*
FROM departments_tree t
JOIN stuff_departments sd ON sd.department_id = t.sub_id
JOIN staff s ON s.id = sd.staff_id
WHERE t.super_id = 2
You can use triggers to populate and update the closure table.
Insert trigger:
DELIMITER //
CREATE TRIGGER `departments_after_insert` AFTER INSERT ON `departments` FOR EACH ROW BEGIN
INSERT INTO departments_tree (super_id, sub_id)
SELECT new.department_id, new.department_id
UNION ALL
SELECT super_id, new.department_id
FROM departments_tree
WHERE sub_id = new.parent_id;
END//
DELIMITER ;
Delete trigger:
DELIMITER //
CREATE TRIGGER `departments_before_delete` BEFORE DELETE ON `departments` FOR EACH ROW BEGIN
DELETE FROM departments_tree
WHERE sub_id = old.department_id;
END//
DELIMITER ;
Update trigger:
DELIMITER //
CREATE TRIGGER `departments_before_update` BEFORE UPDATE ON `departments` FOR EACH ROW BEGIN
DELETE t
FROM departments_tree p
CROSS JOIN departments_tree c
INNER JOIN departments_tree t
ON t.super_id = p.super_id
AND t.sub_id = c.sub_id
WHERE p.sub_id = old.parent_id
AND c.super_id = new.department_id;
INSERT INTO departments_tree (super_id, sub_id)
SELECT p.super_id, c.sub_id
FROM departments_tree p
CROSS JOIN departments_tree c
WHERE p.sub_id = new.parent_id
AND c.super_id = new.department_id;
END//
Note
You will not need a delete trigger, if you use foreighn keys with ON DELETE CASCADE:
CREATE TABLE `departments_tree` (
`super_id` INT(10) UNSIGNED NOT NULL,
`sub_id` INT(10) UNSIGNED NOT NULL,
PRIMARY KEY (`super_id`, `sub_id`),
INDEX `sub_id_super_id` (`sub_id`, `super_id`),
FOREIGN KEY (`super_id`) REFERENCES `departments` (`department_id`) ON DELETE CASCADE,
FOREIGN KEY (`sub_id`) REFERENCES `departments` (`department_id`) ON DELETE CASCADE
);
Note 2
In many implementations of a transitive closure table, you will find a depth or level column.
But you don't need it for the given requirements. And I believe you will never really need it,
as long as you don't try to format tree output in SQL.
I think your main problem is how to traverse down so that will be solved by getRecursiveDepts(). I have not completed the code but you can try something like this
file db.php
class DB {
private $servername = "127.0.0.1";
private $username = "root";
private $password = "root";
private $dbname = "test";
private $port = '3306';
public function getRecursiveDepts($deptIds) {
if (!is_array($deptIds)) {
$deptIds = array($deptIds);
}
$sql = "SELECT id FROM Departments WHERE parentId IN (";
$sql .= implode(', ', $deptIds);
$sql .= ")";
$conn = new mysqli($this->servername, $this->username, $this->password, $this->dbname, $this->port);
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
$result = $conn->query($sql);
if ($result->num_rows > 0) {
$newDept = array();
while($row = $result->fetch_assoc()) {
array_push($newDept, $row['id']);
}
$conn->close();
$moreDepts = $this->getRecursiveDepts($newDept);
if (is_null($moreDepts)) {
$finalIds = array_unique(array_merge($deptIds, $newDept));
} else {
$finalIds = array_unique(array_merge($deptIds, $newDept, $moreDepts));
}
return $finalIds;
} else {
$conn->close();
return null;
}
}
public function getRoles($empId) {
$sql = "SELECT role_id, department_id FROM staff_departmen_role WHERE staff_id = '$empId' GROUP BY role_id, department_id";
$conn = new mysqli($this->servername, $this->username, $this->password, $this->dbname, $this->port);
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
$result = $conn->query($sql);
if ($result->num_rows > 0) {
$emp = array();
while($row = $result->fetch_assoc()) {
if (!array_key_exists($row['role_id'], $emp)) {
$emp[$row['role_id']] = array();
}
array_push($emp[$row['role_id']], $row['department_id']);
}
}
$conn->close();
return $emp;
}
public function getEmpDetails($empId) {
$sql = "SELECT role_id, department_id FROM staff_departmen_role WHERE staff_id = '$empId' GROUP BY role_id, department_id";
$conn = new mysqli($this->servername, $this->username, $this->password, $this->dbname, $this->port);
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
$result = $conn->query($sql);
if ($result->num_rows > 0) {
$emp = array();
while($row = $result->fetch_assoc()) {
if (!array_key_exists($row['role_id'], $emp)) {
$emp[$row['role_id']] = array();
}
array_push($emp[$row['role_id']], $row['department_id']);
}
}
$conn->close();
return $emp;
}
}
file index.php
<?php
include_once 'db.php';
$objDB = new DB();
$empId = 2;
$emps = $objDB->getRoles($empId);
foreach ($emps as $roleId => $deptIds) {
switch ($roleId) {
case 1:
$allDeptIds = $objDB->getRecursiveDepts($deptIds);
break;
case 2://Supervisor GetEmpIds of current dept role >= 2
break;
case 3://Employee GetEmpIds of current dept role >= 2
$emp = $objDB->getEmpDetails($empId);
break;
default:
# code...
break;
}
}
$data = $objDB->getRecursiveDepts($empId);
print_r($data);
?>
I am using MySQL and have a table called sales. Its primary key is sales_id.
-------------------------------------
sales_id | invoice_id |
-------------------------------------
1 | 147
2 | 148
3 | 150
For sales_id 3 the invoice_id is supposed to be 149. I want to know which numbers are missing from invoice_id. I start invoice_id at 147 and end invoice_id at 4497. The invoice_id had no relation with sales_id.
Is it possible to know which numbers are missing from invoice_id, using a query?
I presume that you have a table of invoice - INVOICES. You may try:
SELECT invoice_id FROM INVOICES invoice
WHERE NOT EXISTS (SELECT * FROM SALES s WHERE invoice.invoice_id = s.invoice_id)
EDIT: If you don't have the INVOICES table, you may need to take all the invoices out before checking if there's any gap.
SELECT DISTINCT invoice_id FROM SALES ORDER BY invoice_id ASC
SELECT MAX(invoice_id) FROM SALES
then, by php:
for ($i = 1; $i < $max_invoice_id; $i++)
{
if (!in_array($i, $all_invoice_id))
{
$gapId[] = $i;
}
}
If you're just trying to find numbers out of the sequence in the example you've provided you could use something like this:
SELECT *
FROM `sales`
WHERE (
`invoice_id` - 147 - `sales_id`
) = 0
If you can provide a bigger sample set we might be able to tool a better answer for you.
set #suggest_invoice:=147;
select
sales_id,
invoice_id,
#suggest_invoice:=#suggest_invoice+1,
if(invoice_id=#suggest_invoice, 0, 1) as missing_invoice
from sales
order by sales_id;
I guess you almost getting all the records except the first two with missing_invoice=1
As you are searching for invoice_id then make sure you have an index on that column. otherwise queries will be slow.
You can try the following code
$inv_ids=range(147,4497);
$str_inv_ids=implode(",",$inv_ids);
$query="SELECT DISTINCT invoice_id FROM sales WHERE invoice_id IN ($str_inv_ids)";
$result=mysql_query($query);
while ($row = mysql_fetch_assoc($result)) {
$inv_ids_db[]=$row['invoice_id'];
}
// It gives invoice Ids that are in $inv_ids but not in $inv_ids_db
$missing= array_diff($inv_ids,$inv_ids_db);
print_r($missing);
You can find gaps in a MySQL sequence using this query:
SELECT invoice_id+1
FROM sales s
WHERE NOT EXISTS (
SELECT NULL
FROM sales t
WHERE s.invoice_id = t.invoice_id+1
)
HAVING `invoice_id+1` < (SELECT MAX(invoice_id) FROM sales)
ORDER BY invoice_id
This will return all invoice_id missing from the sequence, regardless of sales_id.
I am tracking a users activity while not logged in with a cookie_id and grouping these by date. When the user logs in I want to transfer all records from their cookie_id to their user_id. The cookie_id and user_id dates can overlap if the user is using different machines throughout the day.
For example, a user with a user_id of 1 logs in from work, but from home she doesn't so we assign her a cookie_id of 123.
The table data could look like this.
id user_id cookie_id date
2 1 NULL 2015-09-07
18 1 NULL 2015-09-10
19 NULL 123 2015-09-10
21 NULL 123 2015-09-11
22 1 NULL 2015-09-11
24 NULL 123 2015-09-12
Finally, one fine Saturday, she decides to log into her account from home.
What I would like to do is merge all of the records that have a coresponding user_id / date record, and update the row with a cookie_id of 123 with the user_id of 1 grouping by date creating any new records that are needed. I'd also like the related bananas table to be updated apprpriately as well.
id user_id cookie_id date
2 1 NULL 2015-09-07
18 1 NULL 2015-09-10
22 1 NULL 2015-09-11
24 1 NULL 2015-09-12
I am using Laravel and would like to use query builder. I have all of the code except the database query working. I am happy to post it up, if anybody thinks it is useful.
My table structure is below and I have made a SQLFiddle.
banana_groups
id int(10),
user_id int(10),
cookie_id varchar(255),
date date NOT NULL,
bananas
id int(10),
banana_group_id int(10),
monkey_id int(10),
EDIT
To embrace the spirit of SO and in response to vho's comment (which mysteriously disappeared), here is my attempt to write this SQL. Needless to say, it is incomplete and doesn't work.
Select *
FROM banana_groups AS m
WHERE EXISTS
(SELECT 1 FROM banana_groups WHERE m.user_id = 2)
AND NOT EXISTS
(SELECT 1 FROM banana_groups WHERE ...
I ended up writing a stored procedure to do this. I will leave the question unanswered so if anybody knows how to do this without a stored procedure I mark their answer correct.
CREATE PROCEDURE `transfer_bananas`(IN _cookieId VARCHAR(255), IN _userId INT)
BEGIN
DECLARE v_id INT;
DECLARE v_user_id INT;
DECLARE v_date DATE;
DECLARE v_done INT DEFAULT 0;
DECLARE cur_banana CURSOR FOR
SELECT id, `date`, (SELECT id from banana_groups
WHERE user_id = _userId AND
`date` = q.date) AS userId FROM banana_groups AS q
WHERE cookie_id = _cookieId;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET v_done = 1;
OPEN cur_banana;
loop_banana: LOOP
FETCH cur_banana INTO v_id,v_date, v_user_id;
IF v_done = 1 THEN
LEAVE loop_banana;
END IF;
IF(v_user_id IS NULL) THEN
UPDATE
banana_groups
SET
user_id = _userId,
cookie_id = NULL
WHERE
id = v_id;
ELSE
UPDATE
bananas
SET
banana_group_id = v_user_id
WHERE
banana_group_id = v_id;
DELETE FROM
banana_groups
WHERE
id = v_id;
END IF;
END LOOP loop_banana;
CLOSE cur_banana;
END
I call it from Laravel with
DB::statement(DB::raw('CALL transfer_bananas(\''
. $cookie_id
. '\', '
. $user->id
.');'
));
I have a MySQL table which has the following columns
urlByCustomer table
----------------------------------------------
|customerID | TeamID | date | numUrlsConsumed|
----------------------------------------------
| | | | |
----------------------------------------------
urlmapping
Column Type Null Default Comments MIME
urlMappingID bigint(20) No
customerID int(11) Yes 0
activityID int(11) Yes 0
contactID int(11) Yes 0
fullURL mediumtext Yes NULL
lastModified timestamp No CURRENT_TIMESTAMP
developerSandbox varchar(25) Yes
I've got this code that is being executed to update the table
$start = strtotime(date('Y-m-d 00:00:00'));
$end = strtotime(date('Y-m-d 23:59:59'));
$countAllThisGuysVals = "SELECT COUNT( DISTINCT`customerID`,`fullURL`)
FROM `urlmapping`
WHERE `urlMappingID` >= $ORIGINAL_COPY
AND `customerID` = $currentCustomerID";
$countTheVals= $conn->query($countAllThisGuysVals);
$countedfinal =0;
foreach ($countTheVals as $countRow) {
$countedfinal = array_sum($countRow)/2;
}
$tableUpdateQuery = "UPDATE `urlByCustomer`
SET `date` = NOW()
WHERE `customerID`= $currentCustomerID AND
UNIX_TIMESTAMP(`date`) BETWEEN '{$start}' and '{$end}'";
$conn->exec($tableUpdateQuery);
$tableUpdateQuery = "UPDATE `urlByCustomer`
SET `numUrlsConsumed` = $countedfinal
WHERE `customerID`= $currentCustomerID AND
UNIX_TIMESTAMP(`date`) BETWEEN '{$start}' and '{$end}'";
$conn->exec($tableUpdateQuery);
echo "update path <br>";
$tableModified = true;
$originalLPID++;
Variables are pretty much all declared, but the declarations are spread out, so I'm just posting this part to shorten it. The update query to the date column seems to be working, but the second update isn't. It worked 17 minutes ago though, so I'm confused since the only thing that changed between the next test was that I added some new values that should be causing it to update that column.
Idk. I guess one possiblity could be the UNIX_TIMESTAMP. I'm running this in Parallels on a Mac, so I'm not sure what that translates to for timestamps.
It looks like you're changing the value of "date" in your first update, without considering the fact that your second update will now not find the same rows in the WHERE clause (as you've just changed the date).
You can do the updates in a single statement: :
UPDATE urlByCustomer
SET `date` = NOW()
, numUrlsConsumed = $countedfinal
WHERE customerID= $currentCustomerID
AND UNIX_TIMESTAMP(`date`) BETWEEN '{$start}' and '{$end}'
I'm trying to change my rating system which only used one table before but now I'm changing it to use multiple tables and I really dont no how to
update or insert a new rating into the database and was wondering how to do this using my MySQL tables structure?
Also how do I do this by adapting the new code to my current PHP code which I want to change which is listed below.
First let me explain what my tables do they hold the information when students rate there own teachers I listed what the tables will hold in the
examples below to give you a better understanding of what I'm trying to do. Students are only allowed to rate there teachers once.
I provided the two MySQL tables that should be updated which are listed below.
My MySQL tables
CREATE TABLE teachers_grades (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
grade_id INT UNSIGNED NOT NULL,
teachers_id INT UNSIGNED NOT NULL,
student_id INT UNSIGNED NOT NULL,
date_created DATETIME NOT NULL,
PRIMARY KEY (id)
);
CREATE TABLE grades (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
letter_grade VARCHAR(2) NOT NULL,
grade_points FLOAT UNSIGNED NOT NULL DEFAULT 0,
PRIMARY KEY (id)
);
What the database will hold.
teachers_grades
id grade_id teachers_id student_id date_created
1 3 2 32 2010-01-23 04:24:51
2 1 32 3 2010-01-23 12:13:58
3 2 32 103 2010-01-23 12:24:45
grades
id letter_grade points
1 A+ 10
2 D 3
3 B 5
Here is the old PHP code.
// function to insert rating
function rate(){
$text = strip_tags($_GET['rating']);
$update = "update vote set counter = counter + 1, value = value + ".$_GET['rating']."";
$result = mysql_query($update);
if(mysql_affected_rows() == 0){
$insert = "insert into vote (counter,value) values ('1','".$_GET['rating']."')";
$result = mysql_query($insert);
}
}
Old table.
CREATE TABLE vote (
`counter` int(8) NOT NULL default '0',
`value` int(8) NOT NULL default '0'
);
first , do mysql_escape_string to the parametrs when inserting like :
mysql_real_escape_string($_GET['rating']);
second
you need to get all parameters (from GET or POST) and insert it to the db ,
the teacher_id ....
now i only see the rating.
Your old table was bit confusing as it seems like it only rates 1 teacher or teachers as a whole.
Now it seems like your new design process requires you to:
- store rating and averages of teachers
- track historical ratings from students
rating table should look something like
Table: rating
rating_id student_id teacher_id grade_id date_created
1 101 21 1 2010-01-23 04:24:51
2 102 21 1 2010-01-23 04:26:51
3 102 22 2 2010-01-23 04:28:51
4 103 24 1 2010-01-23 04:44:51
Your code usage:
$rating_value = $_GET['rating']; // Remember to sanitize your inputs
$student_id = $_GET['student_id'];
$teacher_id = $_GET['teacher_id'];
rate_a_teacher($teacher_id, $student_id, $rating_value);
Your method:
function rate_a_teacher($teacher_id, $student_id, $rating_value)
{
// Find the corrosponding to specified rating value
$grade_id = find_grade_id($rating_value); //TODO
$sql = "
INSERT INTO rating
('student_id', 'teacher_id', 'grade_id', 'date_created')
VALUE
($student_id, $teacher_id, $grade_id, NOW);
";
mysql_query($sql);
}
I skipped implementation for find_grade_id() for you to fill it in yourself.
The purpose of splitting your calcualted values to individual records is so that you can start do interesting reports,
like such:
Find average rating value of each teacher for the past 3 months:
SELECT teacher_id, (SUM(points)/COUNT(rating_id)) AS average_score
FROM rating
LEFT JOIN grades ON grades.id = rating.grade_id
WHERE date_created > SUBDATE(NOW(), INTERVAL 3 MONTH)
GROUP BY teacher_id