Database schema and logic for this - mysql

I am trying to create a timetable for classes in MySQL. The columns are the days of the week and the rows are the time intervals ( i.e. 7:00 - 8:00 ). I am also trying to have 30-minute time intervals and when a particular subject is 2 hours, it will span 4 rows. Each cell contains the name of the course and room. This is part of a small program I am trying to develop. So far, I know what data to insert but I'm stuck on creating the right database schema. Really need the logic on how to get this started (i.e. what data types should I use ). Thanks!

How does this sound:
CREATE TABLE TimeTable (
ID int(8) NOT NULL AUTO_INCREMENT,
Subject varchar(32) NOT NULL,
Room varchar(8) NOT NULL,
StartTime datetime NOT NULL,
EndTime datetime NOT NULL,
Day enum('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday') NOT NULL,
PRIMARY KEY (ID)
);
You only store the data about the subjects.
The other stuff you mentioned is really down to whatever language you're using to output the data. You can't really put intervals in the middle of a database. As Damien_The_Unbeliever said, databases aren't exactly spreadsheets.
Hope this make sense.

You can set one column for day (you can value days from 1 to 7), one column for starting time and one column for finish time. This way your queries will become more simple.
Example:
Day | Start | Finish
-----------------------------
1 8:30 12:00
1 13:00 15:00
This will also come in handy when you don't want to stuck with 30 minutes.

The columns are the days of the week and the rows are the time intervals ( i.e. 7:00 - 8:00 )
That's a bad start. Your columns should be properties of a single lesson and your rows should be all the different lessons throughout the week.
Your columns could be Day of Week, Lesson of Day, Duration, Subject, Room, Teacher, et cetera. In this example, your primary key would be (Day of Week, Lesson of Day).
This way, if you want the schedule for a particular day you just select everything with Day of Week = 2 to get the schedule for Tuesday for example.
Each cell contains the name of the course and room.
This also defys the laws of normalization. You should always have no more than one value per "cell".
Really need the logic on how to get this started (i.e. what data types should I use )
Use the most intuitive type. When you store a number choose integer, when storing few characters choose varchar(n), when using lots of characters use text and so on...

I would create it this way where it is normalized and gives more chance to expand this app further.
Tables (attributes):
timeslots(id, start_time, end_time)
subjects(id, name)
timetable(timeslot_id, subject_id)

Related

Date table lookup efficiency - MySQL

I need to calculate the number of "working minutes" between two datetime values, lets call them 'Created' and 'Finished'.
'Finished' is always subsequent to 'Created'. The two values can differ by anything from 1 second to several years. The median difference is 50,000 seconds or roughly 14 hours.
Working minutes are defined as those occurring between 0900 to 1700 hours, Monday to Friday; excluding weekends and official holidays in our country.
I decided a lookup table was the way to go, so I generated a table of all work minutes, explicitly excluding weekends, nights and holidays...
CREATE TABLE `work_minutes` (
`min` datetime NOT NULL,
PRIMARY KEY (`min`),
UNIQUE KEY `min_UNIQUE` (`min`)
)
I populated this programatically with all the "working minutes" between years 2017 to 2024, and at this point I started to get the feeling I was being very inefficient as the table began to balloon to several hundred thousand rows.
I can do a lookup easily enough, for instance:
SELECT COUNT(min) FROM `work_minutes` AS wm
WHERE wm.min > '2022-01-04 00:04:03'
AND wm.min <= '2022-02-03 14:13:09';
#Returns 10394 'working minutes' in 0.078 sec
This is good enough for a one-off lookup but to query a table of 70,000 value pairs takes over 90 minutes.
So, I am uncomfortable with the slowness of the query and the sense that the lookup table is unnecessarily bloated.
I am thinking I need to set up two tables, one just for dates and another just for minutes, but not sure how to implement. Date logic has never been my forte. The most important thing to me is that the lookup can query over 70,000 values reasonably quickly and efficiently.
Working in MySQL 5.7.30. Thanks in advance for your expertise.
Divide the timerange to 3 parts - starting and finishing incomplete day parts, and middle part which consists from a lot of complete days. Of course if both starting and finishing time stamps have the same date part then it will be one part only, if their dates are consecutive then you\ll have 2 parts to process.
There is no problem to calculate the number of working minutes in incomplete day part. Common overlapping formula with weekday checking will help.
Create static calendar/service table which starts from the date which is earlier than any possible date in your beginning timestamp with guarantee and includes all dates after any possible date in your finishing timestamp. Calculate cumulative working minutes for each date in the table. This table allows to calculate the amount of working time in any range of complete days with single substraction.
Plan A: Convert the DATETIME values to seconds (from some arbitrary time) via TO_SECONDS(), then manipulate them with simple arithmetic.
Plan B: Use the DATEDIFF() function.
Your COUNT(min) counts the number of rows where min IS NOT NULL. You may as well say COUNT(*). But did you really want to count the number of rows?

Mysql Function multiple entries in one cell

So I'm working on a schedule system for my job a basically i wanted to know if there is a way where mysql can do something like:
|Monday |tuesday|wendsday|total
|Dan |5am-7am |7am-6pm|6am-11am|
11am-2pm| |2pm-7pm |
5pm-12am|
where i can enter multiple shifts on 1 day for each person in the same cell if needed instead of the name repeating several times like:
Dan|5-4|
Dan|6-8|
and if there is a function to calculate total time in one cell with multiple shifts
There is a way (representing the data as string), but you wouldn't want to do this - you will loose all calculations, searches etc.
You should not try to represent the data in the database exactly as how it looks on paper.
I would make a table like this:
ShiftID|Person|StartTime|EndTime
Making StartTime & EndTime columns of type DATETIME, you will store not only the HH:mm of a shift's start, but also the day. This is helpful when you have a shift which starts on one day and ends in the next, like starting on Monday 2017-05-15 23:00 and ending on Tuesday 2017-05-16 02:00.
You can extract the date only from this filed using MySQL DATE() function and select only those entires which start OR end on this day.
To calculate the shift's duration you can use MySQL function TIMESTAMPDIFF()
You can even use DAYOFWEEK() to get if it is Monday, Tuesday, etc.
About duplicating the person's name - I would make another table, which will match users with their data to IDs an use ID in the column Person, but for a starter and if your data is not big and if speed is not an issue and if typo errors (like Den instead of Dan) are not a problem ... you could use the name directly in this table.
After storing the data in a table like this you could represent it as you wish in HTML (or print).
You can create a third table with the following columns:
person_id int,
start_time datetime,
end_time datetime
Where person_id would be foreign key to Person table and start_time and end_time would be datetime columns. You can then store multiple records for a person in this table and use MySQL's date functions with GROUP BY to generate the report similar to the one in question.

SQL Query to get data between two weeks?

I have a week column with week numbers as w0, w1, w2.... I am trying to get last last six weeks data. Here's the sql query I am using.
SELECT * FROM week
WHERE uid = '9df984da-4318-1035-9589-493e89385fad'
AND report_week BETWEEN `'w52' AND 'w5'`;
'w52' is essentially week 52 in December 2015 and 'w5' is Jan 2016. The 'between' seems to not work. Whats the best way to get data from the above two weeks?
Here's the CREATE TABLE statement:
CREATE TABLE `week` (`uid` VARCHAR(255) DEFAULT '' NOT NULL,
`report_week` VARCHAR(7) NOT NULL,
`report_files_active` BIGINT DEFAULT NULL);
Essentially this table is getting populated from other table which has date column. It uses dates from other table and summarizes weekly data into this.
Any help is appreciated.
Refer to this SO Discussion which details the reasons for a problem similar to yours.
BETWEEN 'a' and 'b' actually matches to columnValue >='a' and columnValue <= 'b'
In your case w52 is greater than w5 due to lexicographic ordering of Strings - this means that the BETWEEN clause will never return a true (think about it as equivalent to saying BETWEEN 10 and 1 instead of BETWEEN 1 and 10.
Edit to my response:
Refrain from storing the week value as a string. Instead here are a couple of approaches in order of their preference:
Have a timestamp column. You can easily then use MySQL query
facilities to extract the week information out of this. For a
reference see this post.
Maintain two columns - YEAR, WEEKNO where YEAR will store values
like 2015, 2016 etc and WEEKNO will store the week number.
This way you can query data for any week in any year.
please show me table structure and DB name because it different for other, if it is any timestamp then we can use BETWEEN 'systemdate' AND 'systemdate-6'

Optimized SQL for timetable

I want to create a SQL table in my database that can hold a school timetable but every way i tried to proceed with it it was no optimal and over the course of 1-2 changes to the timetable the database table got a bit too big.
It goes a bit like this:
Mo Tu Wed Thu Fri
07:00
08:00
09:00
10:00
11:00
12:00
13:00
14:00
15:00
16:00
This is the basic layout of the timetable and in the table the first 2 columns are timetableid and classid. My first approach was to have columns for each day with the classes in one column but that was a bit buggy, my next approach was to get the columns like this mo_7, mo_8, mo_9 etc. until i reach fri_16 but that took up too much space.
So my question is, what is the most optimal way to save a timetable in a database, in what table layout.
Thank you in advance.
Your problem isn't your database, it's because you're trying to use it like a spreadsheet.
You need to backtrack a little - store a table of classes, along with the various attributes. If each class has only one session, then add 'time' as an attribute. If there's multiple sessions, your probably then need to separate out the schedule into a seperate table.
E.g.:
Table 'classes':
ClassID.
description
teacher
other stuff.
Table 'sessions':
session ID
ClassID
start_time
end_time
day
location
And then you'd use a select to query e.g.
SELECT classID, start_time, day, location
FROM sessions
WHERE day like "Monday"
ORDER BY start_time
Which'd give you your list for Monday. To extend it, you might add a join with the 'classes' table.
SELECT classes.description, classes.teacher, sessions.start_time, sessions.day, sessions.location
FROM sessions, classes
JOIN ON classes.ClassID = sessions.ClassID
WHERE day LIKE 'Monday'
ORDER BY sessions.start_time
Something like that anyway (apologies if the syntax is a bit off, I'll check/amend if I can).
But the point is - let the database store the actual data and use the queries to transform it into a layout that you like. That way you don't need to rewrite your database each time you want to change something minor.

How do i save a date range on just the months, not year or day ie. January through may

How do I save a date range on just the months, not year or day, for example January through May?
I am to prepare a table of crops and the appropriate time for planting. It needs to be non-specific as to year and day.
Some beans can be planted "January through March" or "August through November".
I need to be able to call up a form and select the month of my search and return a list of what crops are appropriate for the month selected.
Should I create a table with fields crop month
and save data as Beans Jan - March
then another entry as Beans August - November
Or
Beans 1,3 and 8,11?
You can create a separate table to keep track of allowed months for your crops:
create table cropmonths
(
cropid int not null,
allowedmonth int not null
);
So you will have 7 records in that table (one for for each month) that would reference a single "beans" row in crops table.