override constraint from no action to cascading at runtime - sql

I feel like I have a very basic/stupid question, yet I never saw/read/heard anything in this direction.
Say I have a table users(userId, name) and a table preferences(id, userId, language). The example is trivial but could be extended to a situation with multi-level relations and way more tables..
When my UI requests to delete a user I first want to show a warning stating that also its preferences will be deleted. If at some point the database gets extended with more tables and relationships, but the software isn't adapted accordingly (the client didn't update) a generic message should be shown.
How can I implement this? The UI cannot know about the whole data structure and should not be bothered to walk down all the relations to manually delete all the depending records.
I would think this would be with constraints.
The constraint would be no action at first so the constraint will throw an error that can be caught by the UI. After the UI receives a confirmation, the constraint should become a cascade.
Somehow I'm feeling like I'm getting this all wrong..

What I would do is this:
The constraint is CASCADE
The application checks if preferences exist.
If they do, show the warning.
If no preferences exist, or the warning is accepted, delete the client.
Changing database relationships on the fly is not going to be a good idea!!
Cheers,
RB.

If you are worried about the user not realising the full impact of their delete, you might want to consider not actually deleting the data - instead you could simply set a flag on a column called say "marked_for_deletion". (the entries could then be deleted a safe time later)
The downside is that you need to remember to filter out the marked rows in other queries. This can be mitigated by creating a view on the table with the marked rows filtered out, and then always using the view in your queries.

Related

Is there a way to create an virtual table that would be compared to one after editing?

I have a table [contractor c] in which only one field [tin] may be edited. If the user tries to change data in other field it shall not be updated. Was wondering if making a view of the c before editing and then comparing the view with edited table is a good idea. But that would require two scripts- before and after update.
I could also make a validation on every single field except tin, but there is 'a lot' of fields.
Looking for the best and most optimal way to approach this task.
This is too long for a comment. There are many ways to do what you describe. "Views" are not one of them. In SQL, a view is a stored query. It does not store values. That is definitely not going to help, because the view changes with the underlying tables.
If only one column can be updated, then one method is to implement a trigger that checks the before- and after- versions of the record and only allows updates when no other fields change.
You can start learning about triggers in the documentation.
An alternative mechanism is to make the table unupdatable except for update permissions on a single column. You can learn about permissions in the documentation.
If for some reason you wanted to do all the work in the application, then transactions might come into use. You would not commit the transaction until the update meets your requirement. Transactions are explained in the documentation.

Database Design for basic CMS

I am creating a basic CMS for a small academic project I want to use to practice design patterns in code later and I am starting with designing the database.
The simple requirement is that a "Container" of some kind will contain "Pages" or "Controls". Also a "Page" will contain "Controls". Think of a container like the holder for several tabbed pages. So when something is saved, everything can be saved at once by simply calling save on the container.
Anyway, I have 3 tables. "Container", "Page" and "Control".
Think of a "Container" like a holder for all the things that will appear on screen. It can be split into other smaller containers if needed (for wide screen).
Think of a "Page" like a form of some kind. Something that will allow the user to place controls used to collect data.
Think of a "Control" like a label, text box, button, etc. There are various types of controls (not shown in the diagram), but the "Controls" table will hold multiple instances of any given control type. For example, a form that has 5 labels and 5 text boxes, will have 10 corresponding entries in the controls table - 5 entries for each type of control. So a control is a singular instance of a control type, will have a unique ID, and can only be used once.
If a container is deleted, I want this to cascade and delete the relevant pages and controls.
Likewise, if a page is deleted, I want this to cascade and delete the controls that were on that page.
I have 2 problems to solve.
Problem 1: A control can belong to either a container or a page, but not both. Essentially a control can have only one owner or parent. Likewise with a page. However with a control, because it can be put on either a container, or a page, I have to connect it with 2 relationships, and create a constraint on the PageId and ControlId columns that ensures only one of them is not null. This I have done successfully, but I am wondering if there is a better way?
Problem 2: Because I am using cascade update and delete with 3 tables. I am getting an error:
Introducing FOREIGN KEY constraint 'FK_Pages_Containers' on table
'Pages' may cause cycles or multiple cascade paths. Specify ON DELETE
NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY
constraints.
I want the controls belonging to a page or container, AND the pages belonging to a container deleted if either the page or container is deleted. So, in my mind, I need the cascading update and delete. So this error is forcing me to change my relationship requirements in a way that will leave orphans.
As a side note, a container could also contain other containers. This might be useful on large wide screens to display a form (page) in two parts, one on the left and one on the right for example, and still be able to save everything by clicking a save button on the parent.
So how can I overcome this? Am I overlooking something in my design?
For clarity here is an ER diagram:
You can see from the stars (*) that it is not saved yet because of the above error. My first goal is to get the database right. I don't like entity framework code first, and I want to use either entity framework database first or an alternative ORM that does it's thing as needed. But I want data integrity handled by the database where it should be.
Any suggestions would be welcome.
Based on the comments under the question. I tried a few solutions. Here is one that I came up with that seems to work best so far. I am just posting this for my own reference later, and maybe it will help others. So I still welcome any other suggestions for improvement.
To start I removed all the relationships and created a container type table. This I linked to a container manager table. Then I lined the container and pages table with a 1:1 relationship to the container management table. The logic behind this was that I currently have 2 types of containers, but later I could add more if I wanted.
In the container types I added data for "Container" and "Page" then referenced this back to the container manager table. I added an additional field in the container manager table to show who the parent of the container or page was. This is a self referencing relationship. It simplified the container and pages table somewhat.
Next I repeated the process with the controls table. It was slightly more complicated but the same idea. It just split containers from controls very nicely and then let the management tables link up to "manage" things.
From there it was simply a matter of adding a container parent to the control manager table and relating it back to the container manager table. All the relationships are cascading and work without problems. As you can see, the management tables, combined with the 1:1 relationships to controls, containers, and pages tables, provide instance specific information allowing for easy management. I really like that I don't have to build any constraints like I had in my original solution to ensure only one of 2 fields were filled in as constraints are not obvious. However, I have noticed that I could technically just combine the data from the linked 1:1 tables directly into the management tables and simplify the diagram even more. Which I may yet do as it is easier and faster to look at 2 tables, than 5. I would be interested to hear peoples opinion of this.
The general use case would be that we know the types of containers or controls in advance. So we can select the type of controls we want to put on a page or container. This information adds data to the management tables and generates a unique Id for the instance of the container, page, or control being used. Then we use that Id to create the actual container, page, or control instance the user will see. Obviously saving the data behind the controls is another matter that is not part of the question so I wont go into my solution for that here.
Thanks to D Mayuri who inspired this approach with is help and comments about separating out the relationships. I am not sure if this is how he intended me to do it. If he posts his own answer, I will of course accept and give him credit.

Effectively make database records read-only

How can I make sure that specific data in the database isn't altered anymore.
We are working with TSQL. Inside the database we store contract revisions. These have a status: draft / active. When the status has become active, the revision may never be altered anymore. A revision can have 8 active modules (each with its own table), each with their own settings and sub-tables. This creates a whole tree of tables with records that may never change anymore when the contract revision has been set to active.
Ideally I would simply mark those records as read-only. But such thing does not exists as of today. The next thing that comes to mind are triggers. Thus I have to add those triggers to a lot of tables, all which are related to the contract revision.
Now maybe there are other approaches, like a database only for archiving on which the user only has insert rights. Thus when a contract revision has become active, it is moved from one DB to the archive DB (insert is allowed). And can never be altered anymore (DENY UPDATE|DELETE).
But maybe there are other more ingenious options I haven't thought of, and you did. Maybe including the CLR or what not.
So how can I make a tree-structure of records inside our TSQL database effectively readonly that is the most maintenance free, easy to understand, quickly to setup, and can be applied in a most generic way?
What ever you do (triggers, granted rights...) might be overcome by a user with higher rights, this you know for sure...
Is this just to archive this data?
One idea coming into my mind was to create a nested XML with all data within on big structure and put this somewhere into a side table. Create a INSTEAD OF UPDATE,DELETE TRIGGER where you just do nothing. Let these tables be 1:1-related.
You can still work with this data, but not quite as fast as being read from physical tables.
If you want, you even might convert the XML to a string and calculate some Hash-Code, which you store in a different place to check for manipulations.
The whole process might be done in one single Stored Procedure call.

Combining SQL Insert and Delete

Ok here is the question
I have three tables
events
- id
- name
questions
- id
- event_id
- name
answers
- id
- question_id
- description
While generating the add form, i can simply search for all the questions belonging to particular event, and show the questions in single HTML form.
But consider the edit scenario. An event manager can in due time add or delete some questions from an event. So in such case, when the user tries to edit already posted answer, I generate the HTML form with the new questions as blank field and existing answers are prefilled input boxes. How can I handle the submission of this form?
When the user submits, should i delete all past answers and do a INSERT on the answers table for all the answer values? or is it a good practice to UPDATE existing answers first and INSERT only new values?
The former obiviously being easier with DELETE followed by INSERT. While the later is somewhat tedious. The problem with former solution is that the id of the answers table will increase every time..
Some people favour the delete/insert approach because, like you say, it is simpler.
Personally I think the update/insert/delete approach, while more work, is more correct.
If you do updates you can then have an audit trail of changes to a particular item. With just insert/delete it's either much harder or plain impossible to have that kind of linkage and history.
As for how to handle the submission of the form, for fields that can be updated (ie they're existing records), you need to be able to identify the field somehow. Personally I just encode something like the primary key in the field name.
If you do this you must of course ensure that you don't have a security hole by validating that the ID supplied is valid and the edit allowed ie never trust the client.
This could take the form of:
<input type="text" name="name_117" value="Some value">
<input type="text" name="name_118" value="Some other value">
<input type="text" name="name_1243" value="Yet another value">
and you have to process all the input parameters, decode the identifier and act accordingly.
Lastly, another problem with insert/delete is that you can't do it (or it just gets really hard) if the items you're deleting/inserting relate to other tables in the database. If you have a question table and store the answers people give, normally you'll reference the question as a foreign key. You lose that association if you delete/insert instead of updating.
First, the ID shouldn't be being used for anything anyway, so disregarding the incrementation is a good reminder.
I would prefer to deal with the primary question, however, cnnceptually, everything else being equal. Is the user in fact establishing a different set of questions with their edits? Then save them as a new set. Or is the original set intended to retain its conceptual identity? Then update/delete/insert. Which seems to be more the case.
In terms of securing the integrity of the question/answer set, I think of sessions having that responsibility. You should consider validating the question set against that associated with the session.
Online quiz applications usually lock down a quiz once it is live. It is a messy problem to deal with completed answer sets versus a changing definition of questions. If your application can handle it, I would favor not allowing questions to change once an event is first published.
Agree with Cletus post, writing seperately for an extra reason and an idea:
The delete/insert approach has a concurrency issue, when your website gets some heavy traffic. Someone else could do an insert after your delete, and then you'll end up with multiple answer lists per user.
MySQL supports the ON DUPLICATE KEY UPDATE syntax. That might be an easy way to combine the insert and the update, if you have an appropriate primary key.

What is the database concept equivalent to "when deleting the user, delete all his posts"?

What is the database concept equivalent to "when deleting the user, delete all his posts"?
And is this a good thing?
Another example: if your website is a programming forum, you need to find and delete comments related to that topic before deleting the topic.
Should this be handled automatically in the database layer?
cascading deletes
I would hesitate to recommend real deletion - instead using a soft deletion which marks a record as deleted - in this case, you might use cascading updates (or not, since the original topic has already been marked as deleted).
Cascading updates, usually used in conjunction with foreign key references. Different DBMS offer varying levels of support.
In the specific case of a forum or similar web site, I'd suggest using "soft" deletion - flag the rows in the databases as being deleted, which will prevent them from being viewed or returned in lists or search results, but don't remove them completely. This facilitates undeletion, etc. to counter shoddy or biased moderation.
In addition, I'd suggest that automatically deleting a user's posts when you delete their user account may not be the best behaviour in all cases - certainly, when dealing with troll/spam accounts, you may want to remove junk posts, but you don't necessarily want to blast away all the information in other cases, particularly as it introduces issues with broken references (e.g. external references, cross-linking from other posts, etc.)
The answer to your question is cascading deletes. For the record, I hate user deletion as a forum feature. If people want to leave, great ... I want to see the history of what they did while they were there.
Not sure if this is what you wanted to find out, but in MySQL the type of thing (I think) you're asking about is called a trigger. It's basically an SQL statement that you associate with a table and an action on that table; for example, you can set a statement that will execute whenever a user's record is deleted which will delete all comments/posts/whatever associated with that user.
see http://dev.mysql.com/doc/refman/5.0/en/create-trigger.html and links therein (that's for MySQL, of course... other DBs may differ)