Best way to store event times in (My)SQL database - mysql

I'm trying to decide on the best way to store event times in a MySQL database. These should be as flexible as possible and be able to represent "single events" (starts at a certain time, does not necessarily need an end time), "all day" and "multi day" events, repeating events, repeating all day events, possibly "3rd Saturday of the month" type events etc.
Please suggest some tried and proven database schemes.

Table: Events
StartTime (dateTime)
EndTime (dateTime) null for no end time
RepeatUnit (int) null = noRepeat, 1 = hour, 2 = day, 3 = week, 4 = dayOfMonth, 5 = month, 6 = year
NthDayOfMonth (int)
RepeatMultiple (int) eg, set RepeatUnit to 3, and this to 2 for every fortnight
Id - if required, StartTime might be suitable for you to uniquely identify an event.
Name (string) - name given to the event, if required
This might help. It would require a decent amount of code to interpret when the repeats are. Parts of the time fields that are at lower resolutions than the repeat unit would have to be ignored. Doing the 3rd saturday of the month woudln't be easy either... the NthDayOfMonth info would be required just for doing this kind of functionality.
The database schema required for this is simple in comparison with the code required to work out where repeats fall.

I worked on a planner application which loosely follows the iCalendar standard (to record events). You may want to read RFC 2445 or this schema published by Apple Inc. icalendar schema to see if they are relevant to the problem.
My database schema (recurring/whole-day event was not considered at the time)
event (event_id, # primary key
dtstart,
dtend,
summary,
categories,
class,
priority,
summary,
transp,
created,
calendar_id, # foreign key
status,
organizer_id, # foreign key
comment,
last_modified,
location,
uid);
the foreign key calendar_id in the previous table refers this
calendar(calendar_id, # primary key
name);
while organizer_id refers this (with other properties like common name etc. missing)
organizer(organizer_id, # primary key
name);
Another documentation that you may find more readable is located here
hope this helps

You need two tables. One for storing the repeating events (table repeatevent) and one for storing the events (table event). Simple entries are only stored in the event table. Repeating entries are stored in the repeatevent table and all single entries for the repeating event are also stored in the event table. This means that everytime you enter a repeating entry, you have to enter all the single resulting entries. You can do this by using triggers, or as part of your business logic.
The advantage of this approach is, that querying events is simple. They are all in the event table. Without the storage of repeating events in the event table, you would have complex SQL or business logic that would make your system slow.
create table repeatevent (
id int not null auto_increment,
type int, // 0: daily, 1:weekly, 2: monthly, ....
starttime datetime not null, // starttime of the first event of the repetition
endtime datetime, // endtime of the first event of the repetition
allday int, // 0: no, 1: yes
until datetime, // endtime of the last event of the repetition
description varchar(30)
)
create table event (
id int not null auto_increment,
repeatevent null references repeatevent, // filled if created as part of a repeating event
starttime datetime not null,
endtime datetime,
allday int,
description varchar(30)
)

Same way the cron does it? Recording both start and end time that way.

Use datetime and mysql's built in NOW() function. Create the record when the process starts, update your column that tracks the end time when it the process ends.

Related

Insert/Update on table with autoincrement and foreign key

I have a table as such:
id entity_id first_year last_year sessions_attended age
1 2020 1996 2008 3 34.7
2 2024 1993 2005 2 45.1
3 ... ... ...
id is auto-increment primary key, and entity_id is a foreign key that must be unique for the table.
I have a query that calculates first and last year of attendance, and I want to be able to update this table with fresh data each time it is run, only updating the first and last year columns:
This is my insert/update for "first year":
insert into my_table (entity_id, first_year)
( select contact_id, #sd:= year(start_date)
from
( select contact_id, event_id, start_date from participations
join events on participations.event_id = events.id where events.event_type_id = 7
group by contact_id order by event_id ASC) as starter)
ON DUPLICATE KEY UPDATE first_year_85 = #sd;
I have one similar that does "last year", identical except for the target column and the order by.
The queries alone return the desired values, but I am having issues with the insert/update queries. When I run them, I end up with the same values for both fields (the correct first_year value).
Does anything stand out as the cause for this?
Anecdotal Note: This seems to work on MySQL 5.5.54, but when run on my local MariaDB, it just exhibits the above behavior...
Update:
Not my table design to dictate. This is a CRM that allows custom fields to be defined by end-users, I am populating the data via external queries.
The participations table holds all event registrations for all entity_ids, but the start dates are held in a separate events table, hence the join.
The variable is there because the ON DUPLICATE UPDATE will not accept a reference to the column without it.
Age is actually slightly more involved: It is age by the start date of the next active event of a certain type.
Fields are being "hard" updated as the values in this table are being pulled by in-CRM reports and searches, they need to be present, can't be dynamically calculated.
Since you have a 'natural' PK (entity_id), why have the id?
age? Are you going to have to change that column daily, or at least monthly? Not a good design. It would be better to have the constant birth_date in the table, then compute the ages in SELECT.
"calculates first and last year of attendance" -- This implies you have a table that lists all years of attendance (yoa)? If so, MAX(yoa) and MIN(yoa) would probably a better way to compute things.
One rarely needs #variables in queries.
Munch on my comments; come back for more thoughts after you provide a new query, SHOW CREATE TABLE, EXPLAIN, and some sample data.

How to store a data whose type can be numeric, date or string in mysql

We're developing a monitoring system. In our system values are reported by agents running on different servers. This observations reported can be values like:
A numeric value. e.g. "CPU USAGE" = 55. Meaning 55% of the CPU is in
use).
Certain event was fired. e.g. "Backup completed".
Status: e.g. SQL Server is offline.
We want to store this observations (which are not know in advance and will be added dynamically to the system without recompiling).
We are considering adding different columns to the observations table like this:
IntMeasure -> INTEGER
FloatMeasure -> FLOAT
Status -> varchar(255)
So if the value we whish to store is a number we can use IntMeasure or FloatMeasure according to the type. If the value is a status we can store the status literal string (or a status id if we decide to add a Statuses(id, name) table).
We suppose it's possible to have a more correct design but would probably become to slow and dark due to joins and dynamic table names depending on types? How would a join work if we can't specify the tables in advance in the query?
I haven't done a formal study, but from my own experience I would guess that more than 80% of database design flaws are generated from designing with performance as the most important (if not only) consideration.
If a good design calls for multiple tables, create multiple tables. Don't automatically assume that joins are something to be avoided. They are rarely the true cause of performance problems.
The primary consideration, first and foremost in all stages of database design, is data integrity. "The answer may not always be correct, but we can get it to you very quickly" is not a goal any shop should be working toward. Once data integrity has been locked down, if performance ever becomes an issue, it can be addressed. Don't sacrifice data integrity, especially to solve problems that may not exist.
With that in mind, look at what you need. You have observations you need to store. These observations can vary in the number and types of attributes and can be things like the value of a measurement, the notification of an event and the change of a status, among others and with the possibility of future observations being added.
This would appear to fit into a standard "type/subtype" pattern, with the "Observation" entry being the type and each type or kind of observation being the subtype, and suggests some form of type indicator field such as:
create table Observations(
...,
ObservationKind char( 1 ) check( ObservationKind in( 'M', 'E', 'S' )),
...
);
But hardcoding a list like this in a check constraint has a very low maintainability level. It becomes part of the schema and can be altered only with DDL statements. Not something your DBA is going to look forward to.
So have the kinds of observations in their own lookup table:
ID Name Meaning
== =========== =======
M Measurement The value of some system metric (CPU_Usage).
E Event An event has been detected.
S Status A change in a status has been detected.
(The char field could just as well be int or smallint. I use char here for illustration.)
Then fill out the Observations table with a PK and the attributes that would be common to all observations.
create table Observations(
ID int identity primary key,
ObservationKind char( 1 ) not null,
DateEntered date not null,
...,
constraint FK_ObservationKind foreign key( ObservationKind )
references ObservationKinds( ID ),
constraint UQ_ObservationIDKind( ID, ObservationKind )
);
It may seem strange to create a unique index on the combination of Kind field and the PK, which is unique all by itself, but bear with me a moment.
Now each kind or subtype gets its own table. Note that each kind of observation gets a table, not the data type.
create table Measurements(
ID int not null,
ObservationKind char( 1 ) check( ObservationKind = 'M' ),
Name varchar( 32 ) not null, -- Such as "CPU Usage"
Value double not null, -- such as 55.00
..., -- other attributes of Measurement observations
constraint PK_Measurements primary key( ID, ObservationKind ),
constraint FK_Measurements_Observations foreign key( ID, ObservationKind )
references Observations( ID, ObservationKind )
);
The first two fields will be the same for the other kinds of observations except the check constraint will force the value to the appropriate kind. The other fields may differ in number, name and data type.
Let's examine an example tuple that may exist in the Measurements table:
ID ObservationKind Name Value ...
==== =============== ========= =====
1001 M CPU Usage 55.0 ...
In order for this tuple to exist in this table, a matching entry must first exist in the Observations table with an ID value of 1001 and an observation kind value of 'M'. No other entry with an ID value of 1001 can exist in either the Observations table or the Measurements table and cannot exist at all in any other of the "kind" tables (Events, Status). This works the same way for all the kind tables.
I would further recommend creating a view for each kind of observation which will provide a join of each kind with the main observation table:
create view MeasurementObservations as
select ...
from Observations o
join Measurements m
on m.ID = o.ID;
Any code that works solely with measurements would need to only hit this view instead of the underlying tables. Using views to create a wall of abstraction between the application code and the raw data greatly enhances the maintainability of the database.
Now the creation of another kind of observation, such as "Error", involves a simple Insert statement to the ObservationKinds table:
F Fault A fault or error has been detected.
Of course, you need to create a new table and view for these error observations, but doing so will have no impact on existing tables, views or application code (except, of course, to write the new code to work with the new observations).
Just create it as a VARCHAR
This will allow you to store whatever data you require in it. It is much more difficult to do queries based on the number in the field such as
Select * from table where MyVARCHARField > 50 //get CPU > 50
However if you think you want to do this, then either you need a field per item or a generalised table such as
Create Table
Description : Varchar
ValueType : Varchar //Can be String, Float, Int
ValueString: Varchar
ValueFloat: Float
ValueInt : Int
Then when you are filling the data you can put your value in the correct field and select like this.
Select Description ,ValueInt from table where Description like '%cpu%' and ValueInt > 50
I had a used two columns for a similar problem. First column was for data type and second value contained data as a Varchar.
First column had codes ( e.g. 1= integer, 2 = string, 3 = date and so on), which could be combined to compare values. ( e.g. find the max integer where type=1)
I did not have joins, but i think you can use this approach. It will also help you if tomorrow more data types are introduced.

MySQL movie reservation system design part 2

This is similar to another question but not entirely the same.
My aim is to design a movie reservation system. A user could click on one or more empty seats for a movie schedule to reserve them. But he needs to make a payment before 15mins is up, otherwise the reserved seats would have to be automatically given up for other users.
I have the following pseudo MySQL :
Table Movie:
id int primary key,
name varchar,
.....
Table MovieSched:
movie_id foreign key refers to Movie,
sched_id int primary key,
showtime datetime, // date and time of schedule
count_signup int, // number of sign ups
max_size int // max number of seats
Table MovieSchedSignUp:
sched_id foreign key refers to MovieSched,
user_id foreign key refers to User,
signup datetime // datetime of signup
Every movie schedule has a max_size of users who can sign up. To register a user, I insert a row in MovieSchedSignUp with the current date and time.
A few constraints naturally arise from the requirements:
Due to possible inconsistency between the interface and database, I need to inform a user A when there are not enough seats available when A tries to reserve seats. (for e.g., another user B could have bought all the seats right before A.)
I need to atomically insert a row in MovieSchedSignUp while ensuring the schedule is not "overbooked" (count_signup <= max_size) as well as updating count_signup at the same time.
I need to ensure payment is made within 15mins, otherwise the reserved seats have to be freed.
My thoughts are:
Have extra columns in MovieSchedSignUp to keep track of when payment is made.
Use a transaction, but how do I return information about whether there are enough seats or not?
Have a batch job running in the background to delete the "expired" rows in MovieSchedSignUp.
What is the most efficient way to go about doing this? Any other thoughts? Don't really want to use a batch job, but is there any other way out?
I think in this situation you are going to have to use a transaction.
Start the transaction
Insert the records to be added to a temp table
Do a join between the temp table, MovieSched and MovieSchedSignUp to
check the number of records (combined temp and MovieSchedSignUp) isn't greater than max size.
If OK then do the insert
If OK them commit the transaction, if not then roll back the
transaction.

Mysql Multiple Unique Keys Across Tables

I have a table like that:
CREATE TABLE `Appointment` (
id INT NOT NULL AUTO_INCREMENT,
user_id INT NOT NULL,
doctor_slot_id INT NOT NULL,
date DATE NOT NULL,
PRIMARY KEY(id),
FOREIGN KEY(user_id) REFERENCES user(id),
FOREIGN KEY(doctor_slot_id) REFERENCES doctor_slot(id)
);
I want that a user can't arrange an appointment with a doctor more than once in a day. So I want to add a unique constraint between doctor_id and user_id but in this structure I can't. I tried those things which are not in SQL syntax:
UNIQUE(user_id, doctor_slot.doctor_id)
and
UNIQUE(user_id, doctor_slot(doctor_id))
and
UNIQUE(user_id, doctor_id(doctor_slot))
But as you know, they didn't work. Are there any suggestions you can make?
Based on your comment about the what the doctor_slot is, it would seem you have a bit on an issue with your schema design. There should be no reason for you to store both a slot_id and a date in the appointment table, in that the doctor_slot already has a date component, so storing the date in the appointment table is a redundant storage of data, and could become problematic to keep in sync.
Of course, without the date on this table it is impossible to force a unique constraint in the database for this table.
My recommendation for any type of calendar-based app like this, would be to first create a date table. I usually use a script like the one here: http://www.dwhworld.com/2010/08/date-dimension-sql-scripts-mysql/ to create this date table. Having such a table can allow you to use a simple date_id to reference all kinds of different information about a date (this is a technique commonly used in data warehouses). As long as you use this date_id in all the other tables where you need dates, it as extremely simple to look of dates in any fashion you desire (by day of week, month, week number, whether it is a weekday or not, etc.).
You could use a similar concept to build your timeslots. Maybe make a table that has 96 entries (24 hours * 15 minutes) to represent 15 minute intervals - obviously you can change this to whatever interval you like.
You could then build your appointment table like this:
appointment_id
user_id
doctor_id
date_id
time_start_id <= time slot for appointment start
time_end_id <= time slot for appointment end
Id don't see separate need for a doctor_slots table here. If you want to track open doctor slots, you could also do that in this table by having user_id simply = NULL until the slot is filled.
This would allow you to enforce unique index on user_id and date_id.

vb.net how can i monitor logged in user task such as add, delete, update data in programme

I am creating an application in vb.net in which many users can add, update, and modify data stored in sql, but one user at a time. Users have to login to the application and then they can start their work. I want to know how I can monitor their work after they log out, like which user updated the record and at which time. Please help me with the logic and how I can make this application for monitor user task.
I already created the database in mysql in which I can store user log in and log out time now I want to store their work.
For a very basic solution, create a new table that's used exclusively for logging. Each action creates an entry (row) in that table that links their user account with a description of the action.
CREATE TABLE UserActivity
(
ActivityId INT NOT AUTO_INCREMENT PRIMARY KEY,
UserId INT NOT NULL,
Message VARCHAR(255),
Timestamp TIMESTAMP
);
If you want more detail, you could create the table to accept foreign keys to the actual entries in the table(s) that was/were changed. But I would start simple with a VARCHAR concise description, e.g.
INSERT INTO UserActivity (UserId, Message)
VALUES (123, 'Updated user 456');
If you're not looking for a complete history and are just looking for who last touched it, you can add two columns to each table: LastUpdated & LastUser, then update those two columns when anything is altered. (As #MarkHall suggested in a comment)
ALTER TABLE mytable
ADD COLUMN last_updated TIMESTAMP
ADD COLUMN last_user INT;