I have a particularly difficult business constraint that I would like to enforce at the database level. The data is financial in nature, and so must be protected from inconsistencies to the nth degree – no trusting the business layer with this stuff. I use the word “temporal” somewhat loosely, meaning that I intend to control how an entity can and cannot change over time.
Glossing over the details, here’s the design:
- An invoice can feature several fees.
- Fees are assigned to an invoice shortly after creation of the invoice.
- The invoice reaches a stage in the process after which it is “locked.”
- From this point on, no fee may be added to or removed from this invoice.
Here’s a stripped down data definition:
CREATE TABLE Invoices
(
InvoiceID INT IDENTITY(1,1) PRIMARY KEY,
)
CREATE TABLE Fees
(
FeeID INT IDENTITY(1,1) PRIMARY KEY,
InvoiceID INT REFERENCES Invoices(InvoiceID),
Amount MONEY
)
You’ll notice that the “lockable” nature of the invoice isn’t represented here; how to represent it – and whether it needs to be directly represented at all – is still an open question.
I have come to believe that this is one of those arrangements that cannot be translated into domain-key normal form, though I may be wrong. (There really is no way to tell, after all.) That said, I still hold out hope for a highly-normalized solution.
I happen to be implementing this on SQL Server 2008 (the syntax may have been a hint), but I’m a curious guy, so if there are solutions that work on other DBMS’s, I’d love to hear about those as well.
Don’t complicate it, I’d go with triggers. There is no shame in using them, this is what they are there for.
To avoid lots of logic in the triggers, I add an “Editable” bit column into the header table, then basically use a divide with Editable to either work or cause a divide by zero error, which I CATCH and convert to a
Invoice is not editable, no changes permittedmessage. There are no EXISTS used to eliminate extra overhead. Try this:here is a simple test script to test out the different combinations: