Есть ли простой способ ссылаться на несколько первичных ключей в одном столбце таблицы назначения? - PullRequest
0 голосов
/ 17 апреля 2019

Я прошу прощения за отсутствие знаний или неправильное использование терминов;Я прохожу онлайн-курс по СУБД, и он в основном самообучается с Microsoft SQL Server.

Перед нами стоит задача создать структуру базы данных и вставить в нее данные для того, что нас интересует.Я решил создать базу данных на основе Dungeons and Dragons, и у меня возник вопрос, правильно ли я что-то делал.

Я намереваюсь создать таблицу Spell_Source, которая будет содержать первичный ключ нескольких различных таблиц (Class иПодкласс) как один столбец, а имя заклинания (первичный ключ в другой таблице) как другой.Однако, когда я иду на ввод данных, ограничения внешнего ключа останавливают вставку.

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

Спасибо за вашу помощь!

CREATE TABLE SPELL_SOURCE (
    SpellName       VarChar(50)     NOT NULL,
    SpellSource     Char(25)        NOT NULL,
    CONSTRAINT      SpellSourcePK1  PRIMARY KEY (SpellName, SpellSource),
    CONSTRAINT      SpellSourceFK   FOREIGN KEY (SpellName)
                        REFERENCES SPELLS(SpellName)
                            ON UPDATE NO ACTION
                            ON DELETE NO ACTION,
    CONSTRAINT      SpellSourceFK1  FOREIGN KEY (SpellSource)
                        REFERENCES CLASS(ClassName)
                            ON UPDATE NO ACTION
                            ON DELETE NO ACTION,
    CONSTRAINT      SpellSourceFK2  FOREIGN KEY (SpellSource)
                        REFERENCES SUBCLASS(SubclassName)
                            ON UPDATE NO ACTION
                            ON DELETE NO ACTION
);

Точная ошибка, которую я получаю от инструмента импорта данных: «Оператор INSERT конфликтует сограничение FOREIGN KEY "SpellSourceFK1". Конфликт произошел в таблице базы данных "dbo.CLASS", "ClassName" "

Ответы [ 2 ]

1 голос
/ 17 апреля 2019

Просто хотел поддержать мою поддержку ответом @ Olivier.Вы будете испытывать большое горе, пытаясь продвинуться вперед с вашим текущим дизайном.

Однако, создавая отношение 1 -> N между вашим подклассом и вашим классом, вы всегда будете поддерживать ссылочную целостность, и ваш класс будет легко запрашиваться через подкласс

1 голос
/ 17 апреля 2019

Проблема в том, что вы ввели значение для SpellSource, для которого нет соответствующего ClassName в таблице CLASS. Столбец может быть частью разных внешних ключей, если это разные составные ключи. Например. FK (a, b), FK (b, c), где b принадлежит 2 ФК. Но в остальном один столбец (как правило) должен иметь только один FK.

Кроме того, если у вас есть классы и подклассы, вы бы ссылались здесь только на подкласс и создаете отношение 1 к n между классом и подклассом. То есть подкласс будет иметь внешний ключ для ClassID

 SPELL_SOURCE                 SPELLS
+-------------  ---+         +---------------+
| PK FK SpellID    | o-----> | PK SpellID    |
| PK FK SubclassID | o--+    |    SpellName  |
+------------------+    |    +---------------+
                        |
                        |     SUBCLASS                    CLASS
                        |    +-----------------+         +---------------+
                        +--> | PK SubclassID   |    +--> | PK ClassID    |
                             |    SubclassName |    |    |    ClassName  |
                             | FK ClassID      | O--+    +---------------+
                             +-----------------+

Не используйте имена в качестве PK. Это будет очень трудно изменить имена позже. Вместо этого обращайтесь только к первичному ключу int IDENTITY(1,1) (автоинкремент), который никогда не изменяется, и сохраняйте имя в отдельном столбце, который вы можете редактировать в любое время. См .: CREATE TABLE (Transact-SQL) IDENTITY (Свойство) .

Вы можете запросить объединенную информацию с помощью

SELECT
    SS.SpellID, SS.SubclassID,
    S.SpellName,
    C.ClassName,
    SC.SubclassName, SC.ClassID
FROM
    SPELL_SOURCE SS
    INNER JOIN SPELLS S
        ON SS.SpellID = S.SpellID
    INNER JOIN SUBCLASS SC
        ON SS.SubclassID = SC.SubclassID
    INNER JOIN CLASS C
        ON SC.ClassID = C.ClassID
ORDER BY
    C.ClassName, SC.SubclassName, S.SpellName

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

Class   1 --> n   Subclass   1 --> n   Spell

Согласно вашим комментариям заклинание может принадлежать классу вместо подкласса (и косвенно также к классу). Тогда я бы предложил следующую структуру

 SPELL_SOURCE (separate PK because of nullables, Unique Constraint UC instead)
+------------------+              SPELLS
| PK SpellSourceID |             +---------------+
| FK UC SpellID    | o---------> | PK SpellID    |
| FK UC SubclassID | o------+    |    SpellName  |
| FK UC ClassID    | o--+   |    +---------------+
+------------------+    |   |
                        |   |     SUBCLASS                       CLASS
                        |   |    +-----------------+    +-----> +---------------+
                        |   +--> | PK SubclassID   |    |  +--> | PK ClassID    |
                        |        |    SubclassName |    |  |    |    ClassName  |
                        |        | FK ClassID      | o--+  |    +---------------+
                        |        +-----------------+       |
                        |                                  |
                        +----------------------------------+

Где в SPELL_SOURCE оба ClassID и SubclassID НУЖНЫ. Всегда только один из двух будет не нулевым. Вы можете добавить ограничение CHECK (ClassID IS NULL AND SubclassID IS NOT NULL) OR (ClassID IS NOT NULL AND SubclassID IS NULL). И используйте ЛЕВЫЕ СОЕДИНЕНИЯ в запросе.

См .: http://www.sqlfiddle.com/#!18/107dd/3/0


Еще один подход состоял бы в том, чтобы сохранить первую структуру, но иметь основную запись или запись подкласса по умолчанию в каждом классе. Например, подкласс, имеющий SubclassName = NULL. Эта запись будет репрезентативной для класса.

Для раскрывающихся списков вы можете выбрать записи, подобные этой

SELECT
    S.SubclassID,
    CASE WHEN S.SubclassName IS NULL THEN
        'CLASS: ' + C.ClassName
    ELSE
        S.SubclassName + ' (' + C.ClassName + ')'
    END AS Name
FROM
    CLASS C
    INNER JOIN SUBCLASS S
        ON C.ClassID = S.ClassID
ORDER BY
    C.ClassName, 
    CASE WHEN S.SubclassName IS NULL THEN 0 ELSE 1 END,
    S.SubclassName

См .: http://www.sqlfiddle.com/#!18/d8777/1/0

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...