Understanding / mySQL aka tricking ForeignKey relationships in Django - mysql

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.

Related

MySQL Relationships & Joins

In a MySQL database where there are relationships between tables and the primary key of one table is stored as a foreign key in a second table, is there still a need to perform a join?
If there is, what is the point on declaring the relationship? I'd take a stab in the dark and say it's something to do with the indexing or related tables can find related records much faster? I've tried Googleing this, but can't seem to find much. I'm sure there is loads out there on this, but I don't know the keywords to search for.
Here is an example of table 1 and table 2:
------------------- Table 1 ----------------------
CREATE TABLE IF NOT EXISTS `db_hint`.`user` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`fb_id` INT NOT NULL,
`last_logged_in` DATETIME NULL,
`permissions` INT UNSIGNED NOT NULL,
PRIMARY KEY (`id`),
INDEX `permissions_id_idx` (`permissions` ASC),
CONSTRAINT `permissions_id`
FOREIGN KEY (`permissions`)
REFERENCES `db_hint`.`permissions` (`id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB;
----------------- Table 2 ----------------------
CREATE TABLE IF NOT EXISTS `db_hint`.`user_stat` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`user_id` INT UNSIGNED NOT NULL,
PRIMARY KEY (`id`),
INDEX `user_id_idx3` (`user_id` ASC),
CONSTRAINT `user_id`
FOREIGN KEY (`user_id`)
REFERENCES `db_hint`.`user` (`id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB;
When performing any kind of join, does the InnoDB engine use the relationship in any way? Thanks.
The point of declaring the foreign key is to enforce data consistency.
You will still need the JOIN in order to get desired data.
In MySQL foreign keys will improve performance, but don't expect much comparable to indexes.
To do a query involving two tables, you need JOIN ... ON ... to say how they are related. FOREIGN KEYs are not involved in a SELECT and has zero impact on performance of SELECT. You do not "have to have" FOREIGN KEYs to perform SELECTs.
A FOREIGN KEY is used during INSERTs (and other writes) to verify that a subsequent JOIN will actually find something in the other table. It is an overhead during the write -- the INSERT actively checks (via an index) that the referenced table has the indicated row.
FOREIGN KEYs may also do a cascading operation. For example, a DELETE can cause another DELETE to happen. I prefer to such take control in my application code.

What does the line " KEY `idx_pid` (`person_id`), " mean?

I am new to mysql and am working on an online server (MYSQL version 5.1.69) and i have the following table
CREATE TABLE `person_info` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`person_id` int(11) NOT NULL,
`info_type_id` int(11) NOT NULL,
`info` text NOT NULL,
`note` text,
PRIMARY KEY (`id`),
KEY `idx_pid` (`person_id`),
KEY `person_info_info_type_id_exists` (`info_type_id`)
)
Can someone explain to me what " KEY idx_pid (person_id)," does?
KEY, in MySQL, is an alias for INDEX; you can see this in the pseudo grammar in the CREATE TABLE documentation:
[INDEX|KEY] [index_name] (index_col_name,...)
It represents the definition of an index on a table, and nothing more. Here,
KEY `idx_pid` (`person_id`),
…creates an index named "idx_pid" on the column "person_id". This could have also been written as,
INDEX `idx_pid` (`person_id`),
However, MySQL's SHOW CREATE TABLE command (and other commands) will prefer KEY. It is an unfortunate choice for a keyword here, as it has nothing to do with a “key¹” in the relational databases sense of the word.
¹A key, in relational database theory, is a set of columns that uniquely identify a row.
It means you're creating an index named "idx_pid" on the person_info.person_id column.
This adds an index named idx_pid on the person_id column which speeds up queries using the persond_id as condition.
You can read up on MySQL indexes here.

Mysql table composite keys

I have this table definition.
CREATE TABLE `friendship` (
`fID` bigint(20) NOT NULL AUTO_INCREMENT,
`Part1` bigint(20) NOT NULL DEFAULT '0',
`Part2` bigint(20) NOT NULL DEFAULT '0',
`AddedBy` bigint(11) NOT NULL DEFAULT '0',
`Accepted` tinyint(4) NOT NULL DEFAULT '0',
PRIMARY KEY (`fID`),
KEY `Part1` (`Part1`,`Part2`),
KEY `Part1_2` (`Part1`),
KEY `Part2` (`Part2`),
KEY `AddedBy` (`AddedBy`),
KEY `Accepted` (`Accepted`)
) ENGINE=MyISAM AUTO_INCREMENT=2 DEFAULT CHARSET=latin1;
Can someone explain me why key "Part1" contains Part1 and Part2, and what are good/bad side of this?
Is it better to use FK on Part1/Part2?
Is there any performance difference using this on MyISAM and InnoDB table?
You have a bad naming convention, for one thing. I would expect the keys to be named:
KEY `Part1_2` (`Part1`,`Part2`),
KEY `Part1` (`Part1`),
In any case, the key that has only Part1 is not needed. The composite key (Part1,Part2) can be used wherever Part1 would be used.
Can someone explain me why key "Part1" contains Part1 and Part2, and
what are good/bad side of this?
As far as I can tell, it's just bad naming. As far as performance and data integrity go, the name here isn't important. It might be troublesome if you later need to drop it by name.
Like Gordon Linoff said, the key named "Part1_2" in your question can be dropped.
Is it better to use FK on Part1/Part2?
There are no unique constraints on either of those columns. MySQL will still let you reference them with a foreign key, but MySQL docs tell you not to do it. (Allowing that was a mistake; it's easier to document it than to fix it.)
Key in MySQL doesn't mean what it means in other SQL dbms. In this CREATE TABLE statement, KEY is a synonym for INDEX; it's not a synonym for UNIQUE or a shorthand for PRIMARY KEY. The only column in this table that can be safely referenced by a foreign key constraint is fID.
Is there any performance difference using this on MyISAM and InnoDB
table?
InnoDB supports row-level locking, transactions, and foreign key constraints. MyISAM doesn't. I'd worry about those differences before I'd think about performance differences. It doesn't matter how fast it runs if your database gives you the wrong answers.

In SQL, is it OK for two tables to refer to each other?

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

database design: User will submit a howto, each howto will have one or more steps associated with, each step can have random pictures associated with

I am trying to design a database but I need some help with the relationships. Am i getting the table design right?
Here is the database idea..
User will submit a howto, each howto will have one or more steps associated with(a one to many). each step can have random pictures associated with(another one to many). so I am thinking of this:
CREATE TABLE `HowtoStepImage`
`id` int(10) unsigned NOT NULL auto_increment,
`user_id` int(10) unsigned NOT NULL,
`howto_id` varchar(25) NOT NULL,
`step_id` varchar(25) NOT NULL,
`img_id` int(10) unsigned NOT NULL,
PRIMARY KEY (`id`),
KEY `hsi_k_1` (`howto_id`),
CONSTRAINT `hsi_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`),
CONSTRAINT `hsi_ibfk_2` FOREIGN KEY (`step_id`) REFERENCES `HowtoStep` (`step_id`),
CONSTRAINT `hsi_ibfk_3` FOREIGN KEY (`img_id`) REFERENCES `StepImage` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
table HowtoStep
step_id, title, content, created
primary key (step_id)
table StepImage
img_id, filename, created
CREATE TABLE `UserHowtoComment` (
`id` int(10) unsigned NOT NULL auto_increment,
`howto_id` varchar(25) NOT NULL,
`user_id` int(10) unsigned NOT NULL,
`comment` varchar(500) NOT NULL,
`created` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `UserHowtoComment_ibfk_1` (`howto_id`),
KEY `UserHowtoComment_ibfk_2` (`user_id`),
CONSTRAINT `UserHowtoComment_ibfk_1` FOREIGN KEY (`howto_id`) REFERENCES `HowtoStepImage` (`howto_id`),
CONSTRAINT `UserHowtoComment_ibfk_2` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
however, I am getting error when creating the table, I am sure it is due to my database design. here is what mysql>SHOW ENGINE INNODB STATUS; shows:
091217 9:59:59 Error in foreign key constraint of table UserhowtoComment:
FOREIGN KEY (`howto_id`) REFERENCES `howtoStepImage` (`howto_id`),
CONSTRAINT `UserHowtoComment_ibfk_2` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8:
Cannot find an index in the referenced table where the
referenced columns appear as the first columns, or column types
in the table and the referenced table do not match for constraint.
Note that the internal storage type of ENUM and SET changed in
tables created with >= InnoDB-4.1.12, and such columns in old tables
cannot be referenced by such columns in new tables.
See http://dev.mysql.com/doc/refman/5.0/en/innodb-foreign-key-constraints.html
for correct foreign key definition.
the howto_id is a key(index) in UserHowtoComment though. I am not sure if that is the exact problem here..
Make 3 tables: one for HowTo, one for HowToStep, one for HowToStepImage.
Give each table a clearly defined key, e.g. a number or a string.
Then let the 'child' table refer to the key of the parent table.
Make sure that the columns have clear names as well.
TABLE HowTo
COLUMNS HowToId(key)
TABLE HowToStep
COLUMNS HowToStepId(key), HowToId
TABLE HowToStepImage
COLUMNS HowToStepImageId(key), HowToStepId
your query is really messy e.g. step_id varchar(25) needs to be an int.
why dont you just use a gui programm or maybe the good old phpMyAdmin, so you can learn the from the Querys they are creating, phpMyAdmin also has a advanced feature call "Designer" to create constraints.
If I read this correctly, your HowToComment id is a foreign key to HowtoStepImage. Does every comment have to have an image? Seems like a chicken and the egg issue. It seems, from your problem description, that an image links to a comment, not the other way around.
you're falling prey to the misleading terminology in MySQL. in the relational model, key is (necessarily) distinct. in the MySQL-speak, it's just an index. you need either PRIMARY KEY or UNIQUE KEY.
edit to add explicitly what is implied above: foreign keys must point to a key in the relational sense.