Single FK referes to PK from multiple tables - mysql

I have Table A, Table B, and Table C. Table C has a column which should be a FK for a PK. The PK could be either Table A's id or Table B's id. What is the best practice to design such tables?
I am thinking of creating 2 columns in Table C, where the first one would determine either Table A or Table B, and the second one represents the id of either Table A or Table B (depending on the first column). Is it the right way to do it?

Using SQL UNION to Combine Two ResultSet with Different Joins
See: http://www.sqlfiddle.com/#!8/3519e/4
I have made an example with 3 tables:
CAR
TRUCK
DRIVER_ASSIGNED
You can create one query to join A with C, another to join B with C, and join the two result sets using UNION. For example:
(select * from `A`,
`C`
where `A`.ID = `C`.`ID` and
`C`.`Type` like 'A')
UNION
(select * from `B`,
`C`
where `B`.ID = `C`.`ID` and
`C`.`Type` like 'B')
In Relation to Classes and Sub-Classes
It seems to be that TABLE A and TABLE B are sub-types of another type/class. So, e.g. TABLE A may be cars, and TABLE B, may be trucks, but they extend vehicles.
In this case I think you need a fourth table, TABLE PARENT, that will combine the common fields of both A and B. C will use the primary key of TABLE PARENT as the foreign K.
TABLE A and B will contain both as foreign keys but maybe also as primary keys the primary key of table PARENT.
So, using my analogy with vehicles let us assume:
TABLE A = CARS
TABLE B = TRUCKS
TABLE C = ASSIGNED_DRIVERS
TABLE PARENT = VEHICLES
TABLE VEHICLES - PARENT of A and B
-------------
ID (PK)
HORSE POWER
LICENSE PLATE
etc...
TABLE CARS -
-------------
ID (PK)
VEHICLE_ID (FK linking to VEHICLES.ID)
NUMBER_SEATS
etc...
TABLE TRUCKS -
-------------
ID (PK)
VEHICLE_ID (FK linking to VEHICLES.ID)
HIGHT (meters)
MAXIMUM_STORAGE_WEIGHT
etc...
TABLE DRIVERS_ID -
-------------
VEHICLE_ID (FK linking to VEHICLES.ID)
DRIVER_OD
START_DATE
END_DATE
etc...
So, the following method would save you from this problem and also be more semantically correct.
You can also check online documentation such as:
http://www.dssbooks.com/web/Files/LookInside/Web-DSS-Chapter-03.pdf (page 55) to see the theory between classes and subclasses.

I am thinking of creating 2 columns in Table C, where the first one would determine either Table A or Table B, and the second one represents the id of either Table A or Table B (depending on the first column). Is it the right way to do it?
No. You would be preventing the DBMS from enforcing the foreign key. You'd have to enforce the FK through triggers or the application code, which is more prone to errors and potentially less performant.
Either make two FK fields (one for Table A, other for Table B) and use a CHECK1 to ensure only one of them is not NULL, or use inheritance.
More info in this post.
1 Unfortunately, MySQL parses but doesn't enforce CHECK constraints, so you'll need to use a trigger instead. This limitation doesn't exist in other DBMSes.

Related

auto_increment index depends other table

I have two tables:
Friends :
id name
1 jhon
2 peter
Teammates:
id name
3 juan
i am looking for a way two auto increment the id of the second table (teammates) according to the first table ( Friends ).
When I add a new register to Teammates it never match with an id of Friends
I think this is not good practice. If you do so, you are introducing an implicit functional dependency between both tables outside of the declared design. If you want to it anyway, you can use a trigger to asign the value instead of making the column autoincrement.
I would suggest to have a table for all people with the real autoincrement id, then you can use several approaches:
i) Make your two actual tables take id values as foreign keys of this new table, with the corresponding integrity constraint.
ii) Simply create 2 views of the table: One for friends, other for teammates.
Table_Friends: (id, name, role)
View_Friends: Select id, name from table_Friends where role = value_for_friend_role
View_Mates: Select id, name from table_Friends where role = value_for_teammate_role

Traverse list of related tables

I have a peculiar situation. In my MySQL database, I have around 90 odd tables and most of the tables have been indexed (We are using INNODB). Some of the tables are having a link like this:
A -> B -> C->D->E
Is there a way where-in I can find the list of all the sub-child tables when i have only table A and E to work with? I have do a dynamic query builder mechanism, and for that purpose I list the users with the list of tables, and in a given situation like above, need to get the required information from just table "A" and "E" alone, without the tables "B","C" and "D" being selected by the search user.
The tables are linked in normal manner..each table is linked to another via a proper foreign key constraint.
Eg.
Table A (Transaction)
Id, Trxn-Date, Amount
Table B (Transaction Header)
Id, Agent_Id (FK to Agent_Profile), Upd_Time, Trnx_Hdr_ID (FK to Table A)
Table C (Agent_Profile)
Id, Prof_ID (FK to Profile)
Table D (Profile)
ID, Pers_Info_Id (FK to Personal_Info)
Table E (Personal_Info)
Id, Firstname, Lastname
User selects Trxn_Date, FirstName, LastName.
How can I retrieve the sub-linked table information, when the selected tables(in this case), happens to be only Transaction and Personal_Info.
Seems like you could consolidate a few of those tables into 2-3 tables without losing anything useful. Tables C D and E just contain columns to point to other tables.
If changed, a query with trxn_date and first/last name would be a ton easier.

having trouble with foreign key queries

I'm new to SQL and I'm having a hard time figuring out how to execute queries with foreign keys on MySQL Workbench.
In my example, I have three tables: people, places, and people_places.
In people, the primary key is people_id and there's a column called name with someone's name.
In places, the primary key is places_id and there's a column called placename with the name of a place.
People_places is a junction table with three columns: idpeople_places (primary key), people_id (foreign key), and places_id (foreign key). So this table relates a person to a place using their numerical IDs from the other two tables.
Say I want the names of everyone associated with place #3. So the people_places table has those associations by number, and the people table relates those numbers back to the actual names I want.
How would I execute that query?
Try this to find all the people names who are associated with place id 3.
SELECT p.name
FROM people as p
INNER JOIN people_places as pp on pp.people_id = p.people_id
WHERE pp.places_id = 3
OK, so you need to "stitch" all three tables together, yeah?
Something like this:
select people.name
from people -- 1. I like to start with the table(s) that I want data from, and
, people_places -- 2. then the "joining" table(s), and
, places -- 3. finally the table(s) used "just" for filtering.
where people.people_id = people_places.people_id -- join table 1 to table 2
and people_places.place_id = places.place_id -- join table 2 to table 3
and places.name = "BERMUDA" -- restrict rows in table 3
I'm sure you can do the rest.
Cheers. Keith.

mysql group by set id

MYSQL Database:
I have a table of data that I need to put into two tables.The one table contains persons and an animal. Every record is a person and an animal. I am chaning this to a multi table database and want to group by the persons name, and then give the group an id (like a customer id) and then in my other table pass the customer ID to the idcustomer to join the two tables. To simplfy this i dont mind if these newly created ids are in the single table with new column names. I can after the fact export them out and import them.
The question really is, how can I create a group by customer name, give that customer and ID and then use that same id in a column to do the join.
To describe the scheme:
I have taken over a project. The database as of now is one table. In this one table is:
persons name, address, childsname, description of child
What would like it to be at least to start with is:
id_person, person name, childsname, childparent, description of child.
The id of the person and the childsparent should be the same. When I break the table down, and put the child information in the child table, the child will have a parent id.
I still plan on having two tables. But I can export and create the tables, my problem is assiging the parent id to the child column with the current data.
An example of a couple rows would be:
person_name, address, childsname, description
mark twain, 23 st., Buckweat, short smart kid
mark twain, 23 st., Daniel, short kinda smart
Instead i would like to have 2 tables, one for the parents and the other table is their children.
The way this database was setup, if one person has more than one child, there is another row that lists all of the same information.
What I have planned is to have multiple tables with a join.
The original database has no index, or unique identifier.
What I want to do is loop through the records, since there is no unique id, if the customer name is identical, meaning they are listed twice, then they must have more than one child.
In that case, i want to be able to go through the database and assign a id for the parents. and also ad another colum called parentid, which will be the child table.
To create the table you need you can use a temporary table - to which you will insert all parent names and give them IDs. Then you can update the existing table:
CREATE TABLE name_to_id (
`id` INT(11) AUTO_INCREMENT,
`name` VARCHAR(256),
PRIMARY KEY (`id`));
INSERT INTO name_to_id (name)
SELECT DISTINCT name FROM my_table;
ALTER TABLE my_table
ADD COLUMN id INT(11) FIRST,
ADD COLUMN parent_id INT(11) AFTER childsname;
UPDATE my_table t
JOIN name_to_id n ON t.name = n.name
SET t.id = n.id, t.parent_id = n.id;
To create the parents and children separate tables you can use:
CREATE TABLE parents AS
SELECT id, name, address FROM my_table
GROUP BY id;
CREATE TABLE children AS
SELECT childsname, parent_id, description
FROM my_table;
You would probably want to ALTER those tables later to add a primary keys and other needed indexes.

Database design - primary key naming conventions

I am interested to know what people think about (AND WHY) the following 3 different conventions for naming database table primary keys in MySQL?
-Example 1-
Table name: User,
Primary key column name: user_id
-Example 2-
Table name: User,
Primary key column name: id
-Example 3-
Table name: User,
Primary key column name: pk_user_id
Just want to hear ideas and perhaps learn something in the process :)
Thanks.
I would go with option 2. To me, "id" itself seems sufficient enough.
Since the table is User so the column "id" within "user" indicates that it is the identification criteria for User.
However, i must add that naming conventions are all about consistency.
There is usually no right / wrong as long as there is a consistent pattern and it is applied across the application, thats probably the more important factor in how effective the naming conventions will be and how far they go towards making the application easier to understand and hence maintain.
I always prefer the option in example 1, in which the table name is (redundantly) used in the column name. This is because I prefer to see ON user.user_id = history.user_id than ON user.id = history.user_id in JOINs.
However, the weight of opinion on this issue generally seems to run against me here on Stackoverflow, where most people prefer example 2.
Incidentally, I prefer UserID to user_id as a column naming convention. I don't like typing underscores, and the use of the underscore as the common SQL single-character-match character can sometimes be a little confusing.
ID is the worst PK name you can have in my opinion. TablenameID works much better for reporting so you don't have to alias a bunch of columns named the same thing when doing complex reporting queries.
It is my personal belief that columns should only be named the same thing if they mean the same thing. The customer ID does not mean the same thing as the orderid and thus they should conceptually have different names. WHen you have many joins and a complex data structure, it is easier to maintain as well when the pk and fk have the same name. It is harder to spot an error in a join when you have ID columns. For instance suppose you joined to four tables all of which have an ID column. In the last join you accidentally used the alias for the first table and not the third one. If you used OrderID, CustomerID etc. instead of ID, you would get a syntax error because the first table doesn't contain that column. If you use ID it would happily join incorrectly.
I tend to go with the first option, user_id.
If you go with id, you usually end up with a need to alias excessively in your queries.
If you go with more_complicated_id, then you either must abbreviate, or you run out of room, and you get tired of typing such long column names.
2 cents.
I agree with #InSane and like just Id. And here's why:
If you have a table called User, and a column dealing with the user's name, do you call it UserName or just Name? The "User" seems redundant. If you have a table called Customer, and a column called Address, do you call the column CustomerAddress?
Though I have also seen where you would use UserId, and then if you have a table with a foreign key to User, the column would also be UserId. This allows for the consistency in naming, but IMO, doesn't buy you that much.
In response to Tomas' answer, there will still be ambiguity assuming that the PK for the comment table is also named id.
In response to the question, Example 1 gets my vote. [table name]_id would actually remove the ambiguity.
Instead of
SELECT u.id AS user_id, c.id AS comment_id FROM user u JOIN comment c ON u.id=c.user_id
I could simply write
SELECT user_id, comment_id FROM user u JOIN comment c ON u.user_id=c.user_id
There's nothing ambiguous about using the same ID name in both WHERE and ON. It actually adds clarity IMHO.
I've always appreciated Justinsomnia's take on database naming conventions. Give it a read: http://justinsomnia.org/2003/04/essential-database-naming-conventions-and-style/
I would suggest example 2. That way there is no ambiguity between foreign keys and primary keys, as there is in example 1. You can do for instance
SELECT * FROM user, comment WHERE user.id = comment.user_id
which is clear and concise.
The third example is redundant in a design where all id's are used as primary keys.
OK so forget example 3 - it's just plain silly, so it's between 1 and 2.
the id for PK school of thought (2)
drop table if exists customer;
create table customer
(
id int unsigned not null auto_increment primary key, -- my names are id, cid, cusid, custid ????
name varchar(255) not null
)engine=innodb;
insert into customer (name) values ('cust1'),('cust2');
drop table if exists orders;
create table orders
(
id int unsigned not null auto_increment primary key, -- my names are id, oid, ordid
cid int unsigned not null -- hmmm what shall i call this ?
)engine=innodb;
insert into orders (cid) values (1),(2),(1),(1),(2);
-- so if i do a simple give me all of the customer orders query we get the following output
select
c.id,
o.id
from
customer c
inner join orders o on c.id = o.cid;
id id1 -- big fan of column names like id1, id2, id3 : they are sooo descriptive
== ===
1 1
2 2
1 3
1 4
2 5
-- so now i have to alias my columns like so:
select
c.id as cid, -- shall i call it cid or custid, customer_id whatever ??
o.id as oid
from
customer c
inner join orders o on c.id = o.cid; -- cid here but id in customer - where is my consistency ?
cid oid
== ===
1 1
2 2
1 3
1 4
2 5
the tablename_id prefix for PK/FK name school of thought (1)
(feel free to use an abbreviated form of tablename i.e cust_id instead of customer_id)
drop table if exists customer;
create table customer
(
cust_id int unsigned not null auto_increment primary key, -- pk
name varchar(255) not null
)engine=innodb;
insert into customer (name) values ('cust1'),('cust2');
drop table if exists orders;
create table orders
(
order_id int unsigned not null auto_increment primary key,
cust_id int unsigned not null
)engine=innodb;
insert into orders (cust_id) values (1),(2),(1),(1),(2);
select
c.cust_id,
o.order_id
from
customer c
inner join orders o on c.cust_id = o.cust_id; -- ahhhh, cust_id is cust_id is cust_id :)
cust_id order_id
======= ========
1 1
2 2
1 3
1 4
2 5
so you see the tablename_ prefix or abbreviated tablename_prefix method is ofc the most
consistent and easily the best convention.
I don't disagree with what most of the answers note - just be consistent. However, I just wanted to add that one benefit of the redundant approach with user_id allows for use of the USING syntactic sugar. If it weren't for this factor, I think I'd personally opt to avoid the redundancy.
For example,
SELECT *
FROM user
INNER JOIN subscription ON user.id = subscription.user_id
vs
SELECT *
FROM user
INNER JOIN subscription USING(user_id)
It's not a crazy significant difference, but I find it helpful.