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.

 All Forums
 General SQL Server Forums
 Database Design and Application Architecture
 Subtype supertype constraints child required

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 ChildTypes
SELECT 1,'First Child','True'
UNION ALL
SELECT 2,'Second Child','True'
UNION ALL
SELECT 3,'Something without children','False'

GO


CREATE TABLE Parents(
Id int Primary Key,
ChildTypeId int NOT NULL References ChildTypes(Id),
Label varchar(50) NOT NULL,
CONSTRAINT Testcon UNIQUE(Id,ChildTypeId)
)

GO

CREATE 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)
)
GO

CREATE 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 these

GO
--insert some test data in, need constraint to allow this
INSERT INTO Parents(Id,Label,ChildTypeId)
SELECT 1,'test',1
UNION ALL
SELECT 2,'test2',2
UNION ALL
SELECT 3,'test3',3


INSERT 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 child
BEGIN TRAN
DELETE FirstChildren
COMMIT TRAN


GO
--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 completed
BEGIN 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 TRAN



GO
--clean up
DROP TABLE FirstChildren
DROP TABLE SecondChildren
DROP TABLE Parents
DROP 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.
Go to Top of Page

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.
Go to Top of Page

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/

Go to Top of Page

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.
Go to Top of Page
   

- Advertisement -