Hi
You will need to create business rules so that you know what to do if a
active office is deleted or if you try to change the status. Although
re-reading your post seems to indicate that the following is not really what
you wanted you should be able to use it to get what you require, this
assumes that there is one active office per company and that is always the
minumum office id for that given company. The IsActive flag is automatically
assigned regardless of the value inserted and updates are not allowed to
change IsActive.
CREATE TABLE [Company] (
[CompanyID] [int] NOT NULL ,
[DateTimeCreated] [datetime] NOT NULL CONSTRAINT
[DF_Company_DateTimeCreated] DEFAULT (getdate()),
[DateTimeModified] [datetime] NULL ,
CONSTRAINT [PK_Company] PRIMARY KEY CLUSTERED
(
[CompanyID]
) ON [PRIMARY]
) ON [PRIMARY]
GO
CREATE TABLE [Office] (
[OfficeID] [int] NOT NULL ,
[DateTimeCreated] [datetime] NOT NULL CONSTRAINT
[DF_Office_DateTimeCreated] DEFAULT (getdate()),
[DateTimeModified] [datetime] NULL ,
[FullOfficeName] [varchar] (100) COLLATE SQL_Latin1_General_CP1_CI_AS
NOT NULL ,
CONSTRAINT [PK_Office] PRIMARY KEY CLUSTERED
(
[OfficeID]
) ON [PRIMARY]
) ON [PRIMARY]
GO
DROP TABLE [CompanyOffice]
CREATE TABLE [CompanyOffice] (
[CompanyID] [int] NOT NULL ,
[OfficeID] [int] NOT NULL ,
[IsActive] [bit] NOT NULL CONSTRAINT DF_IsActive DEFAULT (0),
CONSTRAINT [PK_CompanyOffice] PRIMARY KEY CLUSTERED
(
[CompanyID],
[OfficeID]
) ON [PRIMARY] ,
CONSTRAINT [FK_CompanyOffice_Company] FOREIGN KEY
(
[CompanyID]
) REFERENCES [Company] (
[CompanyID]
),
CONSTRAINT [FK_CompanyOffice_Office] FOREIGN KEY
(
[OfficeID]
) REFERENCES [Office] (
[OfficeID]
)ON DELETE CASCADE
) ON [PRIMARY]
GO
CREATE TRIGGER trg_Company_Office_Insert ON [CompanyOffice]
INSTEAD OF INSERT AS
BEGIN
IF @@ROWCOUNT = 0 RETURN
SET NOCOUNT ON
INSERT INTO [CompanyOffice] ( [CompanyID], [OfficeID], [IsActive] )
SELECT n.[CompanyID], n.[OfficeID],
CASE WHEN NOT EXISTS ( SELECT 1
FROM [CompanyOffice]o
WHERE n.[CompanyID] = o.[CompanyID]
AND o.[IsActive] = 1 )
AND NOT EXISTS ( SELECT 1
FROM INSERTED i
WHERE n.[CompanyID] = i.[CompanyID]
AND n.[OfficeID] > i.[OfficeID] )
THEN 1
ELSE 0 END
FROM INSERTED n
END
GO
CREATE TRIGGER trg_Company_Office_Delete ON [CompanyOffice]
FOR DELETE AS
BEGIN
IF @@ROWCOUNT = 0 RETURN
SET NOCOUNT ON
UPDATE c
SET [IsActive] = 1
FROM [CompanyOffice] c
JOIN DELETED d ON c.[CompanyID] = d.[CompanyID] AND c.[OfficeID] = ( SELECT
MIN(o.[OfficeID]) FROM [CompanyOffice] o WHERE c.[CompanyID] = o.[CompanyID]
AND c.[OfficeID] > d.[OfficeID] )
WHERE d.[IsActive] = 1
END
CREATE TRIGGER trg_Company_Office_Update ON [CompanyOffice]
INSTEAD OF UPDATE AS
BEGIN
IF @@ROWCOUNT = 0 RETURN
SET NOCOUNT ON
IF UPDATE ([IsActive])
BEGIN
IF EXISTS ( SELECT 1
FROM INSERTED i
JOIN DELETED d ON i.[CompanyID] = d.[CompanyID] AND i.[OfficeID] =
d.[OfficeID] AND i.[IsActive] = 0 AND d.[IsActive] = 1 )
OR EXISTS ( SELECT 1
FROM INSERTED i
JOIN DELETED d ON i.[CompanyID] = d.[CompanyID] AND i.[OfficeID] =
d.[OfficeID] AND i.[IsActive] = 1 AND d.[IsActive] = 0 )
RAISERROR ('Can not change IsActive', 16, 1)
END
END
GO
INSERT INTO [Company] ( [CompanyID] , [DateTimeCreated],
[DateTimeModified] )
SELECT 1, getdate(), getdate()
UNION ALL SELECT 2, getdate(), getdate()
UNION ALL SELECT 3, getdate(), getdate()
UNION ALL SELECT 4, getdate(), getdate()
UNION ALL SELECT 5, getdate(), getdate()
INSERT INTO [Office] ( [OfficeID], [DateTimeCreated], [DateTimeModified],
[FullOfficeName] )
SELECT 1, getdate(), getdate(), 'Company 1 Office 1'
UNION ALL SELECT 2, getdate(), getdate(), 'Company 2 Office 2'
UNION ALL SELECT 3, getdate(), getdate(), 'Company 3 Office 3'
UNION ALL SELECT 4, getdate(), getdate(), 'Company 1 Office 4'
UNION ALL SELECT 5, getdate(), getdate(), 'Company 1 Office 5'
UNION ALL SELECT 6, getdate(), getdate(), 'Company 2 Office 6'
UNION ALL SELECT 7, getdate(), getdate(), 'Company 2 Office 7'
UNION ALL SELECT 8, getdate(), getdate(), 'Company 4 Office 8'
UNION ALL SELECT 9, getdate(), getdate(), 'Company 2 Office 9'
UNION ALL SELECT 10, getdate(), getdate(), 'Company 5 Office 10'
UNION ALL SELECT 11, getdate(), getdate(), 'Company 4 Office 11'
INSERT INTO [CompanyOffice] ([CompanyID], [OfficeID] )
SELECT 1, 1
UNION ALL SELECT 2, 2
UNION ALL SELECT 3, 3
UNION ALL SELECT 1, 4
UNION ALL SELECT 1, 5
UNION ALL SELECT 2, 6
UNION ALL SELECT 2, 7
UNION ALL SELECT 4, 8
UNION ALL SELECT 2, 9
UNION ALL SELECT 5, 10
UNION ALL SELECT 4, 11
SELECT * FROM [CompanyOffice]
ORDER BY [CompanyID] , [OfficeID]
DELETE FROM [CompanyOffice]
WHERE [CompanyID] = 2
AND [OfficeID] = 2
SELECT * FROM [CompanyOffice]
ORDER BY [CompanyID] , [OfficeID]
DELETE FROM [CompanyOffice]
WHERE [CompanyID] = 2
AND [OfficeID] = 6
SELECT * FROM [CompanyOffice]
ORDER BY [CompanyID] , [OfficeID]
DELETE FROM [CompanyOffice]
WHERE [CompanyID] = 2
AND [OfficeID] = 9
SELECT * FROM [CompanyOffice]
ORDER BY [CompanyID] , [OfficeID]
UPDATE [CompanyOffice]
SET [IsActive] = 0
WHERE [CompanyID] = 1
AND [OfficeID] = 1
SELECT * FROM [CompanyOffice]
ORDER BY [CompanyID] , [OfficeID]
UPDATE [CompanyOffice]
SET [IsActive] = 1
WHERE [CompanyID] = 1
AND [OfficeID] = 4
SELECT * FROM [CompanyOffice]
ORDER BY [CompanyID] , [OfficeID]
UPDATE [CompanyOffice]
SET [IsActive] = 1
WHERE [CompanyID] = 1
AND [OfficeID] = 1
SELECT * FROM [CompanyOffice]
ORDER BY [CompanyID] , [OfficeID]
INSERT INTO [Office] ( [OfficeID], [DateTimeCreated], [DateTimeModified],
[FullOfficeName] )
SELECT 12, getdate(), getdate(), 'Company 5 Office 12'
INSERT INTO [CompanyOffice]( [CompanyID], [OfficeID], [IsActive] )
SELECT 5, 12, 1
SELECT * FROM [CompanyOffice]
ORDER BY [CompanyID] , [OfficeID]
John
<rk******@gmail.com> wrote in message
news:11*********************@l41g2000cwc.googlegro ups.com...
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...
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 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.
My secondary thought was to just restrict an Insert or Update Statement
that leaves the Table in an "error" state (2 or 0 Active Companies per
an Office). Then I realized that if the "Toggling Trigger" did not
exist from above, it will often be the case that the Table would have
to be left in an "error" state for a short while, until a second update
statement is run. (example, I insert a new active Company in the
CompanyOffice table for an Office, then I go to the other active
Company record for this Office and set the IsActive flag to false...
for that short period of time between the 2 statement the DB is an
"error" state, because there are 2 Active Companies for that single
Office.) That makes this solution very undesirable.
Any suggestions?
Thanks in Advance --
Rich