I see two ways it is done:
Method 1:
CREATE TABLE IF NOT EXISTS `sample` (
`sample_id` tinyint(2) NOT NULL AUTO_INCREMENT,
`description` varchar(32) NOT NULL,
`parent_id` int(10) NOT NULL,
`created` datetime NOT NULL,
PRIMARY KEY (`sample_id`)
) ENGINE=InnoDB;
ALTER TABLE sample ADD CONSTRAINT parent_id FOREIGN KEY (parent_id) REFERENCES parent_tbl(parent_id);
Method 2:
CREATE TABLE IF NOT EXISTS `sample` (
`sample_id` tinyint(2) NOT NULL AUTO_INCREMENT,
`description` varchar(32) NOT NULL,
`parent_id` int(10) NOT NULL,
`created` datetime NOT NULL,
PRIMARY KEY (`sample_id`),
Foreign Key (parent_id) references parent_tbl(parent_id)
) ENGINE=InnoDB;
Which way is better or when to use one over the other?
If you need to add a foreign key to an existing table, use method 1, if you are creating the schema from scratch use method 2.
There isn't a best way, they do the same thing.
The first gives you more flexibility.
1) You are required to use the first method if you create the tables in an order such that a referenced table is created after its referencing table. If you have loops in your references then there may not be a way to avoid this. If there are no loops then there exists an order where all referenced tables are created before their referenced tables, but you may not want to spend time figuring out what that order is and rearranging your scripts.
2) It's not always the case that you know exactly what indexes you will need when you create the table. When you create indexes it is usually a good idea to measure the performance gain on some real data, and perhaps try multiple different indexes to see which works better. For this strategy to work you need to first create the table, insert some data and then you need to be able to modify the indexes for testing. Dropping and recreating the table is not as practical as ALTER TABLE in this situation.
Other than that there isn't really any difference and if you are starting from nothing there is no particular reason to favour one over the other. The resulting index is the same either way.
The end products are indistinguishable.
For clarity (it's nice to see the constraint explictly stand on it's own), I might advocate for the first.
For succinctness (saying the same thing in 1 statement vs 2), I'd might advocate for the second.
Related
Two tables:
CREATE TABLE `htmlcode_1` (
`global_id` int(11) NOT NULL,
`site_id` int(11) NOT NULL,
PRIMARY KEY (`global_id`),
KEY `k_site` (`site_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `htmlcode_2` (
`global_id` int(11) NOT NULL,
`site_id` int(11) NOT NULL,
PRIMARY KEY (`site_id`,`global_id`),
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
which one should be faster for selects and why?
'select * from table where site_id=%s'
The latter table is probably slightly faster for that SELECT query, assuming the table has a nontrivial number of rows.
When querying InnoDB by primary key, the lookup is against the clustered index for the table.
Secondary key lookups require a lookup in the index, then that reveals the primary key value, which is then used to do a lookup by primary key. So this uses two lookups.
The reason to use a PRIMARY KEY is to allow for either quick access OR REFERENTIAL INTEGRITY (CONSTRAINT ... FOREIGN KEY ...)
In your second example, you do not have the proper key for referential integrity if any other table refers to your table. In that case, other operations will be very very slow.
The differences in speed in either case for your particular case should be too small and trivial, but the proper design will dictate the first approach.
The first table represents many "globals" in each "site". That is, a "many-to-one" relationship. But it is the "wrong" way to do it. Instead the Globals table should have a column site_id to represent such a relationship to the Sites table. Meanwhile, the existence of htmlcode_1 is an inefficient waste.
The second table may be representing a "many-to-many" relationship between "sites" and "globals". If this is what you really want, then see my tips . Since you are likely to map from globals to sites, another index is needed.
I have a table SkillLevel created as
CREATE TABLE `sklllevel` (
`Name` varchar(20) NOT NULL,
`level` enum('No Experience','Beginner','Expert','Advisor') DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
With Values
INSERT INTO test.sklllevel (name,level) values ('No Experience','No Experience'),('Beginner','Beginner'),('Expert','Expert'),('Advisor','Advisor');
I want to refer SkillLevel.Level with testSkill.tkSkill in another table created as:
CREATE TABLE `testskill` (
`pkid` int(11) NOT NULL,
`name` varchar(45) DEFAULT NULL,
`tkSkill` tinyint(4) DEFAULT NULL,
PRIMARY KEY (`pkid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
DO I need to have tkSkill as ENUM with same set of Values to set a foreign key? What is the best practise here?
Short answer: enums are stored as a number, so technically, you could join them with tkSkill as a tinyint.
To use it as a foreign key, you indeed need to have both tkSkill and level to be the same enum - but you need level to be a unique column to qualify as a foreign key, so add unique to it (to be really precise: for InnoDB, you can have non-unique foreign keys if you manually create the index, but non-unique foreign keys are generally a bad idea). But you should think about what your key in sklllevel should be, since now it looks as if you want Name to be the key.
And independently from having it as key you should define tkSkill as (the same) enum to make sure they both mean the same if you at one point would like to change the enums (what is a bad idea!) and e.g. add another skilllevel; or if you want to "read" (directly understand) the value when you select directly from the table testskill without the need to join sklllevel; and if you want to insert values into tkSkill by using their enum-namoe (e.g. 'Expert' instead of 3, but you can use both) without looking them up in sklllevel.
Longer answer: Best practice is: don't use enums. Depending on "belief", there is none to only a handful of cases when enums might be slightly useful, if at all. One could be that you don't want to use a reference table to skip a join to display the textvalue/description of an integer-id. In your setup, you are actually using a reference table and still want to use enums.
The closest you'll get to best practice using enums would be to define tkSkill as enum in testskill and don't have the sklllevel-table (the reference table) at all.
But again, I would urge you not to use enums. You can define your table as
CREATE TABLE `sklllevel` (
`Id` tinyint(4) primary key,
`Name` varchar(20) NOT NULL,
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
and then use that id as a foreign key for tkSkill. Or even
CREATE TABLE `sklllevel` (
`Name` varchar(20) primary key
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
and then define tkSkill as varchar(20) and use this as a foreign key - it will use more space, though, but you will have "readable" values in the table if that was the reason for you to use enums in the first place.
Here you can find some background to enums: 8 Reasons Why MySQL's ENUM Data Type Is Evil
I'm trying to achieve a "One to one" relationship in a MySQL database. For example, let's say I have a Users table and an Accounts table. I want to be sure that a User can have only one Account. And that there can be only one Account per User.
I found two solutions for this but don't know what to use, and are there any other options.
First solution:
DROP DATABASE IF EXISTS test;
CREATE DATABASE test CHARSET = utf8 COLLATE = utf8_general_ci;
USE test;
CREATE TABLE users(
id INT NOT NULL AUTO_INCREMENT,
user_name VARCHAR(45) NOT NULL,
PRIMARY KEY(id)
) ENGINE = InnoDB DEFAULT CHARSET = utf8;
CREATE TABLE accounts(
id INT NOT NULL AUTO_INCREMENT,
account_name VARCHAR(45) NOT NULL,
user_id INT UNIQUE,
PRIMARY KEY(id),
FOREIGN KEY(user_id) REFERENCES users(id)
) ENGINE = InnoDB DEFAULT CHARSET = utf8;
In this example, I define the foreign key in accounts pointing to the primary key in users.
And then I make foreign key UNIQUE, so there can't be two identical users in accounts.
To join tables I would use this query:
SELECT * FROM users JOIN accounts ON users.id = accounts.user_id;
Second solution:
DROP DATABASE IF EXISTS test;
CREATE DATABASE test CHARSET = utf8 COLLATE = utf8_general_ci;
USE test;
CREATE TABLE users(
id INT NOT NULL AUTO_INCREMENT,
user_name VARCHAR(45) NOT NULL,
PRIMARY KEY(id)
) ENGINE = InnoDB DEFAULT CHARSET = utf8;
CREATE TABLE accounts(
id INT NOT NULL AUTO_INCREMENT,
account_name VARCHAR(45) NOT NULL,
PRIMARY KEY(id),
FOREIGN KEY(id) REFERENCES users(id)
) ENGINE = InnoDB DEFAULT CHARSET = utf8;
In this example, I create a foreign key that points from the primary key to a primary key in another table. Since Primary Keys are UNIQUE by default, this makes this relation One to One.
To join tables I can use this:
SELECT * FROM users JOIN accounts ON users.id = accounts.id;
Now the questions:
What is the best way to create One to One relation in MySQL?
Are there any other solutions other than these two?
I'm using MySQL Workbench, and when I design One To One relation in EER diagram and let MySQL Workbench produce SQL code, I get One to Many relation :S That's what's confusing me :S
And if I import any of these solutions into MySQL Workbench EER diagram, it recognizes relations as One to Many :S That's also confusing.
So, what would be the best way to define One to One relation in MySQL DDL. And what options are there to achieve this?
Since Primary Keys are UNIQUE by default, this makes this relation One to One.
No, that makes the relation "one to zero or one". Is that what you actually need?
If yes, then then your "second solution" is better:
it's simpler,
takes less storage1 (and therefore makes cache "larger")
hes less indexes to maintain2, which benefits data manipulation,
and (since you are using InnoDB) naturally clusters the data, so users that are close together will have their accounts stored close together as well, which may benefit cache locality and certain kinds of range scans.
BTW, you'll need to make accounts.id an ordinary integer (not auto-increment) for this to work.
If no, see below...
What is the best way to create One to One relation in MySQL?
Well, "best" is an overloaded word, but the "standard" solution would be the same as in any other database: put both entities (user and account in your case) in the same physical table.
Are there any other solutions other than these two?
Theoretically, you could make circular FKs between the two PKs, but that would require deferred constraints to resolve the chicken-and-egg problem, which are unfortunately not supported under MySQL.
And if I import any of these solutions into MySQL Workbench EER diagram, it recognizes relations as One to Many :S Thats also confusing.
I don't have much practical experience with that particular modeling tool, but I'm guessing that's because it is "one to many" where "many" side was capped at 1 by making it unique. Please remember that "many" doesn't mean "1 or many", it means "0 or many", so the "capped" version really means "0 or 1".
1 Not just in the storage expense for the additional field, but for the secondary index as well. And since you are using InnoDB which always clusters tables, beware that secondary indexes are even more expensive in clustered tables than they are in heap-based tables.
2 InnoDB requires indexes on foreign keys.
Your first approach creates two candidate keys in the accounts table: id and user_id.
I therefore suggest the second approach i.e. using the foreign key as the primary key. This:
uses one less column
allows you to uniquely identify each row
allows you to match account with user
What about the following approach
Create Table user
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(45) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Create Table account with a unique index on user_id and account_id with a foreign key relation to user/account and a primary key on user_id and account_id
CREATE TABLE `account` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(45) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Create Table user2account
CREATE TABLE `user2account` (
`user_id` int(11) NOT NULL,
`account_id` int(11) NOT NULL,
PRIMARY KEY (`user_id`,`account_id`),
UNIQUE KEY `FK_account_idx` (`account_id`),
UNIQUE KEY `FK_user_idx` (`user_id`),
CONSTRAINT `FK_account` FOREIGN KEY (`account_id`) REFERENCES `account` (`id`),
CONSTRAINT `FK_user` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
While this solution has the largest footprint in the database, there are some advantages.
Putting the FK_Key in either the user table or the account table is something that I expect to be a one to many releation (user has many accounts ...)
While this user2account approach is mainly used to define a many to many relationship, adding a UNIQUE constraint on user_id and on account_id will prevent creating something else than a one to one relation.
The main advantage I see in this solution is that you can divide the work in different code layers or departements in a company
Department A is responsible for creating users, this is possible even without write permission to accounts table
Departement B is responsible for creating accounts, this is possible even without write permission to user table
Departement C is responsible for creating the mapping, this is possible even without write permission to user or account table
Once Departement C has created a mapping neither the user nor the account can be deleted by departement A or B without asking departement C to delete the mapping first.
In this system, we store products, images of products (there can be many image for a product), and a default image for a product. The database:
CREATE TABLE `products` (
`ID` int(10) unsigned NOT NULL AUTO_INCREMENT,
`NAME` varchar(255) NOT NULL,
`DESCRIPTION` text NOT NULL,
`ENABLED` tinyint(1) NOT NULL DEFAULT '1',
`DATEADDED` datetime NOT NULL,
`DEFAULT_PICTURE_ID` int(10) unsigned DEFAULT NULL,
PRIMARY KEY (`ID`),
KEY `Index_2` (`DATEADDED`),
KEY `FK_products_1` (`DEFAULT_PICTURE_ID`),
CONSTRAINT `FK_products_1` FOREIGN KEY (`DEFAULT_PICTURE_ID`) REFERENCES `products_pictures` (`ID`) ON DELETE SET NULL ON UPDATE SET NULL
) ENGINE=InnoDB AUTO_INCREMENT=30 DEFAULT CHARSET=utf8;
CREATE TABLE `products_pictures` (
`ID` int(10) unsigned NOT NULL AUTO_INCREMENT,
`IMG_PATH` varchar(255) NOT NULL,
`PRODUCT_ID` int(10) unsigned NOT NULL,
PRIMARY KEY (`ID`),
KEY `FK_products_pictures_1` (`PRODUCT_ID`),
CONSTRAINT `FK_products_pictures_1` FOREIGN KEY (`PRODUCT_ID`) REFERENCES `products` (`ID`) ON DELETE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=20 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;
as you can see, products_pictures.PRODUCT_ID -> products.ID and products.DEFAULT_PICTURE_ID -> products_pictures.ID, so a cycle reference. Is it OK?
No, it's not OK. Circular references between tables are messy. See this (decade old) article: SQL By Design: The Circular Reference
Some DBMS can handle these, and with special care, but MySQL will have issues.
Option 1
As your design, to make one of the two FKs nullable. This allows you to solve the chicken-and-egg problem (which table should I first Insert into?).
There is a problem though with your code. It will allow a product to have a default picture where that picture will be referencing another product!
To disallow such an error, your FK constraint should be:
CONSTRAINT FK_products_1
FOREIGN KEY (id, default_picture_id)
REFERENCES products_pictures (product_id, id)
ON DELETE RESTRICT --- the SET NULL options would
ON UPDATE RESTRICT --- lead to other issues
This will require a UNIQUE constraint/index in table products_pictures on (product_id, id) for the above FK to be defined and work properly.
Option 2
Another approach is to remove the Default_Picture_ID column form the product table and add an IsDefault BIT column in the picture table. The problem with this solution is how to allow only one picture per product to have that bit on and all others to have it off. In SQL-Server (and I think in Postgres) this can be done with a partial index:
CREATE UNIQUE INDEX is_DefaultPicture
ON products_pictures (Product_ID)
WHERE IsDefault = 1 ;
But MySQL has no such feature.
Option 3
This approach, allows you to even have both FK columns defined as NOT NULL is to use deferrable constraints. This works in PostgreSQL and I think in Oracle. Check this question and the answer by #Erwin: Complex foreign key constraint in SQLAlchemy (the All key columns NOT NULL Part).
Constraints in MySQL cannot be deferrable.
Option 4
The approach (which I find cleanest) is to remove the Default_Picture_ID column and add another table. No circular path in the FK constraints and all FK columns will be NOT NULL with this solution:
product_default_picture
----------------------
product_id NOT NULL
default_picture_id NOT NULL
PRIMARY KEY (product_id)
FOREIGN KEY (product_id, default_picture_id)
REFERENCES products_pictures (product_id, id)
This will also require a UNIQUE constraint/index in table products_pictures on (product_id, id) as in solution 1.
To summarize, with MySQL you have two options:
option 1 (a nullable FK column) with the correction above to enforce integrity correctly
option 4 (no nullable FK columns)
The only issue you're going to encounter is when you do inserts.
Which one do you insert first?
With this, you will have to do something like:
Insert product with null default picture
Insert picture(s) with the newly created product ID
Update the product to set the default picture to one that you just inserted.
Again, deleting will not be fun.
this is just suggestion but if possible create one join table between this table might be helpfull to tracking
product_productcat_join
------------------------
ID(PK)
ProductID(FK)- product table primary key
PictureID(FK) - category table primary key
In the other table you can just hold that field without the foreign key constraint.
it is useful in some cases where you want to process with the smaller table but connect to the bigger table with the result of the process.
For example if you add a product_location table which holds the country, district, city, address and longitude and latitude information. There might be a case that you want to show the product within a circle on the map.
John what your doing isnt anything bad but using PK-FK actually helps with normalizing your data by removing redundant repeating data. Which has some fantastic advantages from
Improved data integrity owing to the elimination of duplicate storage locations for the same data
Reduced locking contention and improved multiple-user concurrency
Smaller files
that is not a cyclic ref, that is pk-fk
So I've inherited some django.
The mySQL table is simple enough where parent is NOT a FK relationship just the "Parent" id:
CREATE TABLE `Child` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`parent` int(10) unsigned NOT NULL,
`name` varchar(255) NOT NULL,
UNIQUE KEY `id` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=24;
But then the originator did this..
class Child(models.Model):
"""Project Child information"""
id = models.AutoField(primary_key=True)
parent = models.ForeignKey(Parent)
name = models.CharField(max_length=255)
class Meta:
managed = False
Admittedly I am NOT a SQL Jockey but I know that a "real" Foreign Key Relationship looks similar to this notice CONSTRAINT...
CREATE TABLE `Child` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`parent_id` int(11) NOT NULL,
`name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`id`),
KEY `child_63f17a16` (`parent_id`),
CONSTRAINT `parent_id_refs_id_34923e1e` FOREIGN KEY (`parent_id`) REFERENCES `Parent` (`id`)
) ENGINE=InnoDB;
What I want to know is the following:
What problems could I expect to see by this "trickery".
While this appears to work - is it recommended or advised.
Would we be advised to modify the SQL to add in the constraint?
Thanks so much!
Not having an actual constraint might lead to broken references, invalid parents and other sorts of data inconsistencies. I am not a Django expert but I would venture a guess that in most cases Django will still handle the relations fine unless you purposefully add some invalid records.
Normally, if your RDBMS supports foreign key constraints, there is absolutely no reason not to use them, and it could potentially be considered a design flaw to ignore them.
You should consider adding the key constraints. Not only do they give your DBMS a good idea of how to optimize the queries, they also ensure consistency in your data. I am pretty sure Django has a setting somewhere that will automatically generate the SQL to add the key constraints when you run manage.py syncdb
For more information about why you should prefer foreign keys, you should read the MySQL Foreign Key Documentation
Most interestingly:
InnoDB requires indexes on foreign keys and referenced keys so that foreign key checks can be fast and not require a table scan. In the referencing table, there must be an index where the foreign key columns are listed as the first columns in the same order. Such an index is created on the referencing table automatically if it does not exist. (This is in contrast to some older versions, in which indexes had to be created explicitly or the creation of foreign key constraints would fail.) index_name, if given, is used as described previously.
Its supposed to be faster ... since you mysql doesn't check the constraint before adding a row in the child table.
But with the foreign key, it would make your life easier since you can use the on update and on delete.
I'd go with the constraint.