Dynamically structured mySQL database for an Airline Reservation App - mysql

I am working on a personal project and needed some guidance with a mySQL database. I am trying to simulate an airlines reservation system where a user has a personal account added to an Accounts db table (with username and password information). Then I have my Flights db table to store all of the available flights. The Flights db table has all of the flights with information (columns) on departure city, arrival city, departure time, arrival time, price, available seats, and seat capacity.
My question is:
When a user books a flight I must update the available seat value (int) in the Flights table. But then I also must add the flight to the user's account in the Accounts table. I am wondering how to handle this in mySQL, do I have to add a new column to the Accounts table with the corresponding flight's table index from the Flight's db table. Then continue adding a booked flight column as a user books flights to keep track of all the flights a user has booked? Is there a better way to update the Accounts table with flights booked information?
All help is very much appreciated.

Honestly, I would probably go about this a little differently. You may think about adding an additional table that tracks seat reservations. Something like:
TABLE: Accounts
ID | First Name | Last Name | Username | Password
TABLE: Flights
ID | DepartureCity | ArrivalCity | DepartureTime | Price | SeatingCapcity
TABLE: Reservations
ID | Account_ID | Flight_ID | SeatNumber
You can then use SQL functions and math to determine the number of seats available on a particular flight and maintain just the Reservations table when making updates. This also links all flights a particular account is associated with and is not "hard coded" to a particular column. (ie: A user can reserve more than one flight)

The best way to achieve this is to introduce a new table Bookings. A possible structure might be
CREATE TABLE Bookings (user_id INTEGER NOT NULL, flight_id INTEGER NOT NULL);
Then you can fetch all flights of a given user by
SELECT f.* FROM Users u
LEFT JOIN Bookings b ON u.id = b.user_id
LEFT JOIN Flights f ON b.flight_id = f.id
WHERE u.id = ?
And count the bookings for a given flight by
SELECT count(*) FROM Flights f
LEFT JOIN Bookings b
WHERE f.id = ?
This structure has several advantages:
You don't have to change the schema if users book more and more flights. Changing the schema of your database is a quite complex an expensive operation. (BTW: There is a maximum column count in MySQL)
You have an point where you can add further fields that are concerned with bookings. Maybe you want to track reservations.
Its more natural to fetch and add data. If you add columns booked_flight_1 booked_flight_2 and so on. You have to check which slot isn't already taken, which gets more and more complex when you consider deletions. Here it as easy as running this
INSERT INTO Bookings (user_id, flight_id) VALUES (1, 2)
If have to check if a given flight is available, it might be better to check this using your program and insert the booking into the database, if it is.
Joins and transactions might be the techniques you will find useful. The MySQL documentation and every good book on relational databases will give you an introduction in this. Your question sounds to me, as a classical instructional example.

Related

MySQL: What is the most performant way of storing a whole year (365 elements) in MySQL?

Within a project, I have to store the daily availability of different meeting rooms. So let's suppose around 1.000.000 meeting rooms, where each meeting room is in a city, and a city could have hundreds of meeting rooms.
I would also like to be able to make SELECT Queries entering the city and the availability that I need, so I would like to get a list of the available meeting rooms for a day, or set of continues days, in a concrete city.
I have one table called "MeetingRoom", when I store the city and the name of the meeting room, but my design question is how to design the availability part:
Is it better in terms of performance to have a binary array that stores the 365 days of the year with a '1' or '0' according to the availability of the meeting room?
Is it better in terms of performance to have another table called "Availability" that stores a DATE and a BIT with the availability, and then JOIN both tables for each meeting room that exists in a city?
Could it better another option I don't have in mind?
I wonder what querying time would be optimal having around 1.000.000 meeting rooms, and searching for the meeting rooms in a city and available for concrete days. Is it crazy about thinking in database responses below 100 ms?
Some colleagues told me that I should also consider to migrate to MongoDB or NoSQL approach. I don't know if a change of DB could fit better with my issues, or if it don't. Any comment about this?
Thank you very much for the help!!
I don't know if this will help, but if it doesn't, please write me to delete the answer.
Instead of these options you may want to consider that in the "Availability" table to store only the id(surrogate) of the room and the date on which it is reserved. So when you select the data and join both tables you will get only the reserved rooms. I personally think that there is no point of storing all of the room-date relations with status.
Moreover, to improve the performance you can create non-clustered index on the City column for instance.
Please don't fill your database with lots of rows which are default values.
so you don't store availability of a meeting room, you store booking of a meeting room.
primary key of booking table is date and room id , other fields are who booked this room, when booking was asked, a booking id...
If it is possible to book meeting room for part of the day then primary key should be start_date and room id, end date is stored in another field of the table.

how to design: users with different roles see different records

I have a schema design question for my application, hope I can get advices from teachers. This is very alike of Role Based Access Controll, but a bit different in detail.
Spec:
For one company, there are 4 roles: Company (Boss) / Department (Manager) / Team (Leader) / Member (Sales), and there are about 1 million Customers records. Each customer record can be owned by someone, and he could be Boss or Manager or Leader or Sales. If the record's owner is some Sales, then his upper grade (say: his leader / manager / boss) can see this record as well (but others: say the same level of his workmates, cannot see, unless his upper grade manager share the customer to his workmates), but if the record's owner is boss, none except the boss himself can see it.
My Design is like this (I want to improve it to make it more simple and clear):
Table:
departments:
id (P.K. deparment id)
d_name (department name)
p_id (parent department id)
employees
id (P.K. employee id)
e_name (employee name)
employee_roles
id (P.K.)
e_id (employee id)
d_id (department id)
customers
id (P.K. customer id)
c_name (customer name)
c_phone (customer phone)
permissions
id (P.K.)
c_id (customer id)
e_id (owner employee id)
d_id (this customer belongs to which deparment)
share_to (this customer share to other's id)
P.S.: each employee can have multi roles, for example, employee A can be the manager of department_I and meanwhile he can also be one sales of deparment_II >> Team_X.
So, when an employee login to application, by querying from employee_roles table, we can get all of the department ids and sub department ids, and save them into an array.
Then I can use this array to query from permissions table and join it with customers table to get all the customers this employee should see. The SQL might look like this:
SELECT * FROM customers AS a INNER JOIN permissions AS b ON a.id =
b.c_id AND (b.d_id IN ${DEP_ARRAY} OR e_id = ${LOGIN_EMPLOYEE_ID} OR
share_to = ${LOGIN_EMPLOYEE_ID})
I don't really like the above SQL, especially the "IN" clause, since I am afraid it will slow down the query, since there are about 1 million records or even more in the customer table; and, there will be as many records as the customers table in the permissions table, the INNER JOIN might be very slow too. (So what I care about is the performance like everyone :))
To my best knowledge, this is the best design I can work out, could you teachers please help to give me some advice on this question? If you need anything more info, please let me know.
Any advice would be appreciated!
Thanks a million in advance!!
Do not use an array, use a table, ie the value of a select statement. And stop worrying about performance until you know more basics about thinking in terms of tables and queries.
The point of the relational model is that if you structure your data as tables then you can looplessly describe the output table and the DBMS figures out how to calculate it. See this. Do not think about "joining"; think about describing the result. Whatever the DBMS ends up doing is its business not yours. Only after you become knowledgeable about variations in descriptions and options for descriptions will you have basic knowledge to learn about performance.

database design for user subscriptions

So I'm new to databases in the scope of the subject and looking for some advice for what I am sure is fairly simple. first I'm using MySql as my db I currently have two tables one for storing user accounts and details :
TABLE user
id | username | password | email_address | user_devices | contact_method
and another for storing video content by producers which looks like:
TABLE series
id | series_title | still_broadcasting | last_updated |
I would like to implement a feature where Users can select series which they wish to be notified of when new releases are made available and also select how to be notified about these releases (email or push notification ) and how often to be notified (on arrival, hourly, daily, weekly ) I am wondering whats the best way to go about doing this?
I've thought of these ideas by myself but am looking for a second opinion/ better way altogether: (all ideas minus 4 involve storing how to notify user along with how often in user table)
adding a text column to user table called following and just having csv's for each series
adding multiple boolean column's to user table one for each series
adding text column to series table with csv's of user's Id numbers following series
creating an entirely new table for notifications though i don't really see the purpose of this as its very redundant
I then plan to just add cron jobs to my server to actually go about regulaurly sending notifications to user's
Thanks in advance for any help.
First of all, it might be worth giving some articles on basic database design a read. A quick google turned up this which covers identifying relationships
http://www.datanamic.com/support/lt-dez005-introduction-db-modeling.html
Your best bet is to use a linking table i.e.
CREATE TABLE userHasSeries (
userID INT,
seriesID INT
);
This can then be used in an INNER JOIN query to get the users choices. What you are doing here is an n:m link between 2 tables. An example inner join would be
SELECT
u.id AS userID,
u.username,
s.seriesID,
s.series_title,
s.still_broadcasting,
s.last_updated
FROM users AS u
INNER JOIN userHasSeries AS uhs
ON uhs.userID = u.id
INNER JOIN series AS s
ON s.id = uhs.seriesID
If users.user_devices is also a comma seperated list I would advise heavily that you adopt a similar n:m approach there also.
A partial answer which complements what has been written in other answers:
Don't keep a list of devices in the 'user_devices' field - break this out into a separate table. In fact, you'll need two tables: one to list the various devices, and one a join table which has two fields: user_id and device_id. This will enable you to track which user has which device, but also to provide a list of users per device.
If I were you I would add a third table as following:
TABLE user
id | username | password | email_address | user_devices | contact_method |notification_type
TABLE series
id | series_title | still_broadcasting | last_updated
TABLE followings
id | user_id | series_id
In notification_type I would put (on arrival, hourly, daily, or weekly), now in the followings tables I will store all the user's preferred series.
Doing this way makes easy to add, delete, update, or select all user's preferred series. All will be simple SQL queries. Also you avoid parsing comma separated strings.
for example, if you want to get all preferred series of an user:
SELECT * FROM followings AS f INNER JOIN series AS s ON f.series_id = s.id WHERE f.user_id = ?
if want to get all users that prefer a serie:
SELECT * FROM followings AS f INNER JOIN user AS u ON f.user_id = u.id WHERE f.series_id = ?

MySQL innerjoin 3 tables

Supposing I had 3 tables
Passenger (Id [pk], name)
Airplane (registration [pk], num_seats)
Booking (Id, passenger_id [fk -> Passenger.Id], airplane_registration [fk -> Airplane.registration])
The booking table would be a junction object here as there is a many to many relationship between tables Passenger and Booking.
How could I select all the details related to a particular booking id (say 'abcde')?
Is this correct:
SELECT Passenger.name, Airplane.num_seats, Booking.Id
FROM Booking
JOIN Passenger ON Passenger.Id = Booking.passenger_Id
JOIN Airplane ON Booking.airplane_registration = Airplane.registration
WHERE Booking.Id = 'abcde';
Is this the right way to do it?
Also, If I wanted to select all bookings and their details, would I do it the same way? (Without the where clause)?
I was looking to find out if this was correct for a test as MySQL has gone down on my machine.
Yes, your query would work for getting the details of the passengers and the flight for the particular booking ID. For getting all bookings, I would add an ORDER BY bookingID and if needed by passenger name and flight registration.
Without knowing your schema, there's no way to be sure, but that looks just fine to me.

MySQL Multiple interests matching problem

I have a database where users enter their interests. I want to find people with matching interests.
The structure of the interest table is
interestid | username | hobby | location | level | matchinginterestids
Let's take two users to keep it simple.
User Joe may have 10 different interest records
User greg may have 10 different interest records.
I want to do the following algorithm
Take Joe's interest record 1 and look for matching hobbies and locations from the interest database. Put any matching interest id's in the matches field. Then go to joe's interest record 2 etc..
I guess what I need is some sort of for loop that will loop through all of joe's intersts and then do an update each time it finds a match in the interest database. Is that even possible in MySQL?
Further example:
I am Dan. I have 3 interests. Each interest is composed of 3 subjects:
Dan cats,nutrition,hair
Dan superlens,dna,microscopes
Dan film,slowmotion,fightscenes
Other people may have other interests
Joe:
Joe cats,nutrition,strength
Joe superlens,dna,microscopes
Moe
Moe mysql,queries,php
Moe film,specialfx,cameras
Moe superlens,dna,microscopes
Now I want the query to return the following when I log in as Dan:
Here are your interest matches:
--- is interested in cats nutrition hair
Joe is interested in cats and nutrition
Joe and Moe are interested in superlens, dna, microscopes
Moe is interested in film
The query needs to iterate through all Dan's interests, and compare 3,2,1 subject matches.
I could do this in php from a loop but it would be calling the database all the time to get the results. I was wondering if there's a crafty way to do it using a single query Or maybe 3 separate queries one looking for 3 matches, one for 2 and one for 1.
This is definitely possible with MySQL, but I think you may be going about it in an awkward way. I would begin by structuring the tables as follows:
TABLE Users ( userId, username, location )
TABLE Interests( interestId, hobby )
TABLE UserInterests( userId, interestId, level )
When a user adds an interest, if it hasn't been added before, you add it to the Interests table, and then add it to the UserInterests table. When you want to check for other nearby folks with similar interests, you can simply query the UserInterests table for other people who have similar interests, which has all that information for you already:
SELECT DISTINCT userId
FROM UserInterests
WHERE interestId IN (
SELECT interestId
FROM UserInterests
WHERE userId = $JoesID
)
This can probably be done in a more elegant fashion without subqueries, but it's what I thought of now.
As per special request from daniel, although it's kind of duplicate but never mind.
The schema explained
TABLE User (id, username, location )
TABLE Interests(id, hobby )
TABLE UserInterest(userId, interestId, level )
Table users has just user data and a primary key field at the start: id.
The primary key field is a pure link field, the other fields are info fields.
Table Interest again has a primary key that is use to link against and some info field
(ehm well just one, but that's because this is an example)
Note that users and interests are not linked in any way whatsoever.
That's odd, why is that?
Well there is a problem... One user can have multiple intrests and intrests can belong to multiple people.
We can solve this by changing to users table like so:
TABLE users (id, username, location, intrest1, intrest2, intrest3)
But this is a bad, really really bad idea, because:
This way only 3 interests per user are allowed
It's a waste of space if many users have 2, 1 or no interests
And most important, it makes queries difficult to write.
Example query for linking with the bad users table
SELECT * FROM user
INNER JOIN interests ON (user.intrest1 = interests.id) or
(user.intrest2 = interests.id) or
(user.intrest3 = interests.id);
And that's just for a simple query listing all users and their interests.
It quickly gets horribly complex as things progress.
many-to-many relationships
The solution to the problem of a many to many relationship is to use a link table.
This reduces the many-to-many relationship into two 1-to-many relationships.
A: 1 userinterest to many user's
B: 1 userinterest to many interests
Example query using a link-table
SELECT * FROM user
INNER JOIN userInterest ON (user.id = userInterest.userID) //many-to-1
INNER JOIN interest ON (interest.id = userInterest.InterestID); //many-to-1
Why is this better?
Unlimited number of interests per user and visa versa
No wasted space if a user has a boring life and few if any interests
Queries are simpler to maintain
Making it interesting
Just listing all users is not very fun, because then we still have to process the data in php or whatever. But there's no need to do that SQL is a query language after all so let's ask a question:
Give all users that share an interest with user Moe.
OK, lets make a cookbook and gather our ingredients. What do we need.
Well we have a user "Moe" and we have other user's, everybody but not "Moe".
And we have the interests shared between them.
And we'll need the link table userInterest as well because that's the way we link user and interests.
Let's first list all of Moe's Hobbies
SELECT i_Moe.hobby FROM interests AS i_Moe
INNER JOIN userInterests as ui2 ON (ui2.InterestID = i_Moe.id)
INNER JOIN user AS u_Moe ON (u_Moe.id = ui2.UserID)
WHERE u_Moe.username = 'Moe';
Now we combine the select for all users against only Moe's hobbies.
SELECT u_Others.username FROM interests AS i_Others
INNER JOIN userinterests AS ui1 ON (ui1.interestID = i_Others.id)
INNER JOIN user AS u_Others ON (ui1.user_id = u_Others.id)
/*up to this point this query is a list of all interests of all users*/
INNER JOIN Interests AS i_Moe ON (i_Moe.Hobby = i_Others.hobby)
/*Here we link Moe's hobbies to other people's hobbies*/
INNER JOIN userInterests as ui2 ON (ui2.InterestID = i_Moe.id)
INNER JOIN user AS u_Moe ON (u_Moe.id = ui2.UserID)
/*And using the link table we link Moe's hobbies to Moe*/
WHERE u_Moe.username = 'Moe'
/*We limited user-u_moe to only 'Moe'*/
AND u_Others.username <> 'Moe';
/*and the rest to everybody except 'Moe'*/
Because we are using INNER JOIN's on link fields only matches will be considered and non-matches will be thrown out.
If you read the query in english it goes like this.
Consider all users who are not Moe, call them U_others.
Consider user Moe, call him U_Moe.
Consider user Moe's Hobbies, call those i_Moe
Consider other users's Hobbies, call those i_Others
Now link i_Others hobbies to u_Moe's Hobbies
Return only users from U_Others that have a hobby that matches Moe's
Hope this helps.