I have a table that links to another — called PageTopic
and TopicPageRole
respectively.
The foreign key on PageTopic
is:
CONSTRAINT [FK_PageTopic_TopicPageRole] FOREIGN KEY ([PageRoleId]) REFERENCES [dbo].[TopicPageRole] ([PageRoleId])
Both tables have a column named 'IsMultiMember'; I want to cascade update if IsMultiMember changes for the PageRoleId but I've realised that it may lead to unintended consequences — cascading updates if PageRoleId changes.
Have I got it wrong? If I'm right in my thinking, how do I only update the required column when data changes?
Here some code to better explain why I'm trying to do what I am. Some code removed for brevity.
CREATE TABLE [dbo].[TopicPageRole] (
[PageRoleId] TINYINT NOT NULL,
[MultiMember] BIT NOT NULL
CONSTRAINT [PK_TopicPageRole] PRIMARY KEY CLUSTERED ([PageRoleId] ASC),
CONSTRAINT [UQ_TopicPageRole_PageRoleId_MultiMember] UNIQUE NONCLUSTERED ([PageRoleId] ASC, [MultiMember] ASC)
);
CREATE TABLE [dbo].[PageTopic] (
[PageId] INT NOT NULL,
[TopicId] SMALLINT NOT NULL,
[PageRoleId] TINYINT NOT NULL,
[MultiMember] BIT NOT NULL
CONSTRAINT [PK_PageTopic] PRIMARY KEY CLUSTERED ([PageId] ASC),
CONSTRAINT [FK_PageTopic_TopicPageRole] FOREIGN KEY ([PageRoleId]) REFERENCES [dbo].[TopicPageRole] ([PageRoleId]),
CONSTRAINT [FK_PageTopic_TopicPageRole2] FOREIGN KEY ([PageRoleId], [MultiMember]) REFERENCES [dbo].[TopicPageRole] ([PageRoleId], [MultiMember]),
);
// Ensure only one entry per role type where MultiMember is false
GO
CREATE UNIQUE NONCLUSTERED INDEX [IX_PageTopic_PageRoleId_Single]
ON [dbo].[PageTopic]([TopicId] ASC, [PageRoleId] ASC) WHERE ([MultiMember]='0');
IsMultiMember
is a property of table PageRole
; in essence it declares whether a topic is allowed to have multiple pages for a particular role. So a hub page can only exist once per topic, while a post can exist multiple times. The TopicPage
table contains pages along with their assigned topic. Currently, if I don't have a PageRole
/IsMultiMember
column also in that table I can't limit the number of pages. I'd be happy to listen to alternative ways.
I can't see that there would be an issue with using an ON UPDATE CASCADE
on this foreign key. Since PageRoleId
is the primary key for TopicPageRole
, it's not actually likely to change ever (it certainly shouldn't be a common occurence). The only cascading would be on MultiMember
.
However, you should ensure your foreign key is properly indexed if you want the cascading to work efficiently
CREATE NONCLUSTERED INDEX [IX_PageTopic_PageRoleId_MultiMember]
ON [dbo].[PageTopic]([TopicId] ASC, [MultiMember] ASC);
Having said that, you don't need to use the extra column to enforce this constraint. There is another way to do multi-table constraints, using a an indexed view instead of a filtered index.
First, just remove the column and second foreign key from PageTopic
CREATE TABLE [dbo].[PageTopic] (
[PageId] INT NOT NULL,
[TopicId] SMALLINT NOT NULL,
[PageRoleId] TINYINT NOT NULL
CONSTRAINT [PK_PageTopic] PRIMARY KEY CLUSTERED ([PageId] ASC),
CONSTRAINT [FK_PageTopic_TopicPageRole] FOREIGN KEY ([PageRoleId]) REFERENCES [dbo].[TopicPageRole] ([PageRoleId]),
-- make sure you index the foreign key
INDEX IX NONCLUSTERED (PageRoleId ASC)
);
Now we can create a joined indexed view, and put a unique constraint on it.
CREATE OR ALTER VIEW View_PageTopic_PageRoleId_Single
WITH SCHHEMABINDING
AS
SELECT
pt.TopicId,
pt.PageRoleId
FROM dbo.PageTopic pt
JOIN dbo.TopicPageRole pr ON pr.PageRoleId = pt.PageRoleId
WHERE pr.MultiMember = 0;
CREATE UNIQUE CLUSTERED INDEX [IX_PageTopic_PageRoleId_Single]
ON dbo.View_PageTopic_PageRoleId_Single([TopicId] ASC, [PageRoleId] ASC);