keep track of presence - mysql

I am trying to find a way how to keep track of students lessons presence (and absence). The main problem is that they pay in advance a series of 10 or 30 lessons, and once is done the can pay a new serie.
What is the best solution of doing that?
I will have two tables that looks sort of like that
presence
id
user_id
lesson_id
absence
id
user_id
lesson_id
reason
I was thinking on both tables to add a boolean column that will be true when the series is done (sort of bookmark). Then when I access the database my query will, for every user_id, count how many records there are after the last true value (if any).
Doesn't sound like a good strategy to me but I can't find another way.

Firstly, you need a User_Lesson_Series table:
User_Lesson_Series
id
user_id
number_of_lessons_paid_for
payment_date
number_of_lessons_remaining
Secondly, the suggestion made by P.Salmon of having an Attendance table (rather than a Presence table and an Absence table) is a good one:
Attendance
id
lesson_id
user_lesson_series_id
absence_flag
absence_reason
When inserting a record into User_Lesson_Series for the first time, set number_of_lessons_remaining to the same value as number_of_lessons_paid_for.
Every time you record a student's attendance (or non-attendance) in the Attendance table, you should also update that record's parent row in the User_Lesson_Series table, decrementing number_of_lessons_remaining by 1.
If a User_Lesson_Series record has number_of_lessons_remaining = 0, then you should not permit any more Attendance records to be written out for that user_lesson_series_id. Instead, you should require a new user lesson series to be paid for, necessitating a new User_Lesson_Series record to be generated.
Subsequent Attendance records will have the user_lesson_series_id of that new User_Lesson_Series.

I'd probably make this one student_lesson table:
student_lesson (student_id, lesson_id, status_id, absence_reason)
plus a status table for the statuses Open, Attended, Absent.
Once a student pays for the next n lessons, you create n rows in the student_lesson table. And when a lesson has passed, you update the status to either Attended or Absent.
With an index on student_id and status_id you can quickly see how many Open lessons a student still has.

Related

How to implement SQL join on multiple keys for two tables using where clause?

I am interested to find the student headcount of 1st week of fall 2018 for the Business faculty by sex using the following two tables. Here ID is the primary key or part of the primary key for each of the data tables. In plan table, the program is the student's college(for our case, "Business"), Sex has values M, and F, TERM has the values, "Fall", "Spring", and " Summer", Sessions has the values, " 1st week", and " 2nd week". I was wondering if you could check my code. I am kinda new in SQL! I sincerely appreciate your time.
My SQL code:
SELECT count(Student.ID) as COUNT, Student.Sex as Sex
FROM Student JOIN Plan
on Student.ID=Plan.ID
WHERE Student.Term="Fall"
AND Student.Sessions="1st Week"
AND Plan.Program="Business"
GROUP BY Student.Sex;
The answer to this depends on whether or not the ID column in the plan table is the student’s id number , which i believe it is not. The plan table should have an entry for each student session combination, in order to account for the same student having different sessions. See: one to many relationship. From there, you should be able to execute this query after you group by all of the fields mentioned in the select clause.
Anyway, I've created the fiddle for you reference here :
https://www.db-fiddle.com/f/7G2DwLB6SNqbAtKeD96nWG/1
And I can conclude that based on you query condition given, it is correct. But like #Benny mention, the field ID is usually referring to the tables data ID. For example ID in Student is the student id while in Plan its highly possible that its ID for each plan. But you said it's identical AND there's no student_id column in the Plan table, so it should be ok.

Non-unique many-to-many table design

I'm implementing a voting system for a php project which uses mysql. The important part is that I have to store every voting action separately for statistic reasons. The users can vote for many items multiple times, and every vote has a value (think of it like a donation kinda stuff).
So far I have a table votes in which I'm planning to store the votes with the following columns:
user_id - ID of the voting user, foreign key from users table
item_id - ID of the item which the user voted for, foreign key from items table
count - # of votes spent
created - date and time of voting
I'll need to get things out of the table like: Top X voters for an item, all the items that a user have voted for.
My questions are:
Is this table design suitable for the task? If it is, how should I index it? If not, where did I go wrong?
Would it be more rewarding to create another table beside this one, which has unique rows for the user-item relationship (not storing every vote separately, but update the count row)?
Each base table holds the rows that make a true statement from some fill-in-the-(named-)blanks statement aka predicate.
-- user [userid] has name ...
-- User(user_id, ...)
SELECT * FROM User
-- user [user_id] voted for item [item_id] spending [count] votes on [created]
-- Votes(user_id, item_id, count, created)
SELECT * FROM Votes
(Notice how the shorthand for the predicate is like an SQL declaration for its table. Notice how in the SQL query a base table predicate becomes the table's name.)
Top X voters for an item, all the items that a user have voted for.
Is this table design suitable for the task?
That query can be asked using that design. But only you can know what queries "like" that one are. You have to define sufficient tables/predicates to describe everything you care about in every situation. If Votes records the history of all relevant info about all events then it must be suitable. The query "all the items that user User has voted for" returns rows satisfying predicate
-- user User voted for item [item] spending some count on some date.
-- for some count & created,
user User voted for item [item_id] spending [count] votes on [created]
-- for some count & created, Votes(User, item_id, count, created)
-- for some user_id, count & created,
Votes(user_id, item_id, count, created) AND user_id = User
SELECT item_id FROM Votes WHERE user_id = User
(Notice how in the SQL the condition turns up in the WHERE and the columns you keep are the ones that you care about. More here and here on querying.)
If it is, how should I index it?
MySQL automatically indexes primary keys. Generally, index column sets that you JOIN ON, otherwise test, GROUP BY or ORDER BY. MySQL 5.7 Reference Manual 8.3 Optimization and Indexes
Would it be more rewarding to create another table beside this one, which has unique rows for the user-item relationship
If you mean a user-item table for some count & created, [user_id] voted for [item_id] spending [count] votes on [created] and you still want all the individual votings then you still need Votes, and that user-item table is just SELECT user_id, item_id FROM Votes. But if you want to ask about people who haven't voted, you need more.
(not storing every vote separately, but update the count row)
If you don't care about individual votings then you can have a table with user, item and the sum of count for user-item groups. But if you want Votes then that user-item-sum table is expressible in terms of Votes using GROUP BY user_id, item_id & SUM(count).

Pulling different records from multiple tables as one transaction history list

I am working on an employee management/reward system and need to be able to show a single "transaction history" page that shows in chronological order the different events that the employee has experienced in one list. (Sort of like how in facebook you can goto your history/action section and see a chronological list of all the stuff that you have done and affects you, even though they are unrelated to eachother and just have you as a common user)
I have different tables for the different events, each table has an employee_id key and an "occured" timestamp, some table examples:
bonuses
customers
raise
complaints
feedback
So whenever an event occurs (ie a new customer is assigned to the employee, or the employee gets a complaint or raise) a new row is added to the appropriate table with the employee ID it affects and a timestamp of when it occured.
I need a single query to pull all records (upto 50 for example) that include the employee and return a history view of that employee. The field names are different in each table (ie the bonus includes an amount with a note, the customer includes customer info etc).
I need the output to be a summary view using column names such as:
event_type = (new customer, bonus, feedback etc)
date
title (a brief worded title of the type of event, specified in sql based on the table its referencing)
description (verbiage about the action, such as if its event_type bonus display the bonus amount here, if its a complain show the first 50 characters of the complaint message or the ID of the user that filed the complaint from the complaints table. All done in SQL using if statements and building the value of this field output based on which table it comes from. Such as if its from the customers table IF current_table=customers description='A customer was assigned to you by'.customers.assigner_id).
Ideally,
Is there any way to do this?
Another option I have considered, is I could do 5-6 different queries pulling the records each from their own table, then use a mysql command to "mesh/interleave" the results from all the queries into one list by chronological order. That would be acceptable too
You could use a UNION query to merge all the information together and use the ORDER BY clause to order the actions chronologically. Each query must have the same number of fields. Your ORDER BY clause should be last.
The examples below assume you have a field called customer_name in the customers table and bonus_amount in the bonuses table.
It would look something like this:
SELECT 'New Customer' as event_type, date,
'New customer was assigned' as title,
CONCAT('New Customer: ', customer_name, ' was assigned') as description
FROM customers
WHERE employee_id = 1
UNION
SELECT 'Bonus' as event_type, date,
'Received a bonue' as title,
CONCAT('Received a bonus of $', FORMAT(bonus_amount, 2), '.') as description
FROM bonuses
WHERE employee_id = 1
UNION
...
ORDER BY date DESC;

MySQL Database design and effecient query

I have the following tables:
users (id, first_name, last_name)
category (id, name)
rank(id, user_id, rank)
Each user can belong to several categories. And all users are in the rank table and have a value between 0.0 and 1.0, where 0 is the lowest rank and 1 is the highest. I’d like to setup additional tables to create the following webpage:
A visitor to the page (identified by either one of the recorded ids in the user table, or a numeric representation of their ip address) chooses a category and is presented with two randomly chosen users from the users table such that:
1) the visiting user_id has not seen this pairing in a period of 24 hours
2) the two users belong to the chosen category
3) the two users are within 1 rank value of each other. Let me explain that last criteria - if the ranks were sorted, the two chosen users would have adjacent ranks.
This is a hard one and I can’t for the life of me figure it out how to do this effeciently
I truly appreciate any help on this front.
Thanks
You just need two more tables and the rest go in your website logic.
user_category(user_id, category_id)
user_pairing(first_user_id, second_user_id, last_seen)
The first table is to represent a ManyToMany relationship between the users and the category, and the second one is for the users pairing.
I agree with #Yasel, i want to add that you properly want another table
candidate(first_user_id, second_user_id);
this table is used to pre-calculate the candidates for each user, this candidate table is prepopulated every hour/day, so when each first_user_id, second_user_id is assigned, this pair is removed from candidate table and moved into user_pairing table. so each time you only need to query candidate table which should be efficient.

How do I keep one column consistently updated?

I have a faculty table, and each faculty has a certain number of students under him/her. So there is a 'current' column, which is the number of students currently under them.
However, I don't want to ++ and -- every time I switch a student to another faculty. Is there a way to keep the column updated with a query that uses count()? I find it is easier and more accurate to use the query 'select count() from student where advisor = 2' for example that using my current column.
To do this, use a view:
CREATE VIEW studentCount AS
SELECT
profID,
profName,
whatever,
(SELECT COUNT(*)
FROM studentTable
WHERE studentTable.profID=profTable.profID
) AS studentCount
FROM profTable;
Obviously, this needs to be massaged a little to fit your schema, but essentially, setup your view to have all the columns of the table with the faculty info and add a column at the end that counts the number you want in it.
Triggers could be a solution to you problem?
http://dev.mysql.com/doc/refman/5.5/en/triggers.html
You could create a trigger that automaticly updates your faculty table every time a student switch faculty.