help with deleting relational tables - sql

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.

Related

If I got many to many relationship data first, how do I insert them to my table?

Say I have a customer table, a product table and an order table to record who buys what, the order table basically has 2 foreign keys, customer_id & product_id.
Now I got the order information first, within in it I can't find the customer information in my local database. As it turns out this is a new customer, whose information will come later from another thread/queue. To make things even worse the customer id I got from the order information is not the same one I used locally. My local customer id is INTEGER PRIMARY KEY (I do record that "true customer id" as another column and set index on it)
So how I do record this order information? I can come up with some clumsy solution, e.g. if I can't find contact info, I insert a record for it first. Later after I get the real information for this customer, I update customer & order table. But I was wondering is there any "standard" way for the situation like this?
Inserting NULL values, and then updating with the real values later, is simple and will work (if you don't have a NOT NULL constraint).
You should use a transaction so that concurrent users don't see the incomplete data.
You could use deferred foreign key constraints:
If a statement modifies the contents of the database such that a deferred foreign key constraint is violated, the violation is not reported immediately. Deferred foreign key constraints are not checked until the transaction tries to COMMIT. For as long as the user has an open transaction, the database is allowed to exist in a state that violates any number of deferred foreign key constraints.
However, deferred foreign key constraints are useful only if you are inserting values that violate the constraint; a NULL value is not considered a FK violation.

Can a single foreign key point to multiple primary keys?

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.

Constrain table entries on sql table

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.

Database: One To Many (or One To None) relationship

Im modelling a database in MSSQL 2008.
I have 4 tables.
**User**
userID
userName
**NewsCategory**
newsCategoryID
newsCategoryName
**News**
newsID
newsText
newsCategoryID
**Subscription**
userID
categoryID
I understand that I should have foreign keys between the News and the Category tables. But what should I do with the supscriptions? Should I have a Foreign Key between User and Subscription tables though it's not mandatory to subscribe for something?
Yes you should. Foreign key is used for be sure, that Subscription is created for existing user. Foreign key does not mean, that user should be subscribed on something.
Yes you should have this foreign key because it will prevent a Subscription from existing that does not map to a real user id.
It acts as a constraint on your data.
Subscription is a link (many-many) table and "not mandatory" means there will no row for that user or that user/category.
The foreign key is required to enforce data integrity when you do have subscriptions which will be one or more rows.
Note: In optional parent-child type relationships the FK column(s) will be NULLable to capture "non mandatory". In link tables this is captured by row non-existence
Yes, you should add Foreign keys between User and SubCription tables with Subscription table.
Foreign key contraints are for the validating of adding wrong information to the database. For example, in your Subscription table, there shouldn't be userIDs which are not in the User table and there should be CategoryIDs which are not in the NewsCategory table. These contraints will do the validation for you even if you don't do the validation at the user interface end.
You've gotten some good answers. Let me try to add another.
A SUBSCRIPTION requires both a subscriber and a category. Therefore, each of these columns should not allow nulls. Preventing nulls is not the same thing as a foreign key constraint.
It should also be impossible to insert a row into SUBSCRIPTIONS if the user does not already exist in the USERS table; and it should be impossible to insert a row into SUBSCRIPTIONS if the category does not already exist in the CATEGORIES table. To enforce these rules your SUBSCRIPTIONS table requires two foreign key constraints:
ALTER TABLE SUBSCRIPTIONS ADD CONSTRAINT FK_SUBSCRIPTIONS_USERS FOREIGN KEY(userid) REFERENCES USERS(userid)
ALTER TABLE SUBSCRIPTIONS ADD CONSTRAINT FK_SUBSCRIPTIONS_CATEGORIES FOREIGN KEY(categoryid) REFERENCES CATEGORIES(categoryid)
When you create a foreign key constraint on a table, you are in effect saying to the database engine: make sure that any value that gets inserted into this table already exists in that other table. BTW, a requirement for the constraint to be created is that a unique constraint must be in effect on the column(s) referenced in that table; typically, the referenced column(s) of that table will be the primary key of that table.
By creating a foreign key constraint, you are not saying to the database engine: make sure a row gets inserted into this table. It is quite possible (though it would be unusual) that this table has no rows in it whatsoever. The foreign key constraint simply makes sure that any value that does get inserted into this table has a counterpart in that table.

SQL - how to keep track of "simple relations"

I hope somebody can edit my title to better describe what I mean, because I don't know exactly what this would be called. However, consider this setup: I want to create a notification system, where a message is displayed to a user until he clicks "dismiss". I then need to "remember" that this user has dismissed the notification so I don't show it to him again. Here is my current solution
users table has a uid primary key and user info
notifications table has a nid primary key and notification text
notifications_seen table with two columns, uid and nid
When somebody clicks dismiss on a notification, I store their uid and the notification's nid in notifications_seen. This seems to work fine, but phpMyAdmin has giant red messages telling me that notifications_seen does not have an index. However, neither column is unique. Should I really have an extra utterly useless column in notifications_seen and call that a primary key? Is there a better way to do this?
You can use more than one column to create your primary key. In this case, you should set nid AND uid as your primary key in your notifications_seen table. The idea here is that even though neither nid or uid will be unique within your notifications_seen table; the nid/uid PAIR is unique. You should add a primary key constraint to these two columns. This is usually what you would like to do for this kind of situation.
There are times where you might actually want to create an auto-increment row to simplify the primary key. For example, when your best candidate key consists of a lot of columns (I'm pulling this out of the air; but lets say 4 or more columns) or you have columns which contain strings; which would be slower to match when doing lookups. But for this situation, just adding the primary key constraint to the two columns should be more than fine.
Primary keys are indexed BY default; which is why you should just add the primary key constraint to the two columns. This will also preserve the integrity of your data by making sure you don't accidentally insert rows with the same uid/nid pair.
You should also add a foreign key constraint on the uid to the id in the users table, and a foreign key constraint to the nid on the id in the notifications table. Adding the foreign key constraints will ensure you don't insert uids or nids which don't actually exist into your notifications_seen table.
You may be able to create a compound primary key (consisting of both uid and nid).
You could make an index on notifications_seen that contains both columns! Or create a separate column just for a primary key, or do both - having an index on uid and nid might speed up queries (but don't worry too much about that until you start to notice major performance problems - just remember it for the future). Having a primary key for these n:n relations isn't a terrible thing.