Should I relate all of my MySQL tables to each other? - mysql

I'm working on a personal project for timekeeping on various projects, but I'm not sure of the best way to structure my database.
A simplified breakdown of the structure is as follows:
Each client can have multiple reports.
Each report can have multiple line items.
Each line item can have multiple time records.
There will ultimately be more relationships, but that's the basis of the application. As you can see, each item is related to the item beneath it in a one-to-many relationship.
My question is, should I relate each table to each "parent" table above it? Something like this:
clients
id
reports
id
client_id
line_items
id
report_id
client_id
time_records
id
report_id
line_item_id
client_id
And as it cascaded down, there would be more and more foreign keys added to each new table.
My initial reaction is that this is not the correct way to do it, but I would love to get some second(and third!) opinions.

The advantage of the way you're doing it is that you could check all time records for, say, a specific client id without needing a join. But really, it isn't necessary. All you need is to store a reference back up one "level" so to speak. Here are some examples from the "client" perspective:
To get a specific client's reports: (simple; same as current schema you suggest)
SELECT * FROM `reports`
WHERE `client_id` = ?;
To get a specific client's line items: (new schema; don't need "client_id" in table)
SELECT `line_items`.* FROM `line_items`
JOIN `reports` ON `reports`.`id` = `line_items`.`id`
JOIN `clients` ON `clients`.`id` = `reports`.`client_id`
WHERE `clients`.`id` = ?;
To get a specific client's time entries: (new schema; don't need "client_id" or "report_id" in table)
SELECT `time_records`.* FROM `time_records`
JOIN `line_items` ON `line_items`.`id` = `time_records`.`line_item_id`
JOIN `reports` ON `reports`.`id` = `line_items`.`id`
JOIN `clients` ON `clients`.`id` = `reports`.`client_id`
WHERE `client_id` = ?;
So, the revised schema would be:
clients
id
reports
id
client_id
line_items
id
report_id
time_records
id
line_item_id
EDIT:
Additionally, I would consider using views to simplify the queries (I assume you'll use them often), definitely creating indexes on the join columns, and utilizing foreign key references for normalization (InnoDB only).

No, if there is no direct relation in the elements of the model, then there should not be direct relation in the corresponding tables. Otherwise your data will have redundancies and you will have problems for updating.
This is the right way:
clients
id
reports
id
client_id
line_items
id
report_id
time_records
id
line_id

You don't need to create client_id on line_items table if you never join line items directly clients, becouse you can get that by reports table. Same happens to others FKs.
I recommend you think in your report needs/queries over this collection of data before create redundant foreign keys who can complicate your development.
Create redundant FKs is not difficult if you need them in the future, some ALTERS and UPDATE SELECTS solves your problem.
If you not have so much information in the line_items, you can denormalize and add this info in the time_records.

Anywhere there is a direct relationship between two tables, you should use foreign keys to keep the data integrity. Personally, I would look at a structure like this:
Client
ClientId
Report
ReportId
ClientId
LineItem
LineItemId
ReportId
TimeRecord
TimeRecordId
LineItemId
In this example, you do not need ClientId in LineItem because you have that relationship through the Report table. The major disadvantage of having ClientId in all of your tables is that if the business logic does not enforce consistency of these values (a bug is in the code) you can run into situations where you get different values if you search based on
Report:
ReportId = 3
ClientId = 2
LineItem:
LineItemId = 1
ReportId = 3
ClientId = 3
In the above situation, you would be looking at ClientId = 2 if your query went through Report and ClientId = 3 if your query went through LineItem It is difficult once this happens to determine which relationship is correct, and where the bug is.
Also, I would advocate for not having id columns, but instead more explicit names to describe what the id is used for. (ReportId or ClientId) In my opinion, this makes Joins easier to read. As an example:
SELECT COUNT(1) AS NumberOfLineItems
FROM Client AS c
INNER JOIN Report AS r ON c.ClientId = r.ClientId
INNER JOIN LineItem AS li ON r.ReportId = li.ReportId
WHERE c.ClientId = 12

As personal opinion, I would have:
clients
id
time_records
id
client_id
report
line_item
report_id
That way all of your fields are over in the time_records table. You can then do something like:
SELECT *
FROM 'time_records'
WHERE 'time_records'.'client_id' = 16542
AND 'time_records'.'report' = 164652
ORDER BY 'time_records'.'id' ASC

Related

In vTiger 6.5: Which table stores the products that belongs to a quotes?

I need to know which table acts as an intermediary to achieve the many-to-many relationship between these entities.
I know that the table that stores the products is vtiger_products and that the one that keeps the quotes is vtiger_quotes but I do not know which table relates both, so my query is incomplete.
So...
SELECT * FROM vtiger_quotes
INNER JOIN vtiger_products INNER JOIN table_relates_both
ON vtiger_quotes.quoteid = table_relates_both.quoteid
AND vtiger_products.productid = table_relates_both.productid
WHERE vtiger_quotes.potentialid = ?
What's the real name of table_relates_both?
vtiger_inventoryproductrel is the intermediary table between vtiger_quotes and vtiger_products
Below is the structure of vtiger_inventoryproductrel where id column act as a foreign key of Quotes, Opportunity, Invoice etc
If you want to fetch Quotes related to particular Opportunity then you need to execute below query:
SELECT {your required field goes here} FROM vtiger_inventoryproductrel INNER JOIN vtiger_quotes
ON vtiger_quotes.quoteid = vtiger_inventoryproductrel.id
WHERE vtiger_quotes.potentialid = $potential_id
Also note that:
vtiger_crmentity - This is core table in which an entry is added for
all entity type records. This stores meta information like record id,
record owner id, last modified by user id, created time, modified time
and description.
the table name is vtiger_inventoryproductrel

mysql - Maintaining Subscription List of a Group in a Website

I'm creating a website where the users can join certain groups. Now I need to maintain the set of users in each group and/or the set of groups that each user has joined. Since MySql doesn't support arrays, I cannot maintain say, an array of users in a group(as a field in the "groups" table) or an array of groups in a user(as a field in the "users" table). So how can I achieve this?
My current solution is to maintain a table of group-subscriptions which has fields for the userID and groupID. So when I need either of these two lists I can do,
SELECT USERID FROM SUBSCRIPTIONS WHERE GROUPID=3
or
SELECT GROUPID FROM SUBSCRIPTIONS WHERE USERID=4
This will get me the desired lists. Is this the most efficient/standard way to do this or is there a better way?
You wrote all right.
Normally there are 3 types of relations between records in relative databases:
One - one (e.g. user and profile linked via user.profile_id = profile.id)
One - many (user and messages linked via message.user_id = user.id)
Many - many
Your case is the last and it always works via a 3rd table.
For your case it can be users_subscriptions (user_id, subscription_id)
Example query to select all users with their subscriptions:
SELECT u.name, GROUP_CONCAT(s.name) as `subscriptions`
FROM users u
JOIN users_subscriptions us ON us.user_id = u.id
JOIN subscriptions s ON us.subscription_id = s.id
GROUP BY u.id
If I understand your question correctly, that is the standard way.
You've created a "pivot table" that sits between the user table and the groups table and it stores the relationships between the two. This is the way that many-to-many relationships are stored in relational databases. As you correctly stated, you can retrieve all members of a group or all groups for a member that way.

Get stats table from a many to many relationship

I have a pivot table for a Many to Many relationship between users and collected_guitars. As you can see a "collected_guitar" is an item that references some data in foreign tables (guitar_models, finish).
My users also have some foreign data in foreign tables (hand_types and genders)
I want to get a derived table that lists data if I look for a particular model_id in "collected_guitar_user"
Let's say "Fender Stratocaster" is model id = 200, where the make is Fender (id = 1 of makes table).
The same guitar could come in a variety of finish hence the use of another table collected_guitars.
One user could have this item in his collection
Now what I want to find by looking at model_id (in this case 200) in the pivot table "collected_guitar_user" is the number of Fender Stratocasters that are collected by users that share the same genders.sex and hand_types.type as the logged in user and to see what finish they divide in (some percent of finish A and B etc...).
So a user could see that is interested in what others are buying could see some statistics for the model.
What query can derive this kind of table??
You can do aggregate counts by using the GROUP BY syntax, and CROSS JOIN to compute a percentage of the total:
SELECT make.make, models.model_name as model, finish.finish,
COUNT(1) AS number_of_users,
(COUNT(1) / u.total * 100) AS percent_owned
FROM owned_guitar, owned_guitar_users, users, models, make, finish
CROSS JOIN (SELECT COUNT(1) AS total FROM users) u
WHERE users.id = owned_guitar_users.user_id
AND owned_guitar_user.owned_guitar_id = owned_guitar.id
AND owned_guitar.model_id = models.id
AND owned_guitar.make_id = make.id
AND owned_guitar.finish_id = finish.id
GROUP BY owned_guitar.id
Please note though, that in cases where a user owns more than one guitar, the percentages will no longer necessarily sum to unity (for example, Jack and John could both own all five guitars, so each of them owns "100%" of the guitars).
I'm also a little confused by your database design. Why do you have a finish_id and make_id associated directly in the owned_guitar table as well as in the models table?

SQL Query to populate table based on PK of Main Table being joined

Here is my Database structure (basic relations):
I'm attempting to formulate a one-line query that will populate the clients_ID, Job_id, tech_id, & Part_id and return back all the work orders present. Nothing more nothing less.
Thus far I've struggled to generate this Query:
SELECT cli.client_name, tech.tech_name, job.Job_Name, w.wo_id, w.time_started, w.part_id, w.job_id, w.tech_id, w.clients_id, part.Part_name
FROM work_orders as w, technicians as tech, clients as cli, job_types as job, parts_list as part
LEFT JOIN technicians as techy ON tech_id = techy.tech_name
LEFT JOIN parts_list party ON part.part_id = party.Part_Name
LEFT JOIN job_types joby ON job_id = joby.Job_Name
LEFT JOIN clients cliy ON clients_id = cliy.client_name
Apparently, once all the joining happens it does not even populate the correct foreign key values according to their reference.
[some values came out as the actual foreign key id, not even
corresponding value.]
It just goes on about 20-30 times depending on largest row of a table that I have (one of the above).
I only have two work orders created, So ideally it should return just TWO Records, and columns, and fields with correct information. What could I be doing wrong? Haven't been with MySQL too long but am learning as much as I can.
Your join conditions are wrong. Join on tech_id = tech_id, not tech_id = tech_name. Looks like you do this for all your joins, so they all need to be fixed.
I really don't follow the text of your question, so I am basing my answer solely on your query.
Edit
Replying to your comment here. You said you want to "load up" the tech name column. I assume you mean you want tech name to be part of your result set.
The SELECT part of the query is what determines the columns that are in the result set. As long as the table where the column lives is referenced in the FROM/JOIN clauses, you can SELECT any column from that table.
Think of a JOIN statement as a way to "look up" a value in one table based on a value in another table. This is a very simplified definition, but it's a good way to start thinking about it. You want tech name in your result set, so you look it up in the Technicians table, which is where it lives. However, you want to look it up by a value that you have in the Work Orders table. The key (which is actually called a foreign key) that you have in the Work Orders table that relates it to the Technicians table is the tech_id. You use the tech_id to look up the related row in the Technicians table, and by doing so can include any column in that table in your result set.

MYSQL join tables based on column data and table name

I'm wondering if this its even posible.
I want to join 2 tables based on the data of table 1.
Example table 1 has column food with its data beeing "hotdog".
And I have a table called hotdog.
IS it possible to do a JOIN like.
SELECT * FROM table1 t join t.food on id = foodid
I know it doesnt work but, its even posible, is there a work arround?.
Thanks in advance.
No, you can't join to a different table per row in table1, not even with dynamic SQL as #Cade Roux suggests.
You could join to the hotdog table for rows where food is 'hotdog' and join to other tables for other specific values of food.
SELECT * FROM table1 JOIN hotdog ON id = foodid WHERE food = 'hotdog'
UNION
SELECT * FROM table1 JOIN apples ON id = foodid WHERE food = 'apples'
UNION
SELECT * FROM table1 JOIN soups ON id = foodid WHERE food = 'soup'
UNION
...
This requires that you know all the distinct values of food, and that all the respective food tables have compatible columns so you can UNION them together.
What you're doing is called polymorphic associations. That is, the foreign key in table1 references rows in multiple "parent" tables, depending on the value in another column of table1. This is a common design mistake of relational database programmers.
For alternative solutions, see my answers to:
Possible to do a MySQL foreign key to one of two possible tables?
Why can you not have a foreign key in a polymorphic association?
I also cover solutions for polymorphic associations in my presentation Practical Object Oriented Models In SQL, and in my book SQL Antipatterns Volume 1: Avoiding the Pitfalls of Database Programming.
Only with dynamic SQL. It is also possible to left join many different tables and use CASE based on type, but the tables would be all have to be known in advance.
It would be easier to recommend an appropriate design if we knew more about what you are trying to achieve, what your design currently looks like and why you've chosen that particular table design in the first place.
-- Say you have a table of foods:
id INT
foodtype VARCHAR(50) (right now it just contains 'hotdog' or 'hamburger')
name VARCHAR(50)
-- Then hotdogs:
id INT
length INT
width INT
-- Then hamburgers:
id INT
radius INT
thickness INT
Normally I would recommend some system for constraining only one auxiliary table to exist, but for simplicity, I'm leaving that out.
SELECT f.*, hd.length, hd.width, hb.radius, hb.thickness
FROM foods f
LEFT JOIN hotdogs hd
ON hd.id = f.id
AND f.foodtype = 'hotdog'
LEFT JOIN hamburgers hb
ON hb.id = f.id
AND f.foodtype = 'hamburger'
Now you will see that such a thing can be code generated (or even for a very slow prototype dynamic SQL on the fly) from SELECT DISTINCT foodtype FROM foods given certain assumptions about table names and access to the table metadata.
The problem is that ultimately whoever consumes the result of this query will have to be aware of new columns showing up whenever a new table is added.
So the question moves back to your client/consumer of the data - how is it going to handle the different types? And what does it mean for different types to be in the same set? And if it needs to be aware of the different types, what's the drawback of just writing different queries for each type or changing a manual query when new types are added given the relative impact of such a change anyway?