Constraint on table to limit number of records to be stored

前端 未结 2 1209
青春惊慌失措
青春惊慌失措 2021-01-12 22:21

I have a database and there are two tables Ads and Images. There is a primary key adid in Ads table which is a foreign ke

相关标签:
2条回答
  • 2021-01-12 22:48

    There is no constaint to enforce that rule, but a trigger like the following can do it:

    CREATE TRIGGER Images_not_more_than_five_per_add
    ON Images FOR INSERT
    AS
    DECLARE @RowCount int
    SET @RowCount = @@ROWCOUNT
    SET NOCOUNT ON
    IF @RowCount = 1
    BEGIN
        IF (SELECT COUNT(*) FROM Images WHERE Images.addid = (SELECT addid FROM inserted)) > 5
        BEGIN
            RAISERROR('No more than five images per add are allowed', 16, -1)
            ROLLBACK
            RETURN
        END
    END
    ELSE
    BEGIN
        IF EXISTS (
            SELECT *
            FROM
                Images
                INNER JOIN (
                    SELECT DISTINCT addid FROM inserted
                ) I ON Images.addid = I.addid
            GROUP BY
                Images.addid
            HAVING COUNT(*) > 5
        )
        BEGIN
            RAISERROR('No more than five images per add are allowed', 16, -1)
            ROLLBACK
            RETURN  
        END
    END
    
    0 讨论(0)
  • 2021-01-12 22:52

    The foreign key constraint doesn't have such option to limit the number of child rows.

    It is possible to achieve the desired effect using a trigger, but this kind of constraint is relatively simple and it is possible to achieve the desired effect using only declarative constraints.

    Create a helper table CheckFiveRows with column ID that contains five rows with values 1 to 5. ID is the primary key. Make sure that it contains only five rows. Add a column CheckFiveRowID to the Images table that is a foreign key pointing to CheckFiveRows.ID. Add unique constraint/index on (AdID, CheckFiveRowID).

    Table Ads

    CREATE TABLE [dbo].[Ads](
        [AdID] [int] NOT NULL,
        [AdData] [nvarchar](50) NOT NULL,
     CONSTRAINT [PK_Ads] PRIMARY KEY CLUSTERED 
    (
        [AdID] ASC
    ))
    

    Table CheckFiveRows

    CREATE TABLE [dbo].[CheckFiveRows](
        [ID] [int] NOT NULL,
     CONSTRAINT [PK_CheckFiveRows] PRIMARY KEY CLUSTERED 
    (
        [ID] ASC
    ))
    GO
    
    INSERT INTO [dbo].[CheckFiveRows] ([ID]) VALUES
    (1),(2),(3),(4),(5);
    

    Table Images

    CREATE TABLE [dbo].[Images](
        [ImageID] [int] NOT NULL,
        [AdID] [int] NOT NULL,
        [CheckFiveRowID] [int] NOT NULL,
        [ImageData] [nvarchar](50) NOT NULL,
     CONSTRAINT [PK_Images] PRIMARY KEY CLUSTERED 
    (
        [ImageID] ASC
    ))
    GO
    
    ALTER TABLE [dbo].[Images]  WITH CHECK 
    ADD  CONSTRAINT [FK_Images_Ads] FOREIGN KEY([AdID])
    REFERENCES [dbo].[Ads] ([AdID])
    GO
    
    ALTER TABLE [dbo].[Images] CHECK CONSTRAINT [FK_Images_Ads]
    GO
    
    ALTER TABLE [dbo].[Images]  WITH CHECK 
    ADD  CONSTRAINT [FK_Images_CheckFiveRows] FOREIGN KEY([CheckFiveRowID])
    REFERENCES [dbo].[CheckFiveRows] ([ID])
    GO
    
    ALTER TABLE [dbo].[Images] CHECK CONSTRAINT [FK_Images_CheckFiveRows]
    GO
    
    CREATE UNIQUE NONCLUSTERED INDEX [IX_CheckFiveRows] ON [dbo].[Images]
    (
        [AdID] ASC,
        [CheckFiveRowID] ASC
    )
    GO
    

    Testing

    Populate Ads:

    INSERT INTO [dbo].[Ads] ([AdID],[AdData]) VALUES
    (1, 'a1'),
    (2, 'a2');
    
    (2 row(s) affected)
    

    Populate Images:

    INSERT INTO [dbo].[Images] ([ImageID],[AdID],[CheckFiveRowID],[ImageData]) VALUES
    (1,1,1,'i1'),
    (2,1,2,'i2'),
    (3,1,3,'i3'),
    (4,1,4,'i4');
    
    (4 row(s) affected)
    

    Try to add two more row for AdID=1:

    INSERT INTO [dbo].[Images] ([ImageID],[AdID],[CheckFiveRowID],[ImageData]) VALUES
    (5,1,5,'i5'),
    (6,1,5,'i6');
    
    Msg 2601, Level 14, State 1, Line 8
    Cannot insert duplicate key row in object 'dbo.Images' with unique index 'IX_CheckFiveRows'. The duplicate key value is (1, 5).
    The statement has been terminated.
    

    Try to insert 6 rows for AdID=2:

    INSERT INTO [dbo].[Images] ([ImageID],[AdID],[CheckFiveRowID],[ImageData]) VALUES
    (11,2,1,'i1'),
    (12,2,2,'i2'),
    (13,2,3,'i3'),
    (14,2,4,'i4'),
    (15,2,5,'i5'),
    (16,2,6,'i6');
    
    Msg 547, Level 16, State 0, Line 13
    The INSERT statement conflicted with the FOREIGN KEY constraint "FK_Images_CheckFiveRows". The conflict occurred in database "AdventureWorks2014", table "dbo.CheckFiveRows", column 'ID'.
    The statement has been terminated.
    
    0 讨论(0)
提交回复
热议问题