[Tutorial] [MySQL] Table structure and foreign keys
#1

MySQL Table designs and foreign keys

Introduction
So apparently many people want to use MySQL, but they have no idea on how to properly design a table structure. MySQL is an RDB. Relational Database. With the emphasis on that first word. Make relations! Too many people try to make a large gamemode with one, or maybe two tables. No!

Note: This tutorial assumes that you are familiar with database terminology. Words like database, record, field, table, key, etc .. should not appear foreign to you.
Note 2: Your database must be running on the InnoDB engine. Foreign keys do not work with MyISAM.


More rows, less fields
Generally speaking, if you need to add a number to a field name then you're doing it wrong. Creating fields for something like color1, color2 for car colors is fine. Doing something like carmod1, carmod2, ... carmod13 is not. At any moment a vehicle may have 1 to 13 mods applied, but it will always have only 2 colors.

More tables
This is where relations come in. To optimize the example I just showed you, we will need two tables. The main table - we will call this the parent table - will store the vehicle's main information like spawnpos, modelid and so on. The second table - the child table - only contains the carmods, in addition to a reference to the parent table.

Parent table structure
Table name: vehicle_info
Structure:
Field name r_id modelid spawnx spawny spawnz spawna col1 col2
Field type int(11) smallint(3) float float float float tinyint(3) tinyint(3)
Attributes
  • unsigned
  • not null
  • auto_increment
  • primary_key
  • unsigned
  • not null
  • not null
  • not null
  • not null
  • not null
  • unsigned
  • not null
  • unsigned
  • not null
You will notice that I'm not using the generic integer type. Using smaller types saves space in the long run. An unsigned tinyint can save values between 0 and 255, which is just enough for the amount of carcolors there are.

Child table structure
Table name: vehicle_mods
Structure:
Field name r_id mod_model
Field type int(11) smallint(5)
Attributes
  • unsigned
  • primary key
  • unsigned
  • primary key
You see that this design is much more compact as we now only need 2 fields instead of 13. When a mod is added to a vehicle a row is inserted into the table. Similarly, when a mod is deleted from the vehicle, the corresponding row is deleted from the table.

Since each row is unique (a vehicle can't have the same mod applied twice) we will use both fields as the primary key. This will prevent duplicate entires.

Creating relations
Now, up to creating the relation between the tables. If a vehicle is deleted then we don't want orphaned rows in the child table (that is: referencing a vehicle that doesn't exist in the parent table). The most obvious way would be to send a query to delete these rows, right? Wrong. MySQL can automatically delete (or update) these rows when the referenced row in the parent table is deleted (or updated).

The child table always references the parent table, so that makes up for this query:
PHP Code:
ALTER TABLE vehicle_mods 
ADD FOREIGN KEY 
(r_id
REFERENCES vehicle_info (r_id
ON UPDATE CASCADE 
ON DELETE CASCADE 
The relation may also be added directly when the table is created by using REFERENCES ... after the field declaration. The type and attributes of the fields that are being linked must be exactly the same, otherwise it won't work. There also may not be any data in the table that would conflict with the creation of the key.

There are four types of clauses you can use for ON DELETE and ON UPDATE:
  • RESTRICT: Trying to delete or update a referenced key in the parent table will fail as long as there are still records in the child table that are linking to the key in the parent table. This is the default.
  • NO ACTION: Same as restrict
  • CASCADE: If the referenced key is updated or deleted in the parent table then the change will be reflected to the child table. Best option, usually.
  • SET NULL: If the referenced key is updated or deleted in the parent table then the engine will set the referee keys to NULL. This requires that the field accepts NULL values. Not very useful.
Be VERY careful with ON DELETE CASCADE, though. For example, if you're linking a user to a house: if the user is deleted then the house is also deleted. Most likely not what you want.

Verification
To verify that stuff works, put some data in both tables. Start with the parent table. Use the r_id that you just obtained - by means of auto_increment - to insert stuff into the child table. Now delete the row in the parent table. If all went well, the corresponding row in the child table should have vanished.

Lastly ...
Ironically, although this tutorial is about database design, the design of this thread leaves much to be desired. Please let me know if you have any questions, or additions.
Reply
#2

Good tutorial. SERIAL alias is nice for ids (however it uses bigint, so your is better for most cases). One thing though: NO ACTION - it really means same thing as restrict?

#sidenote:
Example of fetching related data in single query (all mods for vehicle at specific spawnpoint)
Quote:

SELECT t0.mod_model FROM `vehicle_mods` t0 LEFT JOIN `vehicle_info` t1 ON t1.r_id = t0.r_id
WHERE t1.spawnx = %.2f AND t1.spawny = %.2f AND t1.spawnz = %.2f

Load vehicle models and check optionally if the vehicle has mod with id 512351
Quote:

SELECT vi.r_id, vi.modelid, vm.mod_model FROM `vehicle_info` vi LEFT JOIN `vehicle_mods` vm ON vm.r_id = vi.r_id AND vm.mod_model = 512351

Yup - no where clause, or it won't return vehicle data. If there isn't mod with id 512351, then the vm.mod_model will return null.

Moar info to be found via ******.
Reply
#3

Nice, I was hoping you would make this for foreign keys...

Edit: I was hoping that you would do the tutorial based on 2 tables such as one for `users` and one for `playerweapons` which are two tables...
Reply
#4

Finally someone who understands the REAL uses of MySQL!

Nice tutorial, Vince!
Reply
#5

Really nice job and I like verry much that thing with the child table xD. I learned about things like this at school but only in microsoft office )
Reply
#6

One of the best MySQL tuts around, but I think it's missing a few essential things:
  • relationship types
  • foreign key indexing
  • clustered index
I've been taught that every table should have a clustered index, especially when foreign keys are involved. A useful quote:
Quote:
Originally Posted by Brad McGehee
As a rule of thumb, every table should have a clustered index. Generally, but not always, the clustered index should be on a column that monotonically increases–such as an identity column, or some other column where the value is increasing–and is unique. In many cases, the primary key is the ideal column for a clustered index.
http://www.sql-server-performance.co...tered-indexes/
Also, If anyone's interested, there's a visual database design tool called MySQL Workbench, which is perfect for designing relational databases.
Reply
#7

Quote:
Originally Posted by YJIET
View Post
One of the best MySQL tuts around, but I think it's missing a few essential things:
  • relationship types
  • foreign key indexing
  • clustered index
I've been taught that every table should have a clustered index, especially when foreign keys are involved. A useful quote:

Also, If anyone's interested, there's a visual database design tool called MySQL Workbench, which is perfect for designing relational databases.
I'm pretty sure indexed are added automatically by most database systems. I'm entirely sure for MySQL and Postgresql. You make a fair point, they do make a huge difference.

@OP

You should tell something about the good practices when creating queries. Things people should avoid (like using IN, SET operators, explicit joins, LIMIT, etc...) + why you should avoid subqueries (they are always slower) and things people should definately use or atleast attempt to use (like JOINs and GROUP BY). If a query is properly built up it can be executed 100x faster.

My course material explained it really well, unfortunately it's in dutch. I found this though: http://docs.oracle.com/cd/B10501_01/...1016.htm#28529
Reply
#8

nice tut very good this will sure help alot of ppl now with using mysql datebase
Reply
#9

nice tutorial, will be useful
Reply
#10

Quote:
Originally Posted by Sinner
View Post
I'm pretty sure indexed are added automatically by most database systems. I'm entirely sure for MySQL and Postgresql. You make a fair point, they do make a huge difference.
I know that primary key constraints create clustered indexes automatically, but I wasn't sure about foreign key indexes. I thought it was MySQL Workbench that was automatically creating indexes for me, but it was probably MySQL that was actually doing it.
Reply
#11

Would this work for a house renting system? Say I made a 'house_renters' table with the 'hID' row working along side the 'ID' row of the 'houses' table.
Reply
#12

If your amount of renters is n > 1, you'll most likely be better of doing that. If you'll maximally have one or two renters per house, you don't have to.

It would be a crime to have a string list delimited by commas for example. Look up 1st Formal Norm and database normalization to know more about the subject.
Reply
#13

EDIT: Thanks, it is now working.

Nice tutorial! +rep.
Reply
#14

Perfect tutorial Vince !
Reply
#15

So how do i make it if I change the username in the parent table, it gets updated in all tables?
Reply
#16

You shouldn't have a username in any other table but the base table. All other tables should link to it via an id. I guess you can make a foreign key that references the name field, but it surely isn't the most effective way.
Reply
#17

Nice tutorial. I have looked in to foreign keys before and gave up, but they seem so simple I may have to look in to it again.
Reply
#18

Excellent tutorial. It's about time somebody expressed the importance of database normalisation around here!
Reply
#19

Firstly, i know that i'm bumping a pretty old thread, but since it's a tutorial i think it's okay.

I just wanted to ask, is there like part 2 of this? It's really a great tutorial and i want to learn more about table structures and other cool MySQL stuff (like these relations).

The manual is pretty boring....This thread was way clearer for me.
Reply
#20

Quote:
Originally Posted by dusk
Посмотреть сообщение
Firstly, i know that i'm bumping a pretty old thread, but since it's a tutorial i think it's okay.

I just wanted to ask, is there like part 2 of this? It's really a great tutorial and i want to learn more about table structures and other cool MySQL stuff (like these relations).

The manual is pretty boring....This thread was way clearer for me.
This gives you a pretty good idea:
http://databases.about.com/od/specif...malization.htm
Reply


Forum Jump:


Users browsing this thread: 8 Guest(s)