Does the foreign key slow down the join query? - mysql

I have two databases test & test2. Both have the same tables(employees & salaries) and both have the same records. test2 database uses a foreign key and test database doesn't.
test structure
test.employees
+--------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------+-------------+------+-----+---------+-------+
| emp_id | int(11) | NO | PRI | NULL | |
| name | varchar(30) | YES | | NULL | |
+--------+-------------+------+-----+---------+-------+
test.salaries
+--------+---------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------+---------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| salary | int(11) | YES | | NULL | |
| emp_id | int(11) | NO | | NULL | |
+--------+---------+------+-----+---------+----------------+
test2 structure
test2.employees
+--------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------+-------------+------+-----+---------+-------+
| emp_id | int(11) | NO | PRI | NULL | |
| name | varchar(30) | YES | | NULL | |
+--------+-------------+------+-----+---------+-------+
test2.salaries
+--------+---------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------+---------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| salary | int(11) | YES | | NULL | |
| emp_id | int(11) | NO | MUL | NULL | |
+--------+---------+------+-----+---------+----------------+
I run the same join query on both databases
select * from employees inner join salaries on employees.emp_id=salaries.emp_id;
This is the output i get from test database which doesn't contain a foreign key
2844047 rows in set (3.25 sec)
This is the output i get from test2 database which contains a foreign key
2844047 rows in set (17.21 sec)
So does the foreign key slow down the join query?

Your empirical evidence suggests that in at least one case it does. So, if we believe your numbers, the answer is clearly "yes" -- and I assume you have ruled out other potential causes such as locks on the table or resource competition (actually the difference is pretty big). I presume that you want to know why.
In most databases, declaring a foreign key is about relational integrity. It would have no effect on the optimization of queries. The join conditions in the query would redundantly cover the same information.
However, MySQL does a bit more when a foreign key is declared. A foreign key declaration automatically creates an index on the columns being used. This is not standard behavior -- I'm not even sure if any other database does this.
Normally, an index would benefit performance. In this case, the optimizer has more choices on how to approach the query. For whatever reason, it is using a substandard execution plan.
You should be able to look at the explain plans and see a difference. The issue is that the optimizer has chosen the wrong plan. I would say that this is uncommon and should not dissuade you from using proper foreign key declarations in your databases.

Related

Look for a particular constraint in mysql

One of my friend created a table in mySQl, how can I check whether he applied a particular constraint on a column name or not.
In my case, I want to verify whether he applied a CHECK constraint on the column CHG_HOUR or not? The query DESC JOB does not help me with the 'CHECK' constraint.
mysql> desc JOB;
+--------------+---------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------------+---------------+------+-----+---------+-------+
| JOB_CODE | char(3) | NO | PRI | NULL | |
| JOB_CLASS | char(30) | YES | | NA | |
| CHG_HOUR | decimal(12,2) | YES | | NULL | |
| OVERTIME_CHG | int(11) | NO | | NULL | |
+--------------+---------------+------+-----+---------+-------+
4 rows in set (0.00 sec)
Short answer: he didn't.
MySQL doesn't support check constraints until 8.0.16 which, at the time of writing, does not seen to be even released.

MySql; proper way to update fields of a table

When trying to describe a table I get a table with missing information (see first table); what would be the best way to update that table in a way that looks like the second one? My SQL background is not so strong, so I'd love to hear ideas on how to do this
First table (how it is actually)
+-----------+------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-----------+------------------+------+-----+---------+-------+
| config_id | int(10) unsigned | NO | | 0 | |
| scope | varchar(8) | NO | | default | |
| scope_id | int(11) | NO | | 0 | |
| path | varchar(255) | NO | | general | |
| value | text | YES | | NULL | |
+-----------+------------------+------+-----+---------+-------+
Second table (how it should be)
+-----------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------+------------------+------+-----+---------+----------------+
| config_id | int(10) unsigned | NO | PRI | NULL | auto_increment |
| scope | varchar(8) | NO | MUL | default | |
| scope_id | int(11) | NO | | 0 | |
| path | varchar(255) | NO | | general | |
| value | text | YES | | NULL | |
+-----------+------------------+------+-----+---------+----------------+
I see following differences between the two tables :
column config_id should be auto_increment and also primary key of the table
a (non-unique) index is missing on column scope
The following statement should change the table as required :
ALTER TABLE mytable
MODIFY COLUMN config_id INT auto_increment,
ADD PRIMARY KEY (config_id),
ADD INDEX idx_scope(scope)
;
PS : DEFAULT NULL does not make sense for config_id : since it is a primary key, your RDBMS will never allow it to be set to NULL.
Please note that this answer is based on the information you provided only. Running this statement will not necessarily make the table structures strictly equivalent, since there could be other differences that can not be seen in the representation that you provided. You can get a complete DDL statement describing the table using the SHOW CREATE TABLE mytable syntax.

MySQL foreign key constraints through more tables

I find myself stack to this problem. I've got the following 3 tables, which I can't modify (it'd be so nice):
Person
+------------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+------------+-------------+------+-----+---------+-------+
| SSN | varchar(50) | NO | PRI | NULL | |
| name | varchar(50) | YES | UNI | NULL | |
| birthday | date | YES | | NULL | |
+------------+-------------+------+-----+---------+-------+
Employee
+------------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+------------+-------------+------+-----+---------+-------+
| SSN | varchar(50) | NO | PRI | NULL | |
| department | varchar(50) | YES | | NULL | |
| salary | varchar(50) | YES | | NULL | |
+------------+-------------+------+-----+---------+-------+
Employer
+------------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+------------+-------------+------+-----+---------+-------+
| name | varchar(50) | NO | PRI | NULL | |
| department | varchar(50) | YES | | NULL | |
| salary | varchar(50) | YES | | NULL | |
+------------+-------------+------+-----+---------+-------+
Contract
+----------------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+----------------+-------------+------+-----+---------+-------+
| employer_name | varchar(50) | NO | PRI | NULL | |
| employee_name | varchar(50) | YES | PRI | NULL | |
+----------------+-------------+------+-----+---------+-------+
I know that:
employee(sin) is foreign key to person(sin),
employer(name) is foreign key to person(name),
contract(employer_name) is a foreign key to employer(name)
And I should insert another foreign key for contract(employee_name), though employee table has only the SSN. Is there a way to reference the foreign key to person.name, passing through employee table, something like
CONSTRAINTS FOREIGN KEY contract(employee_name) REFERENCES TO person(name) WHERE person(sin)=employee(sin);
?
Thank you very much for any help!
An SQL FK constraint says its referencing column list subrow values, if all non-NULL, have to appear as subrow values for its referenced column list, which must be declared UNIQUE/PK in a base table.
Your constraint is not an SQL FK constraint.
If we could use a query in the place of the referenced base table name in an SQL FK constraint then the constraint you would want would be:
Contract(person_name) REFERENCES
(SELECT name FROM Person p JOIN Employee e ON p.name = e.name)(name)
(It can be shown that name is UNIQUE in that table.) But we can't.
If MySQL supported CREATE ASSERTION then you could CHECK that every Contract person_name was IN (SELECT name FROM Person p JOIN Employee e ON p.name = e.name). But it doesn't.
So this is an example of a constraint that, if you can't redesign your tables, you would enforce by appropriate triggers when the tables involved change.
Is there a way to reference the foreign key to person.name, passing through employee table, something like
If you want to figure out situations like this then you have to forego the cop-outs of using precise terms like "foreign key" just because you are somehow reminded of the things they refer to but that aren't there, or using the poetic "passing through", or using the vague "something like". You have to actually write out the conditions that you want your tables to satisfy.

I'm using one table in a mysql database. I want duplicate entries in some fields

I'm setting this up in phpmyadmin. It's a simple table listing a collection of films. One column records the year the film was made. Another the decade it was made. A third, the genre.
I'm getting this message from phpmyadmin:
1062 - Duplicate entry '30' for key 'decade'
This is on the second insert. But, this will also happen for genre and eventually for year.
I thought at first that making these fields varchar instead of int would solve the problem, but I guess I just don't have the colums set up correctly.
What do I need to do?
My Schema:
MariaDB [movies]> desc films;
+------------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+------------+--------------+------+-----+---------+-------+
| id | int(11) | NO | PRI | NULL | |
| alpha-name | varchar(50) | NO | UNI | NULL | |
| page-link | varchar(100) | NO | UNI | NULL | |
| sm-pic | varchar(100) | NO | UNI | NULL | |
| year | varchar(4) | NO | UNI | NULL | |
| decade | varchar(2) | NO | UNI | NULL | |
| genre | varchar(10) | NO | UNI | NULL | |
+------------+--------------+------+-----+---------+-------+
I see now that the keys are designated UNI, but where in phpmyadmin do I select it so that I can allow duplicates?
I can't see where I can thank people so I'm doing it here...
dropping the indexes on these fields does seem to have worked. Thanks for the specific code.
try:
Alter films drop index <column_name>

In mysql can I have a composite primary key composed of an auto increment and another field? Also, please critique my "mysql partitioning" logic

I am experimenting with mysql partitioning ( splitting the table up to help it scale better ), and I am having a problem with the keys on the table. First, I am using a python's threaded comments module... here is the schema
+-----------------+------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-----------------+------------------+------+-----+---------+-------+
| content_type_id | int(11) | NO | MUL | NULL | |
| object_id | int(10) unsigned | NO | | NULL | |
| parent_id | int(11) | YES | MUL | NULL | |
| user_id | int(11) | NO | MUL | NULL | |
| date_submitted | datetime | NO | | NULL | |
| date_modified | datetime | NO | | NULL | |
| date_approved | datetime | YES | | NULL | |
| comment | longtext | NO | | NULL | |
| markup | int(11) | YES | | NULL | |
| is_public | tinyint(1) | NO | | NULL | |
| is_approved | tinyint(1) | NO | | NULL | |
| ip_address | char(15) | YES | | NULL | |
| id | int(11) | YES | | NULL | |
+-----------------+------------------+------+-----+---------+-------+
Note, I have modified this database by dropping the id col (primary by default), and re adding it.
Essentially, I want to have id AND content_type_id as my primary keys. I also want id to auto increment. Is this possible.
Second question. Since I am just learning about mysql partitioning, I am wondering if my partitioning logic is sound. There are 67 different content_types, and some (maybe all) of those content types allow comments to be made on them. My plan is to partition based on the type of object that is being commented on. For instance, the images will be commented on a lot, so I put any content type pertaining to images into one partition, and another content type that can be commented on is "blog entries", so there is a separate partition for that, and so on and so on. This will allow me to spread these partitions possibly to dedicated machines as the load grows. How is my understanding of this concept so far?
Thanks so much!
Since id will be auto incremented, it can be the primary key all by itself. Adding content_type to the primary key would not gain you anything in regards to the uniqueness of the key.
If you want to add an index for faster performance to the 2 columns, then add an alternate unique index to the table with the 2 columns instead of trying to add them both to the primary key. However, be aware that enforing uniqueness on the 2 columns would be a waste since id is already guaranteed to be unique by itself, so a regular index would make more sense if needed.