Nullable foreign keys - good or bad practice? - mysql

Let's assume I have 2 tables: foo and bar.
In third table I want to store different kind of data, however every row will have a reference to either foo OR bar.
Is it correct if I create 2 NULLable foreign keys - foo_id and bar_id - in the third table, or is it againts database design principles?
Basically, I thought all the time that foreign keys need to ALWAYS have a "parent", so if I try to e.g. INSERT a row with no primary key matched (or, in this case, with a foreign key set to NULL), I will get an error. Nullable FK-s are new to me, and they feel a bit off.
Also, what are the alternatives? Is it better to create separate tables storing single reference? Isn't this creating redundancy?
Linking tables?
Help.

A nullable FK is "okay". You will still get an error when you try to insert a non-existing parent key (it is just NULL that is allowed now).
The alternative is two link tables, one for foo and one for bar.
Things to consider:
Link tables allow for 1:N. If you don't want that, you can enforce it by primary key on the link table. That is not necessary for the id column solution (they are always 1:N).
You can avoid columns with mostly NULL values using link tables. In your case, though, it seems that you have NULL for exactly half the values. Probably does not qualify as "mostly". This becomes more interesting with more than two parent tables.
You may want to enforce the constraint that exactly one of your two columns is NULL. This can be done with the id column version using a check constraint. It cannot be done with link tables (unless you use triggers maybe).

it is depend on the business logic of the program. if the foreign key field must has a value , it is bad to set it null-able .
for example .
a book table has category_id field which the value is reference from bookCategory table.
each record in book table must has category . if for some reason you set it as nullable . this will cause some record in book table with category_id is null.
the problem will show up in report. the following 2 query will return different total_book
select count(*) as total_book from book;
select
count(*) as total_book
from
book
inner join bookCategory
on book.category_id = category.id
my advice is don't use null-able unless you expect value and no-value . alot of complex system that sometime have value different from one report and another , usually is cause by this.

Related

Storing key value where key repeats and using primary keys

I am in a situation where i have to store key -> value pairs in a table which signifies users who have voted certain products.
UserId ProductID
1 2345
1 1786
6 657
2 1254
1 2187
As you can see that userId keeps on repeating and so can productId. I wanted to know what can be the best way to represent this data. Also is there a necessity of using primary key in here. I've searched a lot but am not able to find the exact specification about my problem. Any help would be appreciated. Thank you.
If you want to enforce that a given user can vote for a given product at most once, create a unique constraint over both columns:
ALTER TABLE mytable ADD UNIQUE INDEX (UserId, ProductID);
Although you can use these two columns together as a key, your app code is often simpler if you define a separate, typically auto increment, key column, but the decision to do this depends on which app code language/library you use.
If you have any tables that hold a foreign key reference to this table, and you intend to use referential integrity, those tables and the SQL used to define the relationship will also be simpler if you create a separate key column - you just end up carting multiple columns around instead of just one.

Mysql composite primary key

I want to have a lookup table that links two of the same things to eachother. Say I have a 'Person' table and I want to lookup the relationship between two people. I'll have column one of the lookup be 'PersonId1' and column two be 'PersonId2' and the third column be 'Relationship'. Since the relationship goes both ways I don't need to have duplicate records with the PlayerId's switched. Is there any way to make mysql enforce uniqueness on PlayerId1 and PlayerId2 combinations regardless of which order they're in?
Does that make sense?
Short answer: No.
Longer answer: You could set up a trigger to swap the order of the two person ids if the second were smaller than the first, then write them, and use a composite key.
Even longer answer: Not all interpersonal relationships are commutative (not all relationships go both ways). What about the "Employee" or "Mother" relationships? Even the "Friend" relationship, which is presumably peer-to-peer, might be better represented if you had separate rows saying A is B's Friend and B is A's Friend. So maybe you want a three-field composite key on this table.
You mean you want to have a unique row record from PersonID1 and PersonID2 Column (regardless of the Relationship column)? If that so, you may use the Composite key (Multi column key).
Here's an example:
CREATE TABLE Person (
PersonId1 INT,
PersonId2 INT,
PRIMARY KEY (PersonId1, PersonId2)
)
+1 for composite pk. To prevent duplicate combinations, an extra varchar column with for example personid1+personid2 with a unique constraint on it may be a solution...
See also: person data model example

MySQL table - designing efficient table

I'm designing a db table that will save a list of user's favorited food items.
I created favorite table with the following schema
id, user_id, food_id
user_id and food_id will be foreign key linking to another table.
Im just wondering if this is efficient and scalable cause if user has multiple favorite things then it would need multiple rows of data.
i.e. user has 5 favorited food items, then it will consist of five rows to save the list for that user.
Is this efficient? and scalable? Whats the best way to optimize this schema?
thnx in advance!!!
tldr; This is called a "join table" and is the correct and scalable approach to model M-M relationships in a relational database. (Depending upon the constraints used it can also model 1-M/1-1 relationships in a "no NULL FK" schema.)
However, I contend that the id column should be omitted here so that the table is only user_id, food_id. The PK will be (user_id, food_id) in this case.
Unlike other tables, where surrogate (aka auto-increment) PKs are sometimes argued for, a surrogate PK generally only adds clutter in a join table as it has a very natural compound PK.
While the PK itself is compound in this case, each "joined" table only relates back by part of the PK. Depending upon queries performed it might also be beneficial to add covering indices on food_id or (food_id, user_id).
Eliminate Surrogate Key: Unless you have a specific reason for the surrogate key id, exclude it from the table.
Fine-tune Indexing: A this point, you just have a composite primary key that is the combination of the two foreign keys. In which order should the PK fields be?
If your application(s) predominantly execute queries such as: "for given user, give me foods", then PK should be {user_id, food_id}.
If the predominant query is "for given food, give me users", then the PK should be {food_id, user_id}.
If both query "directions" are common, add a UNIQUE INDEX that has the same fields as PK, but in opposite directions. So you'll have PK on {user_id, food_id} and index on {food_id, user_id}.
Note that InnoDB tables are clustered, which eliminates (in this case "unnecessary") table heap. Yet, the secondary index discussed above will not cause a double-lookup (since it fully covers the query), nor have a hidden overhead of PK fields (since it indexes the same fields as PK, just in opposite order).
For more on designing a junction table, take a look at this post.
To my opinion, you can optimize your table in the following ways:
As a relation table with 2 foreighkeys you don't have to use "id" field.
use "innodb" engine to your table
name your relation table "user_2_food", which will make it more clear.
try to use datatype as small as possible, i.e. "smallint" is better than "int", and don't forget "UNSIGNED" attribute.
Creating the below three Tables will result in an efficient design.
users : userId, username, userdesc
foods : foodId, foodname, fooddesc
userfoodmapping : ufid, userid, foodid, rowstate
The significance of rowstate is, if the user in future doesn't like that food, its state will become -1
You have 2 options in my opnion:
Get rid of the ID field, but in that case, make both your other keys (combined) your primary key
Keep your ID key as the primary key for your table.
In either case, I think this is a proper approach. Once you get into a problem of inefficiency, then you will look at probably how to load part of the table or any other technique. This would do for now.

composite foreign key referencing one or more columns

I have a table (tableA) that joins 3 other tables with primary keys 'vehicle','engine','transmission' I would like to be able to assign parts to one or more of these eg 'only this vehicle' or 'only this vehicle with this engine' or 'any vehicle with this engine'
My plan is to have a parts table (tableB) with also primary keys 'vehicle','engine','transmission' and I'd like to be able to insert for example:
4844, null, null to assign a part to only 'vehicle' or
4844, 240, null to assign a part to 'only this vehicle with this engine'.
Is there some way I can enforce integrity at the database level so that.
the fields that are filled in in tableB must reference fields in tableA.
at least one of the fields must be filled in.
the option exists not to insert data into them all?
read up on data model patterns. there are several good books (hay, fowler, silverston, blaha) and hay covers this kinda stuff well
if you want to do this sort of stuff, please use a real database like postgres that has check constraints. for your questions 2 and 3, this is easily solved with checks.

mysql many to many relationship

Been reading the tutorial How to handle a Many-to-Many relationship with PHP and MySQL .
In this question I refer to the "Database schema" section which states the following rules:
This new table must be constructed to
allow the following:
* It must have a column which links back to table 'A'.
* It must have a column which links back to table 'B'.
* It must allow no more than one row to exist for any combination of rows from table 'A' and table 'B'.
* It must have a primary key.
Now it's crystal clear so far.
The only problem I'm having is with the 3rd rule ("It must allow no more than one row to exist for any combination").
I want this to be applied as well, but it doesn't seem to work this way.
On my test instance of mysql (5.XX) I'm able to add two rows which reflect the same relationship!
For example, if I make this relation (by adding a row):
A to B
It also allows me to make this relation as well:
B to A
So the question is two questions actually:
1) How do I enfore the 3rd rule which will not allow to do the above? Have only one unique relation regardless of the combination.
2) When I'll want to search for all the relations of 'A', how would the SQL query look like?
Note #1: Basically my final goal is to create a "friendship" system, and as far as I understand the solution is a many-to-many table. Suggest otherwise if possible.
Note #2: The users table is on a different database from the relations (call it friendships) table. Therefore I cannot use foreign keys.
For the first question:
Create a unique constraint on both
columns
Make sure you always sort the columns. So if your table has the
colummns a and b than make sure
that a is less than or equal to
b
For the second question:
SELECT
*
FROM
many_to_many_table
WHERE
a = A or b = A
It sounds like you want a composite primary key.
CREATE TABLE relationship (
A_id INTEGER UNSIGNED NOT NULL,
B_id INTEGER UNSIGNED NOT NULL,
PRIMARY KEY (A_id, B_id)
);
This is how you setup a table so that there can only ever be one row that defines tables A and B as related. It works because a primary key has to be unique in a table so therefore the database will allow only one row with any specific pair of values. You can create composite keys that aren't a primary key and they don't have to be unique (but you can create a unique non-primary key, composite or not), but your specification requested a primary key, so that's what I suggested.
You can, of course, add other columns to store information about this specific relationship.
Ok WoLpH was faster, I basically agree (note that you have to create a single constraint on both columns at the same time!). And just to explain why you collide with the rules you mentioned: Typically, A and B are different tables. So the typical example for n:m relations would allow entries (1,0) and (0,1) because they'd be refering to different pairs. Having table A=table B is a different situation (you use A and B as users, but in the example they're tables).