views:

43

answers:

3

I have two tables:

info: ID, fee_id

and

fee: ID, amount

and a reference between them (SQL Server 2008):

ALTER TABLE info WITH CHECK ADD CONSTRAINT FK_info_fee FOREIGN KEY(fee_id)
REFERENCES fee (ID)
ALTER TABLE info CHECK CONSTRAINT FK_info_fee
GO

How to configure this reference that way so a record in fee will be deleted if info.fee_id becomes NULL

EDIT: or maybe set info.fee_id to NULL on deleting the corresponding record in fee.

Anyway I can do it this way:

UPDATE info SET fee = NULL WHERE = ..
DELETE FROM fee WHERE ..

but I'm sure that this can be done by the database itself.

+2  A: 

You probably don't want to do this. What would you expect to happen if multiple info rows referenced the same fee row?

If you really want to do something like this, adding logic to an AFTER UPDATE, DELETE trigger on the info table would probably be the way to go. Check if any other info rows reference that same fee row, and if not, delete the fee row.

Michael Petrotta
I have one to one reference, i.e. only one record in `fee` is pointing to `info`.
abatishchev
@abatishchev: do you have database logic constraining the `fee` table to contain only one reference to each `info` row? If you don't, sooner or later, you're going to get multiple references.
Michael Petrotta
My table `fee` in production has also column `amount` and holds the data for 'invite-a-friend'. So constraining not via database logic but real-life business logic :) Only one client can invite a new one.
abatishchev
A: 

If you really intend to remove rows when fee_id is set to null, one way is an update trigger. In an update trigger, the deleted table contains the old version of the updated rows, and the inserted table contains the new version. By joining them, you can take action when a fee_id changes to null:

CREATE TRIGGER deleteFee
ON info
FOR UPDATE
AS
    DELETE FROM Fee 
    WHERE Fee.id IN (
        SELECT old.fee_id 
        FROM deleted old
        JOIN inserted new ON old.id = new.id
        WHERE old.fee_id = fee.id 
        AND new.fee_id is null
    )

This is tricky when multiple info rows refer to the same fee. The fee will be removed if any info row is set to null. A full synch trigger would avoid that:

CREATE TRIGGER deleteFee
ON info
FOR UPDATE
AS
    DELETE FROM Fee 
    WHERE NOT EXISTS (
        SELECT *
        FROM Info
        WHERE Fee.id = Info.fee_id
    )

But this can have other unintended consequences, like deleting half the Fee table in response to an update. In this case, as in most cases, triggers add more complexity than they solve. Triggers are evil and should be avoided at almost any cost.

Andomar
A delete cascade would go the other way: delete `info` rows if a `fee` row is deleted, would it not?
Michael Petrotta
@Michael Petrotta: You're right, I've removed the cascase, I'll upvote your answer :)
Andomar
+1  A: 

Some thoughts:

  • If you have a one:one reference then can the 2 tables be combined?
  • Drilling up from child to parent is odd: if it's 1:1 then can you reverse the FK direction and simply CASCADE NULL?
  • Otherwise, you'll have to use a trigger but assuming 1:1 makes me uneasy...
  • ... unless you have a unique constraint/index on info_fee.fee_id

Like so:

ALTER TABLE info WITH CHECK ADD
CONSTRAINT FK_fee_info_fee FOREIGN KEY (id) REFERENCES info_fee (fee_ID) ON DELETE SET NULL
gbn
Thanks! Seems to be useful! One question: as far as I could understand, in this configuration I have to delete `fee` and appropriate column in `info` will be NULLed automatically. Right?
abatishchev
Yesm, this is waht "ON DELETE SET NULL" does. On parent delete, a child row via the FK is set null
gbn
Oh no. That is not what I need. Parent record must exists always. Child one can be deleted and `info.fee_id` must be NULLed (in any order of this 2 operations). Is it possible using `REFERENCE`? Or only using `TRIGGER`?
abatishchev
You question says "How to configure this reference that way so a record in fee will be deleted"?
gbn
Yea! Record in `fee` (which is child) must be deleted when record in `info` (which is parent) is update
abatishchev
I tried and it does what I need - set NULL in parent record on child deletion
abatishchev