Below I have two tables
users and users_profiles
Both are innoDB and collation: utf8_general_ci
They are as follows:
users
CREATE TABLE `users` (
`uid` int(11) NOT NULL AUTO_INCREMENT,
`status` char(10) NOT NULL,
`username` varchar(15) NOT NULL,
`email` varchar(50) NOT NULL,
`password` char(32) NOT NULL,
`reg_date` int(11) NOT NULL,
`ip` varchar(39) DEFAULT NULL,
PRIMARY KEY (`uid`),
UNIQUE KEY `username` (`username`,`email`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
users_profiles
CREATE TABLE `users_profiles` (
`uid` int(11) NOT NULL,
`first_name` varchar(40) DEFAULT NULL,
`last_name` varchar(50) DEFAULT NULL,
`gender` char(6) DEFAULT NULL,
`website` varchar(100) DEFAULT NULL,
`msn` varchar(60) DEFAULT NULL,
`aim` varchar(60) DEFAULT NULL,
`yim` varchar(60) DEFAULT NULL,
`twitter` varchar(15) DEFAULT NULL,
UNIQUE KEY `uid` (`uid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
After creating the tables in phpmyadmin I created a foreign key on the users_profiles table, code below is what phpMyAdmin created.
As follows:
ALTER TABLE `users_profiles`
ADD CONSTRAINT `users_profiles_ibfk_1` FOREIGN KEY (`uid`) REFERENCES `users` (`uid`) ON DELETE CASCADE ON UPDATE CASCADE;
basically the users_profiles.uid is a foreign key and references the users.uid
In phpMyAdmin I go to insert and fill in some sample data leaving the uid obviously to auto increment. When i have inserted a record in users table I goes into the users_profiles table and notice the users.uid is not inserted automatically in the users_profiles,
Is this normal?
Reason is when someone for example registers on a form, they will be asked for username, email and password, and i do a query in php to insert that data into users table, i thought that because i have a foreign key that it would also automatically insert a row in the users_profiles table with the uid from users table so there is a link between the user and there profile. But when i insert a record into users table the users.uid is not inserted into the users_profiles table.
I tried another example to see what would happen and this one works as i would expect due to the cascade on update and delete.
If i insert a row in users table and then manually insert the users.uid into users_profiles.uid (they are now linked) and add for example my first_name and last_name then in phpmyadmin delete the user from users table it deletes the row in the users_profiles table. This works like it should obviously as i don't want a user to be deleted and have there profile remain.
This has confused me as when I do create a form and a user signs up, they essentially would not have a profile because on signup no profile is created for them with there users.uid in the user_profiles table (no link between them) although I have a foreign key.
Can some explain why it's not working as I expect, maybe it should be working like I want it to but something is wrong or I am missing the whole point otherwise.
UPDATE
In reference to reply from #Mark Wilkins
I understand what you mean now. But something I am not 100% sure on is this:
User signs up, a record is created in users table; they login and visit profile page where the can fill it in and submit the form.
On processing the form am I right in thinking I would need to do the following:
user filled in profile form and submitted (first time they submitted profile as they are a new user), after validating data etc I first check to see if the uid in the users table match a uid in the users_profile table, if there's a match then UPDATE record with new values (this would mean the user has previously filled in there profile as on signup they don't have one) but if no match is found on uid from both tables then I would perform an INSERT query because no profile yet exists for the user. I take it that obviously I would store the uid from users table in session with other data on successful login and the uid in session would be the uid that is inserted into the users_profiles table in column uid? That way a link is created between two tables and if I now decide to delete the user there profile will also be deleted to.
Foreign key constraints are not designed to create rows. Their purpose is to ensure data integrity by forcing that a value in a child table that references a parent table value actually exists in that parent table and prevents a parent row from being deleted that has references to it in a child table.
On insert, the calling code must write rows into the two tables (first users then profiles).
If I followed the description correctly, it is working as expected. A foreign key relationship basically says that a parent must exist for a given child (a user must exist for a given user_profile in your example). It does not require the opposite (that a user_profile record exist for a user). It will never result in an INSERT being performed on the child table. You have to insert the record into the user_profile table and the foreign key relationship will guarantee that it is maintained.
Edit for the additional OP info: In general, yes I believe that is the thing you want to do. I have not dealt enough with web development to know if that particular process is correct. In either case, though (whether or not a profile record has been created), you will need to know which user to modify. My opinion about this, however, would be to create the associated user_profile record directly after creating the user record (just leave the informational fields empty in it). That way you know it exists when the go to edit it and you don't have to perform a MERGE style operation.
Related
I am trying to figure out relationships and deletion options.
I have two tables, User and UserStaff, with a 1:n relationship from User to UserStaff (a user can have multiple staff members).
When my User is deleted, I want to delete all of the UserStaff tables associated with that User. When my UserStaff is deleted, I don't want anything to happen to User. I understand that this is a cascading relationship, but I'm not sure which way.
i.e. Do I select the existing foreign key in my UserStaff table and make it cascading, or do I create a new foreign key in User and set that to cascading?
Yes, it's possible. You should make the FK in UserStaff table. In this way:
User Table
CREATE TABLE `User` (
`Id` int(11) NOT NULL AUTO_INCREMENT,
`Name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`Id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
UserStaff Table
CREATE TABLE `UserStaff` (
`Id` int(11) NOT NULL AUTO_INCREMENT,
`UserId` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`Id`),
KEY `UserId` (`UserId`),
CONSTRAINT `UserStaff_ibfk_1`
FOREIGN KEY (`UserId`)
REFERENCES `User` (`Id`)
ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
From Wikipedia:
CASCADE
Whenever rows in the master (referenced) table are deleted (resp. updated), the respective rows of the child (referencing) table with a matching foreign key column will get deleted (resp. updated) as well. This is called a cascade delete (resp. update[2]).
Here, User is the master table, and UserStaff is the child table. So, yes, you'll want to create the foreign key in UserStaff, with ON DELETE CASCADE
It's been a while since I've used this, but here goes (btw, I use Toad for MySql - a great IDE, and it's free too - http://www.toadworld.com/Freeware/ToadforMySQLFreeware/tabid/561/Default.aspx!)
You need to add a Constraint to the User table. If you have an id column (and the corresponding foreign userid key in UserStaff) then the SouceColumn should be id, the destination table UserStaff and the destination column userid. You can then set the OnDelete action to be 'Cascade'
The other options are pretty self-explanatory - Restrict limits values to the values in the source column, Set Null sets the foreign key matches to Null and No Action does, er, nothing.
This stuff is very easy to do via the Toad IDE. I used MySqlAdmin tools for ages but recently discovered Toad (and it has diff and compare tools too!).
The ON DELETE CASCADE is specified on the foreign key in the UserStaff table. For additional info on foreign keys the MySQL documentation has a number of examples. The User table does not have a foreign key pointing to UserStaff, so it will not be affected by changes to the UserStaff table.
The easiest way might be to make two quick tables and try it out. But since you didn't I can tell you that the outcome will be that it work the way that you want to.
When you have a table User and a table UserStaff were a field in UserStaff uses a foreign key to reference a field in User; then if you delete a record from UserStaff that will be removed wihtout having any affect on the User table. The other way around will delete all records related to that record.
Short version: A field in UserStaff should reference a field in User with CASCADE
I'm relatively new to databases, database architecture and MySQL in general, so forgive me if my method isn't optimal. I have created a table called comments, for which i want to store the users id in to the column post_id which works fine as it stands. This table's sole purpose is to store messages posted on any given users profile and some other related information.
However i want to allow duplicate entries so i can read the comments table and look for a certain users id, then take the column comments from the comments table and display them on the users profile where the id is matched.
I'd do this by doing an INNER JOIN on comments and user_info, specifically the post_id from comments and id from user_info.
When posting the information to the database from a users profile, i get the below error
SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry '59' for key 'post_id'
user_info
'user_info', 'CREATE TABLE `user_info` (\n `id` int(11) NOT NULL AUTO_INCREMENT,\n `username` varchar(12) COLLATE utf8_unicode_ci NOT NULL,\n `pass` varchar(40) COLLATE utf8_unicode_ci DEFAULT NULL,\n `joined` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n PRIMARY KEY (`id`),\n UNIQUE KEY `username` (`username`)\n) ENGINE=InnoDB AUTO_INCREMENT=64 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci'
comments
'comments', 'CREATE TABLE `comments` (\n `post_id` int(11) DEFAULT NULL,\n `comment` text COLLATE utf8_unicode_ci,\n `date_posted` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n UNIQUE KEY `post_id` (`post_id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci'
Let me know if i'm missing anything that would potentially help you answer the question.
You need to drop unique index on post_id:
ALTER TABLE `comments` DROP INDEX post_id;
Then you can create a non-unique index if necessary:
ALTER TABLE `comments` ADD INDEX `post_id` (`post_id`);
Also your comments table lacks primary key. Create an auto increment integer column: comment_id, that would identify the record.
post_id should be an auto increment field as primary key in columns table that uniquely identifies a tuple (row). You should add another column called user_id that stores the id from user_info table. You should then add an index to user_id column in comments table for faster search.
You need three tables: users, comments, and commentlist.
The users table just holds user details. The comments table just holds comments. Commentlist has 3 columns: an id, which you won't really use, user id, and comment id.
You can then have multiple user id entries in the commentlist table, all relating to an individual comment id.
You have to check your query once again. Because the problem may exits on your select query. You have to remove unique index from post_id column of comments table.
If a row in comment is to be related to a row in user_info, and one user_info can have zero, one or more comment, and comment is related to exactly one user_info...
Then you'd store the value of the id column (PRIMARY KEY) column from the user_info, as a value in the comment table.
With the InnoDB engine, you can also define a FOREIGN KEY constraint.
For example:
ALTER TABLE `comment` ADD
`user_info_id` INT COMMENT 'FK ref user_info.id' ;
ALTER TABLE `comment` ADD
CONSTRAINT `FK_comment_user_info` FOREIGN KEY (`user_info_id`)
REFERENCES `user_info` (`id`)
ON UPDATE CASCADE
ON DELETE CASCADE
Leave current the post_id column as a unique key (or change it to a PRIMARY KEY) on the table.
Note that a JOIN operation will return multiple rows, when there are multiple comments for a given user_info
FROM `user_info` u
LEFT
JOIN FROM `comment` c
ON c.user_info_id = u.id
WHERE u.id = 42
If a comment can be related to more than one user_info, then that would be a many-to-many relationship, and the normative pattern to implement that would be to add a third association table, a row in the association table would have foreign key references to both the user_info table and the comment table.
I've got a user table, and a picture table.
The picture table has an uploader id column which is a foreign key.
It refers to the user id column in the user table.
For some reason, when I try to insert a new record into the picture table, it's not working. No error messages pop up. It just doesn't insert the new record.
INSERT INTO picture (pic_url, pic_uploader) VALUES($picurl, $uploader);
$picurl is an image src path from a file upload. The uploaded files are in the right directory, and the exact same code works perfectly for an earlier record without a foreign key.
$uploader contains the foreign key value -- from a session variable that contains the user id of the user account -- but it's not inserting it into the table.
Here's the SQL for the tables, in case that helps...
Picture table
CREATE TABLE IF NOT EXISTS picture (
pic_id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
pic_url varchar(200) NOT NULL,
pic_uploader bigint(20) unsigned NOT NULL,
PRIMARY KEY (pic_id),
KEY pic_uploader (pic_uploader)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
Picture constraints
ALTER TABLE picture
ADD CONSTRAINT pictures_ibfk_1 FOREIGN KEY (pic_uploader) REFERENCES user (user_id);
User table
CREATE TABLE IF NOT EXISTS user (
user_id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
user_name varchar(80) NOT NULL,
user_img varchar(200) NOT NULL DEFAULT 'img/defaultpic.png',
user_email varchar(100) NOT NULL,
user_pword char(60) NOT NULL,
user_stat enum('0','1','A') NOT NULL,
PRIMARY KEY (user_id),
UNIQUE KEY user_email (user_email)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=3 ;
There can be a reason that $uploader value does not exists in your user table as user_id.
First try to insert in simple way directly in db by any tool like sqlyog putty, mysqladmin etc.
INSERT INTO picture (pic_url, pic_uploader) VALUES('pic1', 'user1');
Make sure user1 should exist in user table. If it works fine then problem in your application other wise mysql will return an error by this we will be able to check the exact problem either it is due to foreign key or else. So do it and share results.
IN your picture table you are using columns NOT NULL.
But you try to insert only two values in the insert command that's why data is not inserted.
Either insert data for all columns or make columns as NULLABLE.
Thanks
create table Board (
boardID char(30) not null,
readLevel tinyint not null,
writeLevel tinyint not null,
PRIMARY KEY (boardID) ) engine=InnoDB character set=utf8;
create table Post (
postID int not null AUTO_INCREMENT,
title char(50) not null,
content TEXT not null,
writeDate date not null,
readCount int not null,
PRIMARY KEY (postID)) engine=InnoDB character set=utf8;
create table Save_Board_Post(
boardID char(30) not null,
postID int not null,
FOREIGN KEY (boardID) REFERENCES Board(boardID) ON UPDATE CASCADE,
FOREIGN KEY (postID) REFERENCES Post(postID) ON UPDATE CASCADE ) engine=InnoDB character set=utf8;
insert into Board (boardID, readLevel, writeLevel) values ('testBoard', 0, 0);
insert into Post (title, content, writeDate, readCount) values ('testPost1', 'test', CURRENT_TIMESTAMP() ,0);
select * from Board where boardID='testBoard';
select * from Post where tile='testPost1';
select * from Save_Board_Post where boardID='testBoard';
I'm rookie in sql. and I'm not native about English.
So, Please forgive my English skills.
Here's my mysql code.
Last five lines are for test. And select from Board and Post is working fine.
But
select * from Save_Board_Post where boardID= 'testBoard';
It doesn't work. This code has no error. but there is no output result.
I guess it means no data in Save_Board_Post table.
I thought REFERENCES command is automatically creation data when insert parent table.
If it does not, please let me know how to automatically creation in relation data.
No, that's not what REFERENCES does. All that your REFERENCES constraints mean is that every row that is inserted (manually) into the Save_Board_Post table must have a boardID and a postID that exist in the Board and Post tables. Nothing is inserted into that table automatically.
If you are trying to represent what board a post is in, the appropriate way to do this would be to make the board ID be a property of the post, e.g.
CREATE TABLE Post (
postID INTEGER NOT NULL AUTO_INCREMENT,
boardID CHAR(30) NOT NULL,
...
FOREIGN KEY (boardID) REFERENCES Board(boardID)
);
rather than having an entirely separate table just for that data.
You cannot automatically insert data in child table by inserting in the parent table. You got it right when you said it failed because there was no data in the table. Referential integrity exist to remove redundancy in a database. I dont think there is a way for you to automatically insert into the child table by inserting into a parent table. you have to do it manually.
I'm quite new to mysql. I want to make a database in mysql for a school. This database should store teachers' information and give some of these teachers the possibilities to create groups.
So I created a database that contains a table group and a table professeur. Group has many-to-many relationship with teachers and vice-versa which derives another table Group_professeur. Here is a simple structure of the tables:
Professeur:
CREATE TABLE IF NOT EXISTS `professeur` (
`id_professeur` int(11) NOT NULL AUTO_INCREMENT,
`nom_professeur` varchar(50) NOT NULL,
`prenom_professeur` varchar(50) DEFAULT NULL,
PRIMARY KEY (`id_professeur`),
UNIQUE KEY `LOGIN` (`login`),
UNIQUE KEY `MDP` (`passwd`),
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=103 ;
Group
CREATE TABLE IF NOT EXISTS `groupe` (
`id_groupe` int(11) NOT NULL AUTO_INCREMENT,
`nom_groupe` varchar(255) NOT NULL,
`id_prof_responsable` int(11) NOT NULL,
PRIMARY KEY (`id_groupe`),
UNIQUE KEY `nom_groupe` (`nom_groupe`),
KEY `id_prof_responsable` (`id_prof_responsable`),
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
ALTER TABLE `groupe`
ADD CONSTRAINT `fk_professeur_to_groupe` FOREIGN KEY (`id_prof_responsable`) REFERENCES `professeur` (`id_professeur`) ON UPDATE CASCADE
Groupe_has_teachers:
CREATE TABLE IF NOT EXISTS `groupe_professeur` (
`id_groupe` int(11) NOT NULL,
`id_professeur` int(11) NOT NULL,
KEY `id_groupe` (`id_groupe`),
KEY `id_professeur` (`id_professeur`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
ALTER TABLE `groupe_professeur`
ADD CONSTRAINT `groupe_professeur_ibfk_2` FOREIGN KEY (`id_professeur`) REFERENCES `professeur` (`id_professeur`) ON UPDATE CASCADE,
ADD CONSTRAINT `groupe_professeur_ibfk_1` FOREIGN KEY (`id_groupe`) REFERENCES `groupe` (`id_groupe`) ON UPDATE CASCADE;
Teachers can modify only the group(s) they created(i.e they can insert and delete members from their groups). Also, not all teachers have the right to create and modify groups.
After creating the tables, I was wondering who will give them the appropriate rights to do all these stuffs. I thought about creating an administrator. The admin can give the rights to certain teachers to create and modify their own groups and can also revoke these privileges.
I created a table which will store an administrator but who will give the admin the necessary rights to do these. Which brings me back to square one. And this table might have some relationships with other tables in the database thereby deriving some unnecessary tables.
Anyway I thought about changing the professors table to something general like staffs and adding the admin to the table. And then adding these staffs to the database. That means creating a new staff corresponds to adding the staff's information to the table staffs and then adding this staff as a user to the database. From there I can use SQL functions like GRANT and REVOKE to each user.
I'm not sure if this method is very efficient because these means if the school has 1000 professors then it has 1000 users in it's database.
Is there any efficient way to tackle this problem? Thanks.
Controlling application user access using the built-in MySQL notion of a user is unorthodox. For a basic application, I would recommend having another column in the group table for an owner_user_id that would refer to the professor table. Then in the application code, check for that id when the group is being altered.
Good luck!
You'll want to take a look at Role-based Access Control
Another explanation by Tony Marston