1

I'm working on a small pizza delivery website and I ran in to a small problem with the MySQL tables.

I found this on Stackoverflow: https://stackoverflow.com/a/10322293/80907

It mentions the following:

  • Both tables have to use the INNODB engine.
  • "In the referencing table, there must be an index where the foreign key columns are listed as the first columns in the same order. "

The first isn't really a problem, but the second rule is where I'm scratching my head.

This is a website where you can order pizzas, so I'm saving all data on the users and their order in the database.

Here's a screenshot of what I'm about to write out: enter image description here

So I'll have a "Users" table and an "Orders" table. The Users will have to have a one-to-many relationship to Orders. Simply put, the order is identified by the user who created it. So it's one-to-many, identifying.

This means that the "Orders" table will have a foreign key, such as "Users_id".

The problem arises when you have to make a table for the many-to-many relationship between the Pizzas table and the Orders table.

This table, let's call it "Order_Details"(MySQL Workbench automatically called it "Orders_has_Pizzas") must reference both "Orders" and "Pizzas".

Now, since "Orders" already references the Users table in an identifying relationship, that's part of the primary key of "Orders".

And let's get that rule out once again:

  • "In the referencing table, there must be an index where the foreign key columns are listed as the first columns in the same order. "

What this means is that you must reference the entire primary key. If I delete the "Order_Users_id" key, I'll get a 1005 error upon trying to create the database.

My question is simply: is there a way around this? Because as it is right now, I have that User id mentioned in 3 different tables.

Or, am I not understanding it properly and is it indeed necessary to do this for the sake of not having to query different tables for that data?

EDIT: People seem to disagree with me on the relation between Users and Orders being identifying.

I don't see how an individual order can be identified without knowing the id of the user. After the order is made, someone is going to have to deliver the pizza, meaning they'll need to know where to deliver it. That data is in the Users table. Therefor, the Users_id is part of the identity of a single order.

That's how I see it anyway. If I'm wrong, please explain why.

EDIT 2: Thanks to a_horse_with_no_name for clarifying the concept of "identity" in terms of databases, I see the error of my logic now. Info can be found in the comments.

Community
  • 1
  • 1
KdgDev
  • 14,299
  • 46
  • 120
  • 156
  • 2
    `Users_id` should not by part of the primary key in `Orders`. – SLaks Jan 20 '14 at 14:04
  • @SLaks: It's an identifying relationship. Which means it must be part of the primary key. Or not? – KdgDev Jan 20 '14 at 14:05
  • 1
    @slaks is right, KdgDev how do identify an Order? By `order id`, it isn't? Then only `order id` is necessary as PK identifier. – dani herrera Jan 20 '14 at 14:07
  • @danihp That sounds wrong to me. The Order is identified not on its own but by the user who created the order. Thus, I see it as an identifying relationship. A question to always ask is: can the child-table exist without the parent table. And the answer for this case is that it can not. An Order can not come in to existence without a user having created it. – KdgDev Jan 20 '14 at 14:11
  • 2
    Each order has a unique identifier (presumably `id`) therefor the `user_id` does not need to be part of the PK of the `orders` table. It would only be necessary if `orders.id` started at 1 for each user, e.g. id=1, user_id=1, id=2, user_id=1, id=1, user_id=2, ... –  Jan 20 '14 at 14:14
  • 2
    Regarding your last edit: if `orders.id` is *unique* then it's enough to identify the order. The user to which that specific order should be delivered is an *attribute* of the order, not an identifying key. The fact that you only want to reference `orders.id` in your `orders_has_pizza` table proves that `orders.id` is unique in your system. –  Jan 20 '14 at 14:34
  • @a_horse_with_no_name So, does that mean that the concept of identity, in terms of sql databases, isn't a question of "does the entry in this table exist because of the entry in another table?", but more an issue of being able to pick out a single entry in the table and be certain it's unique? Being able to uniquely identify it. – KdgDev Jan 20 '14 at 14:44
  • 2
    Yes. A primary key is there to ***uniquely** identify a row in a table* –  Jan 20 '14 at 14:53
  • Keep your table names plural, but your columns singular. So pizza_id (and not pizzas_id). Also i would keep **everything** lower case. Especially with mysql, it *can* cause problems with case sensitive file systems. – Roger Jan 20 '14 at 15:09
  • Also, there is no real reason to denormalise (i.e. repeat the data) `user_id` in `orders_has_pizzas` because you get the user via `orders`. You would only denormalise if you were experiencing performance issues and needed to speed up certain queries. So `orders_has_pizzas` (or better still `pizza_orders`) only need consist of `order_id` and `pizza_id`. – Raad Jan 20 '14 at 15:17
  • @Rogier Workbench did that automatically. I just didn't edit it before exporting it as a PNG image. But you are correct, that is how it should be done. – KdgDev Jan 20 '14 at 15:19

1 Answers1

2

To answer your original question, no, InnoDB foreign key constraints are not required to reference the entire primary key of the referenced table.

In other words, both of the following work in InnoDB:

mysql> ALTER TABLE Orders_has_Pizzas ADD FOREIGN KEY (Orders_id) 
    REFERENCES Orders (id);
Query OK, 0 rows affected (0.63 sec)

mysql> ALTER TABLE Orders_has_Pizzas ADD FOREIGN KEY (Orders_id,Orders_Users_id) 
    REFERENCES Orders (id, Users_id);
Query OK, 0 rows affected (0.02 sec)

In fact, InnoDB allows a foreign key to reference any indexed column, even if it's not part of the primary key:

mysql> CREATE TABLE Foo (fooid INT PRIMARY KEY, nonunique INT, KEY (nonunique));
Query OK, 0 rows affected (0.05 sec)

mysql> CREATE TABLE Bar (barid INT PRIMARY KEY, foo_nonid INT, FOREIGN KEY (foo_nonid) 
    REFERENCES Foo(nonunique));
Query OK, 0 rows affected (0.06 sec)

However, this is not standard SQL and I don't recommend doing it. It means that a row in Bar could reference more than one row in the parent table Foo. Which means a JOIN between these two on the foreign-primary key relationship could unexpectedly create a sort of mini-Cartesian product.

In the Orders table, it's possible for either column of a compound primary key to contain duplicates. Which means a given row in Orders_has_Pizzas could theoretically reference multiple Orders.

As for the question about an identifying relationship, I would agree that Orders has an identifying relationship with respect to Users. That is, it makes no sense for an order to exist with no referenced user.

But in a table where we use an auto-incrementing mechanism to generate unique id's, it seems redundant and unnecessary to add the extra column to the PK. Why would we need Orders to contain the users id? The id alone is guaranteed unique and therefore sufficient to uniquely address each row.

I would say that's a practical choice, whereas the theory would guide us to create the compound primary key in Orders.

It becomes more clear in a many-to-many table like your Orders_has_Pizzas. This table has an identifying relationship with both Orders and Pizzas. The primary key consists of two foreign keys, one referencing Orders and the other referencing Pizzas. There's no need for an auto-increment PK at all.

Some people add a superfluous auto-increment id for many-to-many tables, for the sake of a convention that every table has to have a single-column automatically-generated primary key. But there's no theoretical or practical reason to do this.

CREATE TABLE Orders_has_Pizzas (
  id INT AUTO_INCREMENT PRIMARY KEY, -- what is this column for?
  Orders_id INT,
  Orders_Users_id INT,
  Pizzas_id INT,
);
Bill Karwin
  • 538,548
  • 86
  • 673
  • 828
  • Thanks for the explanation. Before I went home, I showed the thread to my instructor and she said my original belief about what the concept of identity is, in terms of databases, was wrong, on a theoretical level. After reading the comments here, I conclude that relationships are identifying if the field is required to obtain a unique row. So, Users and Orders don't have an identifying relationship. I don't need a user_id in order to obtain a unique row in the Orders table. But, having the user_id there will allow me to do a JOIN based on it, which is a nice thing to have available. Correct? – KdgDev Jan 20 '14 at 20:05
  • 1
    From the reading I've done, if the child table's FK is part of its own PK, then it's an identifying relationship, otherwise not. Though that seems like kind of a circular definition, or putting the implementation before the logical relationship it's trying to model. There could be cases (like yours) where the FK is not part of the PK, but the Orders entity really must not omit its user reference. In that case, you can make user_id NOT NULL. – Bill Karwin Jan 20 '14 at 20:45