Ограничить запись таблицы базы данных на основе данных в другой таблице - PullRequest
0 голосов
/ 21 апреля 2019

У меня есть две таблицы базы данных, которые связаны между собой:

CREATE TABLE [dbo].[TopicKeyword] 
(
    [Id]      SMALLINT     NOT NULL,
    [Keyword] VARCHAR(100) NOT NULL,
    [Volume]  INT          NOT NULL,
    [PageId]  SMALLINT     NOT NULL,

    CONSTRAINT [PK_TopicKeyword] PRIMARY KEY CLUSTERED ([Id] ASC)
);


CREATE TABLE [dbo].[TopicCluster] 
(
    [KeywordId] SMALLINT NOT NULL,

    CONSTRAINT [PK_TopicCluster] PRIMARY KEY CLUSTERED ([KeywordId] ASC),

    CONSTRAINT [FK_TopicCluster_TopicKeyword] 
        FOREIGN KEY ([KeywordId]) REFERENCES [dbo].[TopicKeyword] ([Id])
);

Как видите TopicCluster ссылки TopicKeyword с использованием KeywordId.

Однако мне нужно обеспечить дополнительную целостность таблицы TopicCluster.

PageID в таблице TopicKeyword может повторяться несколько раз, но в таблице TopicCluster должно быть разрешено только одно ключевое слово на *1015*.

Например:

TopicKeyword

Id, PageId

  • 1, 5
  • 2, 6
  • 3, 5 // 5 повторяется

TopicCluster

KeywordId

  • 1
  • 2
  • 3 // не должно быть разрешено, потому что 1 уже ссылается на PageId: 5

Я создал решение, но я не специалист по SQL, поэтому я не уверен, что в данном случае это правильный подход. Вот снова таблицы, переработанные, чтобы включить новые ограничения:

CREATE TABLE [dbo].[TopicKeyword] 
(
    [Id]      SMALLINT     NOT NULL,
    [Keyword] VARCHAR(100) NOT NULL,
    [Volume]  INT          NOT NULL,
    [PageId]  SMALLINT     NOT NULL,
    CONSTRAINT [PK_TopicKeyword] PRIMARY KEY CLUSTERED ([Id] ASC)
);
GO

// New
CREATE UNIQUE NONCLUSTERED INDEX [IX_TopicKeyword_Id_PageId]
ON [dbo].[TopicKeyword]([Id] ASC, [PageId] ASC);

CREATE TABLE [dbo].[TopicCluster] 
(
    [KeywordId] SMALLINT NOT NULL,
    [PageId]    SMALLINT NOT NULL,
    CONSTRAINT [PK_TopicCluster] PRIMARY KEY CLUSTERED ([KeywordId] ASC),
    CONSTRAINT [AK_TopicCluster_PageId] UNIQUE NONCLUSTERED ([PageId] ASC), // New
    CONSTRAINT [FK_TopicCluster_TopicKeyword] FOREIGN KEY ([KeywordId]) REFERENCES [dbo].[TopicKeyword] ([Id]),
    CONSTRAINT [FK_TopicCluster_TopicKeyword2] FOREIGN KEY ([KeywordId], [PageId]) REFERENCES [dbo].[TopicKeyword] ([Id], [PageId]) // New
);
GO

// New
CREATE NONCLUSTERED INDEX [IX_TopicCluster_KeywordId_PageId]
ON [dbo]

Любой совет приветствуется.

Ответы [ 2 ]

0 голосов
/ 22 апреля 2019

Ваш DDL подходит для вашего случая использования.

Если у меня есть что-то плохое, чтобы сказать об этом, это будет столбец PageId, который должен присутствовать только в таблице TopicKeyword (дублирование данных), вы можете использовать SQL JOIN для получения идентификатора PageId при необходимости.

Если я угадаю правильно, ваша таблица TopicKeyword должна хранить одну уникальную комбинацию (ключевое слово и страницу) на строку, для этого я бы сделал следующее:

CREATE TABLE [dbo].[TopicKeyword] 
(
    [Id]      SMALLINT     NOT NULL,
    [Keyword] VARCHAR(100) NOT NULL,
    [Volume]  INT          NOT NULL,
    [PageId]  SMALLINT     NOT NULL,
    CONSTRAINT [PK_TopicKeyword] PRIMARY KEY CLUSTERED ([Id] ASC),
    CONSTRAINT [UQ_TopicKeyword] UNIQUE NONCLUSTERED ([Keyword] ASC, [PageId] ASC)
);
GO

PK будет использоваться для справки, а уникальное ограничение обеспечит уникальную комбинацию (ключевое слово и страницу) для строки.

Для второй части вы хотите сохранить в таблице кластера ключевые слова, которые были связаны только с одной страницей, для этого я бы сделал следующее:

CREATE FUNCTION FN_IsPageIdUnique
(
    @KeywordId SMALLINT
)
RETURNS BIT
AS 
BEGIN
    DECLARE @IsPageIdUnique BIT
    DECLARE @PageIdCount INT

    SELECT @PageIdCount = COUNT(k2.[PageId])
    FROM [TopicKeyword] k1
    INNER join [TopicKeyword] k2 ON k2.[PageId] = k1.[PageId]
    WHERE k1.[Id] = @KeywordId

    IF (@PageIdCount = 1)
        SET @IsPageIdUnique = 1
    ELSE
        SET @IsPageIdUnique = 0

    RETURN @IsPageIdUnique
END
GO


CREATE TABLE [dbo].[TopicCluster] 
(
    [KeywordId] SMALLINT NOT NULL,
    CONSTRAINT [PK_TopicCluster] PRIMARY KEY CLUSTERED ([KeywordId] ASC),
    CONSTRAINT [FK_TopicCluster_TopicKeyword] FOREIGN KEY ([KeywordId]) REFERENCES [dbo].[TopicKeyword] ([Id]),
    CONSTRAINT CK_TopicCluster CHECK (dbo.FN_IsPageIdUnique(KeywordId))
);
GO

Ограничение проверки поможет вам обеспечить уникальность данных вашего кластера на основе данных другой таблицы, которые будут рассчитаны с помощью скалярной функции FN_IsPageIdUnique.

0 голосов
/ 22 апреля 2019

Я очистил ваш скрипт:

CREATE TABLE TopicKeyword 
(
    Id      smallint     NOT NULL,
    Keyword varchar(100) NOT NULL,
    Volume  int          NOT NULL,
    PageId  smallint     NOT NULL,
    CONSTRAINT PK_TopicKeyword PRIMARY KEY (Id),
    CONSTRAINT UK_TopicKeyword UNIQUE (Id, PageId) -- to create FOREIGN KEY in TopicCluster
)
GO

CREATE TABLE TopicCluster 
(
    KeywordId smallint NOT NULL,
    PageId    smallint NOT NULL,

    CONSTRAINT PK_TopicCluster PRIMARY KEY (KeywordId), -- to exclude duplicates in KeywordId
    CONSTRAINT UK_TopicCluster_PageId UNIQUE (PageId),  -- to exclude duplicates in PageId

    CONSTRAINT FK_TopicCluster_TopicKeyword
          FOREIGN KEY (KeywordId, PageId)
          REFERENCES TopicKeyword (Id, PageId) -- to prevent pairs of KeywordId&PageId which not in TopicKeyword
)
GO
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...