Для предотвращения использования дубликатов тегов в базе данных

Я хотел бы знать, как вы можете предотвратить использование двух одинаковых тегов в таблице базы данных. Один сказал мне, что используют два закрытых ключа в таблице. Однако сайт W3Schools утверждает, что это невозможно.

Моя реляционная таблица

http://files.getdropbox.com/u/175564/db/db7.png

Моя логическая таблица

http://files.getdropbox.com/u/175564/db/db77.png

Контекст таблиц

http://files.getdropbox.com/u/175564/db/db777.png

Как вы можете предотвратить использование дублирующих тегов в вопросе?

5 ответов

Решение

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

Я обновил эту модель, чтобы предотвратить дублирование тегов. Это не имело значения прежде. Но так как вы хотите, вот оно (для Postgres):

START TRANSACTION ISOLATION LEVEL SERIALIZABLE, READ WRITE;

CREATE SCHEMA so;

SET search_path TO SO,"$user",public;

CREATE DOMAIN so.HashedPassword AS 
    BIGINT CONSTRAINT HashedPassword_Unsigned_Chk CHECK (VALUE >= 0);

CREATE TABLE so."User"
(
    USER_ID SERIAL NOT NULL,
    USER_NAME CHARACTER VARYING(50) NOT NULL,
    EMAIL_ADDRESS CHARACTER VARYING(256) NOT NULL,
    HASHED_PASSWORD so.HashedPassword NOT NULL,
    OPEN_ID CHARACTER VARYING(512),
    A_MODERATOR BOOLEAN,
    LOGGED_IN BOOLEAN,
    HAS_BEEN_SENT_A_MODERATOR_MESSAGE BOOLEAN,
    CONSTRAINT User_PK PRIMARY KEY(USER_ID)
);

CREATE TABLE so.Question
(
    QUESTION_ID SERIAL NOT NULL,
    TITLE CHARACTER VARYING(256) NOT NULL,
    WAS_SENT_AT_TIME TIMESTAMP NOT NULL,
    BODY CHARACTER VARYING NOT NULL,
    USER_ID INTEGER NOT NULL,
    FLAGGED_FOR_MODERATOR_REMOVAL BOOLEAN,
    WAS_LAST_CHECKED_BY_MODERATOR_AT_TIME TIMESTAMP,
    CONSTRAINT Question_PK PRIMARY KEY(QUESTION_ID)
);

CREATE TABLE so.Tag
(
    TAG_ID SERIAL NOT NULL,
    TAG_NAME CHARACTER VARYING(20) NOT NULL,
    CONSTRAINT Tag_PK PRIMARY KEY(TAG_ID),
    CONSTRAINT Tag_UC UNIQUE(TAG_NAME)
);

CREATE TABLE so.QuestionTaggedTag
(
    QUESTION_ID INTEGER NOT NULL,
    TAG_ID INTEGER NOT NULL,
    CONSTRAINT QuestionTaggedTag_PK PRIMARY KEY(QUESTION_ID, TAG_ID)
);

CREATE TABLE so.Answer
(
    ANSWER_ID SERIAL NOT NULL,
    BODY CHARACTER VARYING NOT NULL,
    USER_ID INTEGER NOT NULL,
    QUESTION_ID INTEGER NOT NULL,
    CONSTRAINT Answer_PK PRIMARY KEY(ANSWER_ID)
);

ALTER TABLE so.Question 
    ADD CONSTRAINT Question_FK FOREIGN KEY (USER_ID) 
    REFERENCES so."User" (USER_ID) ON DELETE RESTRICT ON UPDATE RESTRICT;

ALTER TABLE so.QuestionTaggedTag 
    ADD CONSTRAINT QuestionTaggedTag_FK1 FOREIGN KEY (QUESTION_ID) 
    REFERENCES so.Question (QUESTION_ID) ON DELETE RESTRICT ON UPDATE RESTRICT;

ALTER TABLE so.QuestionTaggedTag 
    ADD CONSTRAINT QuestionTaggedTag_FK2 FOREIGN KEY (TAG_ID) 
    REFERENCES so.Tag (TAG_ID) ON DELETE RESTRICT ON UPDATE RESTRICT;

ALTER TABLE so.Answer 
    ADD CONSTRAINT Answer_FK1 FOREIGN KEY (USER_ID) 
    REFERENCES so."User" (USER_ID) ON DELETE RESTRICT ON UPDATE RESTRICT;

ALTER TABLE so.Answer 
    ADD CONSTRAINT Answer_FK2 FOREIGN KEY (QUESTION_ID) 
    REFERENCES so.Question (QUESTION_ID) ON DELETE RESTRICT ON UPDATE RESTRICT;

COMMIT WORK;

Обратите внимание, что теперь есть отдельная таблица тегов с TAG_ID в качестве первичного ключа. TAG_NAME это отдельный столбец с ограничением уникальности, предотвращающим дублирование тегов. QuestionTaggedTag таблица сейчас есть (QUESTION_ID, TAG_ID), который также является его первичным ключом.

Надеюсь, я не зашел слишком далеко, чтобы ответить на этот вопрос, но когда я пытался писать меньшие ответы, мне приходилось распутывать свои предыдущие ответы, и было проще просто опубликовать это.

Вы можете создать уникальное ограничение для (question_id, tag_name) в таблице тегов, что обеспечит уникальность пары. Это означает, что к одному и тому же вопросу не может быть прикреплен один и тот же тег более одного раза. Тем не менее, один и тот же тег может применяться к разным вопросам.

Вы не можете создать два первичных ключа, но вы можете наложить ограничение уникальности на индекс.

У вас может быть только один первичный ключ (я полагаю, это то, что вы подразумеваете под "закрытым" ключом), но этот ключ может быть составным ключом, состоящим из идентификатора вопроса и имени тега. В SQL это будет выглядеть (в зависимости от вашего диалекта SQL):

CREATE TABLE Tags
(
  question_id int,
  tag_name varchar(xxx),
  PRIMARY KEY (question_id, tag_name)
);

Это гарантирует, что вы не можете иметь один и тот же тег на один и тот же вопрос.

Я буду использовать PostgreSQL или Oracle.

Я чувствую, что следующее соответствует коду Кена, который предназначен для MySQL.

CREATE TABLE Tags 
     (
         QUESTION_ID integer FOREIGN KEY REFERENCES Questions(QUESTION_ID) 
                             CHECK (QUESTION_ID>0), 
         TAG_NAME nvarchar(20) NOT NULL,
         CONSTRAINT no_duplicate_tag UNIQUE (QUESTION_ID,TAG_NAME)
     )

Я добавил несколько дополнительных мер в запрос. Например, CHECK (USER_ID>0) чтобы убедиться, что нет поврежденных данных в базе данных.

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

Я вижу, что нам нужно дать имя для ограничения. Его имя no_duplicate_tag в команде.

Другие вопросы по тегам