Please start any new threads on our new
site at https://forums.sqlteam.com. We've got lots of great SQL Server
experts to answer whatever question you can come up with.
Author |
Topic |
michael.appleton
Posting Yak Master
160 Posts |
Posted - 2009-10-21 : 11:32:36
|
Hello,I have what a consider a very common database design problem concerning subtypes. My requirements are:A parent table that stores the childtype of an object dependent on which child table other data is stored in.An object can only have one entry in one child table, and dependent on the childtype, must exist in the corresponding child table.So basically a one to one relationship between parent and children tables, where only one child exists for each parent.This article [url]http://www.sqlteam.com/article/implementing-table-inheritance-in-sql-server[/url] seems to explain one of the simplest ways to use a constraint to check whether a parents type is the correct type for the child table it also has data in. But I am stuck thinking of a way to implement the constraint of having to have a child existing. In the example in the article, a person could exist in the people table and be of type teacher, but not have an entry in the teachers table.I thought that I could use triggers for this constraint, but I really don't know how to write something like that. Perhaps if there was a method of a trigger running after an entire batch rather than a single interaction on a table, I could write a solution. But I don't think such a method exists. Here is some code to show my requirements a bit more clearly:CREATE TABLE ChildTypes( Id int Primary Key, Label varchar(50) not null, HasChildTable bit not null)INSERT INTO ChildTypesSELECT 1,'First Child','True'UNION ALLSELECT 2,'Second Child','True'UNION ALLSELECT 3,'Something without children','False'GOCREATE TABLE Parents( Id int Primary Key, ChildTypeId int NOT NULL References ChildTypes(Id), Label varchar(50) NOT NULL, CONSTRAINT Testcon UNIQUE(Id,ChildTypeId))GOCREATE TABLE FirstChildren( Id int Primary Key references Parents(Id), ChildTypeId AS 1 Persisted References ChildTypes(Id), OtherStuff varchar(50), Foreign Key (Id,ChildTypeId) References Parents(Id,ChildTypeId))GOCREATE TABLE SecondChildren( Id int Primary Key references Parents(Id), ChildTypeId AS 2 Persisted References ChildTypes(Id), ReallyDifferentAttributes varchar(50), Foreign Key (Id,ChildTypeId) References Parents(Id,ChildTypeId))GO--create constraint where one child exists for each entry in parent table. Could use triggers? Not sure how I should construct theseGO--insert some test data in, need constraint to allow thisINSERT INTO Parents(Id,Label,ChildTypeId)SELECT 1,'test',1UNION ALLSELECT 2,'test2',2UNION ALLSELECT 3,'test3',3INSERT INTO FirstChildren(Id,OtherStuff)SELECT 1,'some data'INSERT INTO SecondChildren(Id,ReallyDifferentAttributes)SELECT 2,'some really different data'GO--test condition in which constraint should stop query, when removing child information without changing the childtype to --something that has no childBEGIN TRAN DELETE FirstChildrenCOMMIT TRANGO--Test condition in which constraint would pass query, when changing the childtype of the object.--I guess constraint would have to work after the whole batch has been completedBEGIN TRAN DECLARE @IdToBeChanged int SET @IdToBeChanged=2 DELETE SecondChildren WHERE Id=@IdToBeChanged UPDATE Parents SET ChildTypeId=1 WHERE Id =@IdToBeChanged INSERT INTO FirstChildren(Id,OtherStuff) SELECT @IdToBeChanged, 'now a first child'COMMIT TRANGO--clean upDROP TABLE FirstChildrenDROP TABLE SecondChildrenDROP TABLE ParentsDROP TABLE ChildTypes Perhaps I am thinking about this problem in the wrong way, but it is a pretty fundamental business rule that a parent MUST have only one child, so I imagine it is best managing it at the database level. I sometime read about storing business rules in different places, but besides storing this in the app, which doesn't seem like a good idea, I'm not really sure what people mean. Perhaps I'll have to always rely on stored procedures to interact with this table, but I'd really rather have a constraint that is always applied.Also, some people say that one-to-one relationships should be avoided in relational database design. Although I've simplified the problem in the example, in the real database this would mean having 100s of columns to cater for every child type, filled with nulls for most cases, which doesn't seem right. Also, I have a similar issue with one-to-many relationships, where I need at one or more children to exist in one child table dependent on the child type declared in the parent table, but I hope by solving the first problem, this one should be easy.Thanks for the help. |
|
dportas
Yak Posting Veteran
53 Posts |
Posted - 2009-10-22 : 04:46:26
|
The constraint that you want actually cannot be enforced in most SQL DBMSs because multi-table updates aren't generally supported - the parent table must always be populated before the child table can be. Unfortunately therefore it's a fundamental limitation of SQL that it can only enforce optional referential relationships, not ones that are mandatory in both the parent and child table (except in some uninteresting special cases where inserts to the parent table are not permitted at all).Oracle, DB2 and other DBMSs have what's called a "deferrable" constraint as a workaround for this problem - but a deferrable constraint really just means that the constraint is temporarily disabled.SQL Server does not have the defertable constraint syntax. You can immitate it by creating two foreign key constraints and then disabling one of them temporarily for each insert but that has the effect of disabling the constraint across all connections rather than just for the current insert.You may just have to live with the fact that the constraint cannot be enforced in the database and implement it though other code instead. |
|
|
michael.appleton
Posting Yak Master
160 Posts |
Posted - 2009-10-22 : 06:10:25
|
Thanks for the reply. Yes, I've read about deferrable constraints, and they do seem the only "proper" way to do it, but as you've said SQL Server does not support this. It's strange, this requirement must be so common, I'd expect something as mature as SQL, and SQL Server in particular, would have an accepted, simple solution, perhaps via multi-updates.I don't think I can use the two foreign key approach as I have many child tables. I think that only works if you have one child table. Perhaps you could explain how that could work. As for implementing this or other difficult rules through other code, would you suggest something like having scheduled stored procedures that check rules every hour or required period of time? I keep reading about storing business rules in a different "layer", but I'm not always sure exactly what this means.I really do appreciate the help this forum provides, thanks again. |
|
|
dportas
Yak Posting Veteran
53 Posts |
Posted - 2009-10-22 : 07:01:47
|
quote: Originally posted by michael.appleton It's strange, this requirement must be so common, I'd expect something as mature as SQL, and SQL Server in particular, would have an accepted, simple solution, perhaps via multi-updates.
You are absolutely right. Declarative integrity of any kind is immediately crippled in a database that can't support multiple assignment. The industry has learned to live with SQL despite this and other serious deficiencies. Although it might be possible to extend the SQL syntax to support multiple assignment, so many things would probably need changing that the end result wouldn't really be recognisable as SQL any more. If you were willing to go that far then why bother to extend SQL at all, with its quirks and limitations? Much better to move to a new database paradigm - one that could more fully live up to what the relational data model was always supposed to be. Vendors have been slow to do that for obvious commercial reasons but there is certainly a growing realisation that SQL is broken - although oddly that's a topic more frequently discussed outside the SQL data management community than within it! I think things are slowly changing and hopefully it will eventually be for the better.There are third party business rules engines that can help you but the approach I most commonly see is to enforce such rules through a server-side business layer (in .NET, Java or whatever) and/or using service-oriented architectures. A good source on business rules is:http://www.brcommunity.com/ |
|
|
michael.appleton
Posting Yak Master
160 Posts |
Posted - 2009-10-22 : 11:59:59
|
Well that's great, the problem's with SQL! I think my less than perfect work around might be only update/insert/delete access to said tables via stored procedures where checks will be included and the store procedure will roll back if they fail. Less than perfect, and might impact performance, but there's going to have to be some compromise. These rules are so fundamental I think it's vital they are as close to the data as possible. I think a different layer is needed for more dynamic and changing rules from what I can tell. I think I'll try and keep most of mine inside the database. |
|
|
|
|
|
|
|