On 25 Mar 2005 10:33:54 -0800,
rkrueger@gmail.com wrote:
(snip DDL - thanks for posting it!)[color=blue]
>The CompanyOffice.dbo.IsActive bit field is supposed to be marked
>"true" for 1 record per a given Office (i.e. there can only be a single
>"Active" Company for any given Office). I decided the best way to
>enforce is through a trigger...[/color]
Hi Rich,
Well, there's another design possiblity as well: instead of an IsActive
column, you could add a column to the Office table: ActiveCompany. Then
set up a foreign key from (ActiveCompany, OfficeId) in the Office table
to (CompanyId, OfficeId) in CompanyOffice, to ensure that it's
impossible to active a company that the office isn't related to.
In case you have good reasons to stick to your original design, I'll try
to address the rest of your message.
[color=blue]
>My initial thoughts were a toggling effect (similar to the behavior
>that a radio button exhibits)... which would work like a champ for a
>Single Row Insert or Update[/color]
Only in part. I can see you'd automatically deactivate the previous
choice if you activate a new choice - but what would you do if I set the
IsActive bit in a specific row to 0 without simultaneously setting it to
1 in another row for the same office. Would you just randomly pick one
of the office's companies and activate it? Or would you abort the
transaction and raise an error? If it were my project, I'd choose the
latter.
[color=blue]
> but for a Multi Row Insert/Update not that
>staight forward... I fooled around a little with some complicated
>sub-queries that did not pan out. The only other way to do this is to
>utilize a cursor (at least that I can think of). Because of the
>overhead with a cursor, I find this incredibly undesirable.[/color]
(snip)
The multi-row insert/update does need a bit more thought, but there's no
need to use a cursor (there seldom is). Let's first investigate the
possibilities:
1. The insert/update leaves one or more offices without any active
company. As indicated above, I'll assume this should result in an error
condition.
2. One or more offices that are affected by the insert/update have one
active company. Great; that's exactly what wew want. No action needed
for these offices.
3. One or more offices that are affected by the insert/update now have
more than one active company, BUT only one of them as a direct result of
the insert/update (the other already was active). In this case, the
toggle can be applied.
4. One or more offices that are affected by the insert/update now have
more than one active company, AND more than one of them as a direct
result of the insert/update. Though you could choose to pick one at
rando to retain as active and silently deactivate all others, I'd say
that this should really result in an error.
Note that all four situations can be present in the same multi-row
insert or update, so the trigger should handle them all. For efficiency,
I'll check the two error conditions first, then handle the toggling.
Situation 2 needs no handling, of course!
CREATE TRIGGER MyTrigger
ON CompanyOffice
AFTER INSERT, UPDATE
AS
-- Bail out if no processing needed
IF @@ROWCOUNT = 0 RETURN
IF NOT UPDATE(IsActive) RETURN
-- Prevent recursive execution
IF TRIGGER_NESTLEVEL(object_id('MyTrigger')) > 1 RETURN
-- Situation 4: 2 companies for same office activated
IF EXISTS (SELECT *
FROM inserted
WHERE IsActive = 1
GROUP BY OfficeID
HAVING COUNT(*) > 1)
BEGIN
RAISERROR ('Situation 4', 16, 1)
ROLLBACK TRANSACTION
RETURN
END
-- Situation 1: no companies activated for an office
IF EXISTS (SELECT *
FROM CompanyOffice
WHERE OfficeID IN (SELECT OfficeID FROM inserted)
GROUP BY OfficeID
HAVING MAX(IsActive) = 0)
BEGIN
RAISERROR ('Situation 4', 16, 1)
ROLLBACK TRANSACTION
RETURN
END
-- Situation 3: switch active company for office
-- no need to check for >1 new active company for an office;
-- that situation has already been handled above.
UPDATE CompanyOffice
SET IsActive = 0
WHERE IsActive = 1
AND EXISTS (SELECT *
FROM inserted AS i
WHERE i.OfficeID = CompanyOffice.OfficeID
AND i.CompanyID <> CompanyOffice.CompanyID
AND i.IsActive = 1)
go
(Note: the above code is untested)
Best, Hugo
--
(Remove _NO_ and _SPAM_ to get my e-mail address)