Question:
Is there a way to make the foreign ID point to something more generic than one specific table?
Details:
Often I run into the situation where I have several tables which have nothing to do with each other, but still need a common table (in below examples engine is innodb)
CREATE TABLE IF NOT EXISTS movies
(
id INT NOT NULL auto_increment,
name VARCHAR(100) NOT NULL ,
PRIMARY KEY(id)
);
CREATE TABLE IF NOT EXISTS books
(
id INT NOT NULL auto_increment,
name VARCHAR(100) NOT NULL ,
PRIMARY KEY(id)
);
CREATE TABLE IF NOT EXISTS songs
(
id INT NOT NULL auto_increment,
name VARCHAR(100) NOT NULL ,
PRIMARY KEY(id)
);
CREATE TABLE IF NOT EXISTS news_papers
(
id INT NOT NULL auto_increment,
name VARCHAR(100) NOT NULL ,
PRIMARY KEY(id)
);
CREATE TABLE IF NOT EXISTS scrolls
(
id INT NOT NULL auto_increment,
name VARCHAR(100) NOT NULL ,
PRIMARY KEY(id)
);
CREATE TABLE IF NOT EXISTS sumarian_wheat_tablets
(
id INT NOT NULL auto_increment,
name VARCHAR(100) NOT NULL ,
PRIMARY KEY(id)
);
Now I want to keep a record of every time each is viewed like so
CREATE TABLE IF NOT EXISTS movie_history
(
id INT NOT NULL auto_increment,
foreign_id INT NOT NULL ,
view_date TIMESTAMP DEFAULT now(),
FOREIGN KEY (foreign_id) REFERENCES movies ( id ),
PRIMARY KEY(id)
);
CREATE TABLE IF NOT EXISTS book_history
(
id INT NOT NULL auto_increment,
foreign_id INT NOT NULL ,
view_date TIMESTAMP DEFAULT now(),
FOREIGN KEY (foreign_id) REFERENCES books ( id ),
PRIMARY KEY(id)
);
CREATE TABLE IF NOT EXISTS song_history
(
id INT NOT NULL auto_increment,
foreign_id INT NOT NULL ,
view_date TIMESTAMP DEFAULT now(),
FOREIGN KEY (foreign_id) REFERENCES songs ( id ),
PRIMARY KEY(id)
);
CREATE TABLE IF NOT EXISTS news_paper_history
(
id INT NOT NULL auto_increment,
foreign_id INT NOT NULL ,
view_date TIMESTAMP DEFAULT now(),
FOREIGN KEY (foreign_id) REFERENCES news_papers ( id ),
PRIMARY KEY(id)
);
CREATE TABLE IF NOT EXISTS scroll_history
(
id INT NOT NULL auto_increment,
foreign_id INT NOT NULL ,
view_date TIMESTAMP DEFAULT now(),
FOREIGN KEY (foreign_id) REFERENCES scrolls ( id ),
PRIMARY KEY(id)
);
CREATE TABLE IF NOT EXISTS sumarian_wheat_tablet_history
(
id INT NOT NULL auto_increment,
foreign_id INT NOT NULL ,
view_date TIMESTAMP DEFAULT now(),
FOREIGN KEY (foreign_id) REFERENCES sumarian_wheat_tablets ( id ),
PRIMARY KEY(id)
);
Is there a more correct way to handle such situations without making n new tables? I realize that I can make one history table and copy it over with CREATE TABLE...LIKE... but that still requires making n new tables, plus I have to go in and ALTER the foreign_id.
My first thought is just dump the fk reference and have one history table:
CREATE TABLE history(
base_table VARCHAR,
base_table_id INT,
view_date TIMESTAMP DEFAULT now()
);
But I assume you want the fk to maintain the integrity (question: is this really necessary, or can this be worked around?). I guess you could accomplish this by creating a table of "pks in use". For example:
create a table "keys" with columns id (autoincrement) and base_table_name
create a table "movies", where id is both pk and also a fk to "keys.id" (but not an autoincrement column)
add a "before insert" trigger to "movies" which inserts a record into "keys" returning the generated id to be used as the id for the "movie" record
create a history table with a fk to "keys"
create a "delete" trigger on "movies" which also removes the record from "keys" if you want the integrity maintained, or cascading deletes, etc
So the generated "id" is shared across many tables. There is a school of thought that suggests using a primary key unique across all relations within the database (an "enterprise key"), so it is not unprecedented. Instead of using sequences or autogenerated columns, sometimes a GUID or UUID is used.
This replaces extra history tables with triggers on each base table, which might not be a great thing, depending on your environment. I haven't done this myself, just throwing some thoughts out there, so take it for what its worth.
This depends on the record that you're keeping. If you just want to know hits, add one field to each table that is incremented each time your 'hit' criteria is met (ie, there is a read from a webpage). If you want to hold more information:
CREATE TABLE IF NOT EXISTS view_history
(
id INT NOT NULL,
table VARCHAR NOT NULL,
//other relevant stats to a given view, such as ip and so on.
)
The id and table form a composite key as to what table it refers to.
I don't think there is a way to specify more than one table on a single foreign key.
If you define a single history table, you cannot enforce referential integrity using a single foreign key. You could enforce it programmaticaly as explained here
This describes how to do it for other storage engines that do not support FKs, but could be used as a guide to implement what you need. It suggests creating triggers that will enforce same validations a foreign key would.
Other approach:
CREATE TABLE IF NOT EXISTS history
(
id INT NOT NULL auto_increment,
movie_id INT,
book_id INT,
song_id INT,
news_paper_id INT,
view_date TIMESTAMP DEFAULT now(),
FOREIGN KEY (movie_id) REFERENCES movie ( id ),
FOREIGN KEY (book_id) REFERENCES book ( id ),
FOREIGN KEY (song_id) REFERENCES song ( id ),
FOREIGN KEY (news_paper_id) REFERENCES news_paper ( id ),
PRIMARY KEY(id)
);
Related
I have below data and using mysql. Person_name is unique and TelephoneNumbers are unique per person.
Person_name1=TelephoneNumber1, TelephoneNumber2, TelephoneNumber3...
Person_name2=TelephoneNumber4, TelephoneNumber5, TelephoneNumber6...
Option 1. Create 1:Many master and child table.
CREATE TABLE Person (
personName varchar(50) NOT NULL,
id int NOT NULL AUTO_INCREMENT,
PRIMARY KEY (id),
UNIQUE KEY personName (personName)
);
CREATE TABLE Telephone (
telephoneNumber int,
mappingId int,
PRIMARY KEY (telephoneNumber),
foreign key(mappingId) references Person(id)
);
Option 2. Create one table with personName, telephoneNumber as Composite Key.
CREATE TABLE
Person_Telephone (
personName varchar(50) NOT NULL,
telephoneNumber int NOT NULL,
PRIMARY KEY(personName, telephoneNumber)
);
Option 1 is it over complicating creating two tables for just two fields?
Option 2 looks perfect and will there be any issues if Option 2 chosen over Option 1?
The option 2 gives you duplicate persons that you must control in every query.
The best is have the entities separate, it's a classic 1-N relation
Since users can have multiple phone numbers, I think 2 tables would be the best solution.
CREATE TABLE person (
PRIMARY KEY (id) AUTO_INCREMENT,
person_name VARCHAR(45) NOT NULL,
);
CREATE TABLE phone_number (
PRIMARY KEY (id) AUTO_INCREMENT,
phone_number VARCHAR(11) NOT NULL,
FOREIGN KEY (person_id) REFERENCES person(id)
)
Now you can simply JOIN the tables like this:
SELECT
t1.id,
t1.person_name,
t2.phone_number
FROM person t1
LEFT JOIN phone_number t2
ON (t1.id = t2.person_id);
I have a table A which has an Auto incrementing Primary key.
I have a table B with a field as foreign key referencing to those Primary key values.
I want to add a record in Table A and Table B simultaneously,(note I'm not defining these auto incrementing fields.When I execute my codes the records in table A are created but table B fails to do so. I understand that I cannot reference a foreign key as a auto incrementing null value but is there any alternative to what I'm trying to achieve???
My code for creating tables are:
Table A
Create TABLE IF NOT EXISTS Friends(
friend_id Int PRIMARY KEY AUTO_INCREMENT,
friend_email Varchar(50) NOT NULL UNIQUE,
Password Varchar(20) NOT NULL,
Profile_Name Varchar(30) NOT NULL,
Date_Started DATE NOT NULL,
Num_Of_Friends INT UNSIGNED )
Table B
Create TABLE IF NOT EXISTS myFriends(
friend_id1 Int AUTO_INCREMENT ,
friend_id2 Int NOT NULL,
FOREIGN KEY(friend_id1) REFERENCES Friends(friend_id))
The queries for adding into these tables
For adding into Table A
INSERT INTO `Friends` (`friend_id`, `friend_email`, `Password`, `Profile_Name`, `Date_Started`, `Num_Of_Friends`) VALUES (NULL, '$Email', '$password1', '$Profile_Name', CURRENT_DATE(), '0')";
For adding into Table B
INSERT INTO `myFriends` (`friend_id1`,`friend_id2`) VALUES (NULL,0)";
I was trying to create multiple tables (some of them referncing the other tables) at once.
I think I matched data types and set primary/foreign keys correctly. But I can only see an error
of 'You cannot add foreign key constraint'. I thought the referenced tables might be created on the first before other tables refernce it, so I reversed the order and the result was the same.
Lastly I tried creating and executing only the referenced tables first(item_type), then referencing tables(item) later.. and... it worked!
However, I wonder if those codes can be executed at once.
Here is code below..
(just two tables are shown to make it simple..)
CREATE TABLE item (
i_id SMALLINT unsigned NOT NULL AUTO_INCREMENT,
i_name VARCHAR(30) NOT NULL,
t_id SMALLINT unsigned NOT NULL,
PRIMARY KEY (i_id),
FOREIGN KEY (t_id) REFERENCES item_type(t_id)
) ENGINE=INNODB;
CREATE TABLE item_type (
t_id SMALLINT unsigned NOT NULL AUTO_INCREMENT,
t_name VARCHAR(20) NOT NULL,
PRIMARY KEY(t_id)
);
You can't define a foreign key to a table that doesn't exist, so doing the CREATE TABLE operations in the order above is not going to work. If you create the item_type table first, then the item table with the foreign key to item_type, it should work.
Database engines execute sql code in batches, so one statement that crate table is one batch, but in you example, first batch references second batch which is not executed yet, so change order of batches and it will be working.
CREATE TABLE item_type (
t_id SMALLINT unsigned NOT NULL AUTO_INCREMENT,
t_name VARCHAR(20) NOT NULL,
PRIMARY KEY(t_id)
);
CREATE TABLE item (
i_id SMALLINT unsigned NOT NULL AUTO_INCREMENT,
i_name VARCHAR(30) NOT NULL,
t_id SMALLINT unsigned NOT NULL,
PRIMARY KEY (i_id),
FOREIGN KEY (t_id) REFERENCES item_type(t_id)
) ENGINE=INNODB;
In MySQL I have these 3 tables :
CREATE TABLE IF NOT EXISTS Seasons
(
season_id INT NOT NULL AUTO_INCREMENT,
start_date DATE,
end_date DATE,
club_num INT,
desc TEXT,
PRIMARY KEY(season_id)
);
ALTER TABLE Seasons AUTO_INCREMENT=10000;
CREATE TABLE IF NOT EXISTS Clubs
(
club_id INT NOT NULL AUTO_INCREMENT,
club_name VARCHAR(70),
PRIMARY KEY(club_id)
);
ALTER TABLE Clubs AUTO_INCREMENT=100000;
CREATE TABLE IF NOT EXISTS ClubsCloths
(
season_id INT NOT NULL,
club_id INT NOT NULL,
first_shirt VARCHAR(50),
second_shirt VARCHAR(50),
PRIMARY KEY(season_id,club_id),
FOREIGN KEY (season_id) REFERENCES Seasons(season_id),
FOREIGN KEY (club_id) REFERENCES Clubs(club_id)
);
In the last one I have 2 foreign keys that reference to first and second table. Now I want to know is it wisely to have 2 foreign key in a one table ?
thanks
It is normal. The ClubsCloths table is used to support many-to-many relationship between Seasons and Clubs.
It's perfectly normal to have several foreign keys to different tables (or the same table, doesn't matter).
I am trying to add a self relation in an existing Innodb table here is table structure
Table person
person_id int (10) primary key not null auto increment,
parent_id int (10) NULL default null,
name varchar(30)
When I use this command
ALTER TABLE `person` ADD FOREIGN KEY ( `parent_id` ) REFERENCES `person` (`person_id`) ON DELETE RESTRICT ON UPDATE RESTRICT ;
I get the error data type mismatch. I think this could be due to null values in parent_id. Is there any way to skip this check?
Thanks
person_id and parent_id need to be the exact same data type. For example, if person_id is INT UNSIGNED and parent_id is INT, then you can't create the foreign key.
Run this command and compare the data types of the two columns:
SHOW CREATE TABLE `person`\G