I have a case where the DB is set up with (simplifying) a table User and a table SuperUser. Both have a column Id. Every SuperUser row has a row in the User table with the same Id value (not my design!!!). The SUperUser table does not pull common info from the User table, it duplicates it.
Can/should any foreign key that points to User.Id also have a FK:PK relationship with SuperUser.Id? My take on this is it is at a minimum a very bad idea and that many (most?) DBs can't enforce this relationship/
Am I off base here?
Based on my understanding of your question you would run into two major problems with a foreign key set up to both the User and SuperUser tables.
You would not be able to enter or update any table with a foreign key set up like that unless the user was in both tables since the foreign key expects that userid to be present in both of them.
If you have the foreign key set up with a cascade delete and remove a record from the SuperUser then all tables with that set up would remove records associated with that userid potentially leaving you with just the record in the User table and all other information lost.
What would be a better option would be setting the userid column in the SuperUser table to be a foreign key of the userid column in the User table that way you will not run into issues where a user is not in both tables or removing a user from the SuperUser table.
Since every user has a record in the user table and I am assuming that not every user is a super user, I would just reference them by user and check if they are a super user when needed.
It would good to remove the duplicated fields if at all possible. This can easily become a nightmare to maintain as things grow.
Related
Let's say I have a database with two tables, User and Store.
Lets make the rules:
A User must belong to one Store
A Store may have one or more Users
A store though, may have a store manager. What is the best approach for this?
Adding a 'is_store_manager' boolean column at the Users table, or create a foreign key called something like manager_user_fk at the Store table? I guess that would create a many to many relationship though, which would be bad, but it would be a solid constraint to select a user I think. What would be the best approach?
Don't create a fk on the Store. It is somewhat redundant and will make some future SQL queries harder.
You could add another table, UserType with the Manager, and Non-Manager types. You'd then add a fk on the Users table pointing to the UserType.
Edit:
If you wanted a user to be allowed multiple roles, you'd need another join table:
Let's call the previous table table Role, instead of UserType, and add another table, UserRole that is a join between User and Role (it has only 2 columns: a foreign key to User, and a foreign key to Role. With this setup, you wouldn't have any fk on the User table, as this join table would hold all the information about the relationship. A user could have as many roles as you like then.
An alternative to the accepted solution which only allows a user to be of one type you can use what I've been doing to replace boolean status fields. Create a table called UserManager with a primary key also being a foreign key to User.
Any user with an entry in UserManager is a manager. To get the managers you just join the User table with the UserManager. This also lets you store more meta data (i.e. you could store when the user became a manager etc).
Then if you want an AdminUser table, you do the same thing. Any user in the AdminUser table is also an admin. You can have a user be both (or none, or one). Along with storing more meta data about the type.
I have a list of users in a table. When I delete a User and then create a user with the same account name, it takes the primary id of the deleted user.
The problem with this is, though a user is deleted, I keep track of what all users are deleted. When a new user takes the primary id of a deleted user, I have two instances with same primary id in my table.
I want the primary id to be different from the old one.
How do I do it?
This is a known behavior of AUTO_INCREMENT fields in MySQL:
http://forums.mysql.com/read.php?10,167180
If this is really your problem, you will have to find an other strategy to keep track of deleted user accounts.
For more information, see Stop MySQL Reusing AUTO_INCREMENT IDs
So I have a SQL relationship problem. Lets say I have a database where I want to keep records of information about individuals. Now I have setup a table to take on that information. Okay so far so good.
Often times duplicate information can be discovered in the table and it would be removed. A record is considered a duplicate if a particular field has the same value as another field in another row. Example: Duplicate emails.
Now I want to create another table in the database to keep track of every duplicate that is ever discovered and deleted. My first thought into this was to create a Foreign Key relationship. So I created and then connected a dupes table to my persons table. The relationship was a simple Foreign to Primary key relationship with an on delete constraint.
Now while that may have worked at first the problem arose that the dupes table was receiving records that were deleted even if they were not deleted because they were dupes. This was a problem because even if I decided to delete a person from the persons table just because I did not like them, they would stored in the dupes table anyway.
Then I thought, why not create a disposition field in the persons table and connect that as a unique or primary key to my dupes table's index foreign key. Well the problem is a unique key must have a unique value so multiple dispositions of dupe or I don't like you would not work. The other option was to make the disposition field a primary key. That has the same problem though.
What would be the right relationship for this problem?
I can think of this implementation: An on delete trigger, with a 'before delete' check. The before delete check would confirm if the record being deleted is a duplicate or not. Not sure what all RDBMS systems support such checks though.
IMO, the theoritical relationship is complicated because the record is supposed to be preserved even after the dupe is deleted.
Foreign Keys are not going to solve this problem. I discovered Triggers and their exactly what I need.
I have 3 related tables as shown in the image below.
Everyone has an entry in the User table and a UserRole.
Some Users are Subscribers.
I want to constrain the Subscribers table so that it can only contain users whose role IsSusbcriber but retain the UserRole at User level. Is it possible?
Excuse the current relationships between the table, they represent whats there at the moment rather whats necessarily needed.
I think you could drop the IsSubscriber columns and add a UserSubscriberRoles table that will contain exactly those roles that had previously set the IsSubscriber column.
CREATE UserSubscriberRoles
( UserRoleId PRIMARY KEY
, FOREIGN KEY (UserRoleId)
REFERENCES UserRoles (UserRoleId)
) ;
Then change the FKs in Subscribers table to:
FOREIGN KEY (UserId, UserRoleId)
REFERENCES User (UserId, UserRoleId)
FOREIGN KEY (UserRoleId)
REFERENCES UserSubscriberRoles (UserRoleId)
Is it possible?
In theory, yes; you can use a SQL assertion. See this StackOverflow answer for an example.
In practice, however, no major DBMS supports SQL assertions, and what you describe cannot be implemented as a foreign-key constraint, so I think your only option is to write a trigger that evaluates this constraint, and raises an exception if it's not satisfied.
The only way to contrain this without RoleId in the table is via either a trigger (and triggers are usually a bad design choice), or a SQL Agent job that periodically removes people from Subscribers that don't fit the criteria.
With RoleID in Subscribers, you can add a check constraint that limits it to a specific value.
This would really be better enforced with a different design and/or in the application code.
I have four tables:
users: id, thread_id
threads: id, language_id
posts: id, user_id, language_id
languages: id
USERS.thread_id is a foreign key to THREADS.id, POSTS.user_id is foreign key to USERS.id and POSTS.language_id foreign key to LANGUAGES.id.
I can't delete an user because the POSTS.user_id will throw a foreign key constraint error, and I can't delete the post because I want all the posts (and threads) to be readable there even if the user is deleted.
What should I do?
This is known as a soft delete and you can see it at work right here on SO. When you see an answer from a 'deleted' user, they're grayed out.
Keep the user in the database but add a flag column isDeleted which you set when the user is deleted.
Then (obviously) disallow any logins for that user and (optionally) display them specially.
Looks like everything is working as designed :) If MySQL let you delete the user while leaving posts.user_id pointing to it, your DB would become inconsistent.
If you want posts from deleted users to be readable, reset their user_id to something else (0? NULL?) before deleting the user.
If you want the user info to remain too, then you obviously can't delete the user row. You should add some kind of 'user is deleted' column that changes how the user is shown on the UI.
Foreign keys are for enforcing data integrity. Since you have a reason to have posts and threads exist without a valid user id, then you don't really need the foreign key data integrity.
I would either remove the foreign key entirely, or utilize the ON DELETE portion of a foreign key creation clause. You can have MySQL CASCADE, RESTRICT, or SET NULL when a referenced foreign value changes.
In this case, you could create the foreign key with ON DELETE SET NULL, and the user id would be set to NULL in your posts table when you delete a user. Foreign keys are created with RESTRICT by default, which is why you can't delete the user and leave an orphaned value in the posts table.
Don't actually add foreign key restraints to your tables. They aren't necessary. Without them you're free to do whatever you want. Only add them if they are necessary.