Является ли одно поле несколькими связями внешнего ключа из другой таблицы [SQL] - PullRequest
2 голосов
/ 10 октября 2019

У меня есть 3 стола Student, Teacher, User.

Ученик:

CREATE TABLE Student( id INT NOT NULL PRIMARY KEY,name VARCHAR(50) NOT NULL);
INSERT INTO [dbo].[Student]([id],[name]) VALUES(4,'Ram'),(5,'Raman');

Учитель:

CREATE TABLE Teacher( id INT NOT NULL PRIMARY KEY,name VARCHAR(50) NOT NULL);
INSERT INTO [dbo].[Student]([id],[name]) VALUES(1,'Raj'),(2,'Rahul');

Пользователь:

CREATE TABLE [dbo].[User](
    id INT NOT NULL PRIMARY KEY,
    user_id INT NOT NULL,
    user_type CHAR(1) NOT NULL,
    user_name VARCHAR(10) NOT NULL,
    user_password VARCHAR(255) NOT NULL,

    CONSTRAINT  FOREIGN KEY (user_id) REFERENCES Student (id),
    CONSTRAINT  FOREIGN KEY (user_id) REFERENCES Teacher (id) );

Теперь я пытаюсь INSERT в User таблице с запросом ниже

INSERT INTO [dbo].[User] ([id] ,[user_id]  ,[user_type],[user_name] ,[user_password])   VALUES  (1 ,1,'S','Raj_001','********')

Это дает мне ошибку для нарушения внешнего ключа из-за значенияиз user_id доступно в Teacher, а не в Student

Поэтому мой вопрос: как я могу добиться, чтобы значение user_id присутствовало в одной из этих таблиц, и данные должны быть вставленыв User таблице.

Ответы [ 6 ]

3 голосов
/ 10 октября 2019

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

Было бы лучше иметь два столбца в вашей пользовательской таблице, один для идентификатора учителяи один для студента ID. Фактически, если пойти дальше, учитывая, что в таблицах учеников и учителей есть только дополнительные данные, то их имя, почему бы просто не исключить их и сохранить в таблице пользователей?

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

1 голос
/ 04 ноября 2019

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

Данные ученика / учителя часто бывают беспорядочными

Как человек с опытом нормализации наборов данных в высшем образованиипроблема, с которой вы столкнулись, нашла отклик у меня. Образовательные пользователи могут быть во всех трех категориях (студент, учитель и пользователь) или только в одной из них, в зависимости от того, как и почему категория была связана. Хуже того, они могут входить из нескольких направлений и в конечном итоге с несколькими несвязанными учетными записями. Более зрелые институты и инструменты имеют защиту от этого, но я все еще вижу созданные пользователями базы данных и десятилетние «временные» решения, которые причиняют мне экзистенциальную боль.

Главный камень преткновения

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

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

Одна таблица, чтобы управлять ими всеми

Прежде чем я продолжу, я хочу сказать, что можно получить все поля, которые вы отслеживаете. в одну таблицу, но наличие нескольких таблиц с различными целями - это простой способ защиты от изменений позже.

Таблица внешнего ключа должна наследовать ключ от другой таблицы, но люди часто говорят, что внешние ключи не могут бытьпервичные ключи также. Почему?

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

Мы исправляем это с помощью ограничения Unique. Применительно к полю внешнего ключа, Unique, по сути, заставляет его выступать в качестве первичного ключа.

Пример метода

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

/*Create Tables*/
CREATE TABLE ID(
USER_ID int NOT NULL PRIMARY KEY AUTO_INCREMENT,
USER_CREATED timestamp
);

CREATE TABLE USER(
USER_ID int NOT NULL UNIQUE FOREIGN KEY REFERENCES ID(USER_ID),
USER_LOGIN VARCHAR(10) NOT NULL UNIQUE,
USER_PASSWORD VARCHAR(255) NOT NULL,
USER_NAME VARCHAR(50) NOT NULL
);

CREATE TABLE PERMISSIONS(
USER_ID int NOT NULL UNIQUE FOREIGN KEY REFERENCES ID(USER_ID),
STUDENT CHAR(1),
TEACHER CHAR(1)
);

Это создает флаг для ученика и учителя, который может быть как истинным, так и ложным. Если вы хотите, чтобы код принудительно вводил их только в одно или другое, вы все равно можете сделать так, чтобы в таблице разрешений вместо поля USER_TYPE было указано поле. Я предлагаю нулевое или ни одно из возможных значений в любом случае, если вы планируете использовать это в течение любого промежутка времени. Желаем удачи.

1 голос
/ 03 ноября 2019

Прежде всего, избавьтесь от таких ключевых слов из названия таблицы, как [User], user_id и т. Д.

Это действительно проблематично и раздражает.

Во-вторых, почему 2 введите [User] стол, id, user_id? Это не требуется.

Я буду хранить только id или user_id.

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

Из таблицы [User] видно, что id и user_type равны composite primary key.

Так и должно быть. Если это так, тогда вы не можете определить FK constraint, так как user_type недоступен ни в таблице Teacher, ни в таблице Student.

И что, к примеру, выглядит, например, первые данныевставляется в Student или Teacher, затем данные вставляются в таблицу User в том же Transaction.

Таким образом, во всех вышеприведенных сценариях Instead of Trigger является идеальным сценарием в этом состоянии.

Мой сценарий просто демо,

Create Proc spStudentInsert
as
set nocount on
set xact_abort  on
begin try
begin tran

--bulk insert or single insert ,no problem
insert into Student

insert into [User]

if (@@Trancount>0)
commit
end try
begin catch
if (@@Trancount>0)
rollback
end catch

CREATE TRIGGER INSTEADOF_TR_I_User ON [user]
INSTEAD OF INSERT
AS
BEGIN
    DECLARE @Flag BIT = 1

    IF NOT EXISTS (
            SELECT 1
            FROM Student S
            INNER JOIN inserted i ON i.id = S.id
            )
        SET @Flag = 0
    ELSE IF NOT EXISTS (
            SELECT 1
            FROM Teacher T
            INNER JOIN inserted i ON i.id = T.id
            )
        AND @Flag = 1
        SET @Flag = 0

    IF (@Flag = 0)
    BEGIN
        RAISERROR (
                N'Invalid user'
                ,16
                ,1
                )

        RETURN
    END
END

В случае, если я ошибаюсь по поводу id, user_type composite PK, тогда вы можете сделать другой путь,

PK идентификатора пользователя FK в Studentстол, а также стол учителя. Кроме того, id - это PK в соответствующей таблице.

Итак, сначала вы вставляете в таблицу User, затем вставляете в таблицу Student или Teacher.

Таким образом, дизайн в этом случае будетбыть,

CREATE TABLE [dbo].[User](
    id INT NOT NULL ,

    user_type CHAR(1) NOT NULL,
    user_name VARCHAR(10) NOT NULL,
    user_password VARCHAR(255) NOT NULL,
    CONSTRAINT [PK_user] PRIMARY KEY (id)
    )
    INSERT INTO [dbo].[User] ([id] ,[user_type],[user_name] ,[user_password])   
    VALUES  (1 ,1,'S','Ram_001','********')
    --drop table [User]
    --alter table [user]
    -- drop constraint PK_user
CREATE TABLE Student( id INT NOT NULL PRIMARY KEY,name VARCHAR(50) NOT NULL);

ALTER TABLE Student
 add CONSTRAINT FK_StudentUser  FOREIGN KEY (id) REFERENCES [User] (id);

INSERT INTO [dbo].[Student]([id],[name]) VALUES(1,'Ram'),(5,'Raman');

--select * from [Student]

CREATE TABLE Teacher( id INT NOT NULL PRIMARY KEY,name VARCHAR(50) NOT NULL);

ALTER TABLE Teacher
 add CONSTRAINT FK_TeacherUser  FOREIGN KEY (id) REFERENCES [User] (id);

INSERT INTO [dbo].Teacher([id],[name]) VALUES(1,'Raj'),(2,'Rahul');

Так что, как явствует из вашего вопроса, я создам Instead of Trigger и пойду с этим model.

1 голос
/ 03 ноября 2019

ТАК, что вы хотите сообщить системе, что ваш пользователь должен находиться в одной из ваших таблиц. это невозможно в логике баз данных, но вы можете написать скрипт, который имеет условие (ЕСЛИ существует), а затем вставить ваши пользовательские данные

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

1 голос
/ 03 ноября 2019

У вашего определения внешнего ключа есть некоторые логические проблемы. Это заставляет user_id существовать в обеих таблицах. Решение здесь зависит от потребностей бизнеса и реальных данных.
Можно создать таблицу Person с отношением 1-1 к таблицам student и Teacher, а затем использовать столбец Person.Id в определении внешнего ключа. Это решение предполагает, что данные учеников и учителей могут меняться по-разному.
В качестве другого способа (который объясняется в других ответах), если данные вашего учащегося и учителя аналогичны, вы можете объединить обе таблицы и данные различий в одном добавленном столбце «Тип».

1 голос
/ 03 ноября 2019

Есть два способа сделать это без повторного выполнения схемы таблицы

  1. Создать 4-ую таблицу, которая содержит объединение идентификатора ученика и учителя. Предположительно, вы вставляете в эту таблицу всякий раз, когда вставляете в Ученика и Учителя, а затем накладываете ограничение на эту таблицу.

  2. Создайте ограничение на основе пользовательской функции, а не внешний ключ, которыйсмотрит на объединение таблиц ученика и учителя.

Ни одно из этих решений не является отличным / чистым, и, как уже отмечали другие, вы, вероятно, имеете дело с тем фактом, что схемане идеально

Тем не менее, если вы просто модифицируете существующую систему (и я предполагаю, что это упрощенная версия того, с чем вы на самом деле имеете дело), ​​то одно из двух упомянутых мной решений проще, чем повторение схемы. .

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