MySQL multiple tables relationship (code opinion) - mysql

I have 4 tables: rooms(id, name, description), clients(id, name, email), cards(id, card_number, exp_date, client_id) and orders(id, client_id, room_id, card_id, start_date, end_date).
The tables are all InnoDB and are pretty much simple. What I need is to add relationships between them. What I did was to assign cards.client_id as a Foreign Key to db.clients and orders.client_id, orders.room_id and orders.card_id as Foreign Keys to the other tables.
My question: is this way correct and reliable? I never had the need to use Foreign Key before now and this is my first try. All the Foreign Keys are also indexes.
Also, what's the easiest way to retrieve all the information I need for db.orders ?
I need a query to output: who is the client, what's his card details, what room/s did he ordered and what's the period he's checked in.
Can I accomplish this query based on the structure I created?

You must create the FK's in all columns that relate to other tables. In your case, create on: cards.client_id, orders.client_id, orders.room_id, orders.card_id
In the case of MySQL it automatically creates indexes for these FK's.
On your select, I believe it can be the following:
SELECT * FROM orders
INNER JOIN client on client.id = orders.client_id
INNER JOIN cards on cards.client_id = client.id
INNER JOIN rooms on rooms.id = orders.room_id
I do not know what columns you need, there is only you replace the * by the columns you need, so SQL is faster.

Related

How Mysql indexes works in case of JOIN?

Suppose I have two tables patient, person
Mysql query is like below.
select fname , lname
from patient p
left join per on (per.person_id=p.person_id)
where p.account_id=2 and (per.fname like 'will%' OR per.lname like 'will%' ).
In case of this query how mysql will use index created on (p.account_id,p.person_id)
person_id is a foreign key from person table in patient table. .
I suspect you do not want LEFT. With LEFT JOIN, you are asking for account #2 whether or not he is named 'will'.
SELECT fname, lname
FROM patient p
JOIN per ON per.person_id = p.person_id
WHERE p.account_id = 2
AND (per.fname LIKE 'will% OR per.lname LIKE 'will%')
will find the full name of account #2 if it is a 'will', else return nothing.
You have not said what indexes you have, so we cannot explain your existing indexes. Please provide SHOW CREATE TABLE for each table.
For either version of the query, these indexes are the only useful ones:
p: INDEX(account_id) -- if it is not already the PRIMARY KEY
per: INDEX(person_id) -- again, if it is not already the PRIMARY KEY.
A PRIMARY KEY is a UNIQUE index.
The first index (or PK) would be a quick lookup to find the row(s) with account_id=2. The second would make the join work well. No index is useful for "will" because of the OR.
The query will look at patient first, then per, using "Nested Loop Join".
Please also provide EXPLAIN SELECT ..., so we can discuss the things I am guessing about.

SQL- Remove symmetrical duplicates

I have a database table called rates with four foreign keys and a decimal amount (primary key is understood). I am using MY-SQL database 5.6.17. I suspect that the data contains duplicate amounts for reverse combinations point_id_2 and point_id_2. The other two foreign keys,method_id and class_id seem to be mirrored in the respective tuples which appear to have duplicate amounts. See the image below.
If you look at the foreign keys,point_id_1, point_id_2 and the amount this is what I mean by "symmetrical data".
Is it possible to track down all rows where point_id_1 and point_id_2 are interchanged and the amounts are the same?
This way I can then decide on which rows to remove.
So you just want to know if you have duplicates on the two POINT_ID fields for the same amount? You just need a simple join on the fields you think are matching:
SELECT r1.*, r2.*
FROM
rates r1
INNER JOIN rates r2
ON r1.point_ID_1 = r2.point_ID_2 AND r1.point_ID_2 = r2.point_ID_1
WHERE
r1.amount = r2.amount

Is it really necessary to create foreign key in this query?

I have a table called investors having a primary key as ID. I also have additional tables named Users Login_Logs and Accounts. All of these tables contain the foreign key investor_id. Between investors and Users we have a one to one relation. between investors and Login_Logs a one to many relation and between investors and Acccounts a one to many relation as well. My question is in order to create a query that loads info contained in the table Users, Login_Logs and Accounts - do I need to store the Users id, Accounts id and Login_Logs id in the investors table? I mean, do I need to create foreign keys in the investors table for all columns?
No, foreign key constraints don't affect joins and read queries. They ensure that the values in the child column(s) exist in the referenced column(s). They're used for integrity, not for linking rows or tables.
AFAICT recording the user id in your investors table is redundant, and recording the account and login_log ids in investors isn't practical.
To be able to join the tables efficiently, what you need is to index the investor_id in each of the tables. Then, it's up to your query to connect the tables as required.
The problem with retrieving all the information about investors at the same time is that you have (multiple) one-to-many relations. If we join all the tables:
SELECT *
FROM investors i
JOIN users u ON i.ID = u.investor_id
JOIN accounts a ON i.ID = a.investor_id
JOIN login_logs l ON i.ID = l.investor_id
Then, if an investor has 2 accounts and 2 login_logs, then we'll get 4 rows. SQL databases can't nest related data. Instead, you may have to use 3 queries to retrieve everything about investors:
SELECT *
FROM investors i
JOIN users u ON i.ID = u.investor_id
SELECT *
FROM accounts
SELECT *
FROM login_logs
Then process the results in code. You could process the combined query above programmatically, but it's a bit more complicated.

Need MySQL key indexed?

Let say i have two tables,
for the sake of question, let's assume that they are two tables called customers and cars.
Customers
id name age
Cars
id customer_id brand_id engine cc
Do we need to index customer_id? Does it give any advantage?
like to highlight that on InnoDB, index automatically created on foreign key columns.
see innodb-foreign-key-constraints
in your case customer_id if the foreign key constraint is applied.
Yes it is, you probably want to join the customers table, you need to put a index on customer_id so the lookup can be done faster.
But like said in the comments, it depends, if you're not going to join the customers table (or do a WHERE / GROUP BY / ORDER BY etc. on it) and purely use it do display the id, it is not necassery.
Depending on your application business logic and how you will query the base, having an index on customer_id will give you a huge advantage on queries like
select * from customers join cars on customer_id = customers.id -- list all customers with their associated cars
Or even
select * from cars where customer_id = 2 -- list all cars for user 2
More generally, it is always a good idea to index foreign key constraints.

Database better inner join for performance in this case

I have 2 tables that manages the time spent on doing various things:
#times(id, time_in_minutes)
#times_intervals(id, times_id, time_in_minutes, start, end)
Then the #times might relate to different things:
#tasks(id, description)
#products(id, description, serial_number, year)
What is the best practice in order to reuse the same #times and #times_intervals for #task and #products?
I would think about:
#times(+task_id, +product_id)
// add task_id and product_id to the original #times table
But if I do so, when I'd join the #times table with #task and #products table would be slower as should choice between the 2 (task_id or product_id). When task_id is not null join on the #tasks and viceversa.
(I'm using MySQL6)
Thanks a lot
I would drop the time_in_minutes column from the times table. This information is redundant if it is just the sum of the detail and is a premature optimization.
I would add a product_time table containing product_id, times_id and a task_time table containing task_id, time_id
Then to get the total time with a product:
SELECT *
FROM product p
INNER JOIN product_time pt
ON pt.product_id = p.id
INNER JOIN (
SELECT times_id, SUM(time_in_minutes) as time_in_minutes
FROM times_intervals
GROUP BY times_id
) AS t
ON t.times_id = pt.times_id
Typically to make this perform, you would have a non-clustered covering index for times_intervals with columns times_id and time_in_minutes - note that the times table is simply a data-less header table at this point and the only purpose it to group the times_intervals and it's only necessary because you have this very similar arrangement for tasks.
If there were not two (or more) entities using the times_intervals, you might simply put product_id in the times_intervals and treat it as your header/master id.
I would suggest against adding an id column to times for every table you might join it to. It would break normalization and make joins much more complicated.
If you only have one time (or time interval) for a task or a product, make a column in that table that references the times table. Otherwise, you could make a separate table like
#multitimes(multi_id, time_id)
where the two columns together are a primary key, and then have products and tasks reference multi_id. Then each record in each of those tables can be related to any number of times without any conflicts.