Создание составного внешнего ключа в SQL Server 2008 - PullRequest
26 голосов
/ 11 июля 2011

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

Первичная таблица

PK - Key1 - varchar(20)
PK - Key2 - date

Вторичная таблица

PK - AutoID
FK - Key1 - varchar(20)
FK - Key2 - date

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

Столбцы в Первичной таблице не соответствуют первичному ключу или уникальному ограничение.

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

Есть какие-нибудь мысли о том, как мне установить отношение внешнего ключа между этими двумя таблицами?

Ответы [ 5 ]

45 голосов
/ 11 июля 2011

Внешний ключ ДОЛЖЕН ссылаться на столбцы, которые составляют уникальный индекс (PK или UK) с таким же количеством столбцов, их типами и порядком.Например:

CREATE TABLE PrimaryTable (
  Key1 varchar(20),
  Key2 date)
GO

ALTER TABLE PrimaryTable ADD CONSTRAINT PK
  PRIMARY KEY (Key1, Key2)
GO

CREATE TABLE SecondaryTable (
  AutoID int IDENTITY,
  Key1 varchar(20),
  Key2 date)
GO

ALTER TABLE SecondaryTable ADD CONSTRAINT FK
  FOREIGN KEY (Key1, Key2) REFERENCES PrimaryTable (Key1, Key2)
GO
13 голосов
/ 11 июля 2011

Часть этого сфокусирована, часть - контекст для других, имеющих какие-либо проблемы, подобные этой (как кто-то на самом деле ищет в первую очередь?)

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

Следующая проблема - данные. Если вы не можете создать ключ из-за данных, вы должны выяснить, что существует в дочерней таблице, а чего нет в родительской таблице. ВЛЕВО СОЕДИНЯЕТСЯ к таблицам (вторичные со второй / левой стороны соединения) и включают только те строки, у которых первичная таблица пуста. Вам придется либо создать эти записи в родительской таблице, либо избавиться от них.

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

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

11 голосов
/ 11 июля 2011

Это будет работать:

CREATE TABLE PTable (
     Key1 varchar(20) not null,
     Key2 date not null,
     constraint PK_PTable PRIMARY KEY (Key1,Key2)
)

CREATE TABLE STable (
     AutoID int IDENTITY(1,1) not null primary key,
     Key1 varchar(20) not null,
     Key2 date not null,
     constraint FK_STable_PTable FOREIGN KEY (Key1,Key2) references PTable (Key1,Key2)
)

Что вам нужно сделать, так это заставить Management Studio создать сценарий для ваших таблиц и сравнить их с приведенным выше.

7 голосов
/ 09 сентября 2012

Здесь есть несколько хороших ответов, но я бы хотел сделать еще один шаг - ради потомков.

Внешний ключ должен ссылаться либо на Primary key (уникальный, кластеризованный индекс), либо на Unique ограниченный столбец в другой таблице.По сути, необходимым компонентом является ограничение Unique.Я бы добавил, что вы можете иметь пустые столбцы в вашем внешнем ключе, НО, если вы разрешите пустые значения в «составном» ключе, SQL пропускает проверку данных в отношении внешнего ключа.Это важный момент, который следует помнить, поскольку основная причина, по которой большинство из нас использует внешние ключи, заключается в обеспечении целостности данных в наших базах данных.

В заключение я хотел бы явно объявить все мои имена ключей.Почему, спросите вы?Если в будущем вам потребуется использовать «Полнотекстовое индексирование» для улучшения возможностей поиска, это не заставит вас ссылаться на все «автоматически сгенерированные» имена ключей.Это может не иметь большого значения для небольших проектов, которые не требуют преобразования данных или запланированных обновлений полнотекстового индекса, но если вы создаете сценарий для этой функции, вы можете усложнить свою работу (например, искать фактическое имя вашего основного компьютера).Имя ключа по умолчанию: pk_someTable_1248594832828495904).

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

  1. Не разрешать пустые значения в составных внешних ключах, если это возможно.
  2. Назовите ключи явно, используя согласованное соглашение об именах (например, PK_Schema/56_TalbeName_Col1_Col2).Это не только дает вам стандартное имя для ключа, но и из индекса легко увидеть, на какие столбцы ссылаются и в каком порядке.

Код:

CREATE TABLE MySchema.PrimaryTable ( 
  Key1 varchar(20) NOT NULL, 
  Key2 date NOT NULL,
  CONSTRAINT PK_MySchema_PrimaryTable_Key1_Key2 PRIMARY KEY (Key1, Key2)
)
GO 

CREATE TABLE MySchema.SecondaryTable ( 
  AutoID int IDENTITY, 
  Key1 varchar(20) NOT NULL, 
  Key2 date NOT NULL,
  CONSTRAINT FK_MySchema_SecondaryTable_Key1_Key2
     FOREIGN KEY (Key1, Key2) REFERENCES PrimaryTable (Key1, Key2)
)
GO 

OptillectTeam в основном мертв с его ответом.Я просто хотел уточнить несколько важных вещей, которые не были упомянуты ранее.На сайте MSDN есть хорошая ссылка, обсуждающая это и многое другое на внешние ключи: Ограничение внешнего ключа .

1 голос
/ 02 июня 2016

ДЛЯ ДОБАВЛЕНИЯ УНИКАЛЬНОГО КЛЮЧЕВОГО ИСПОЛЬЗОВАНИЯ НИЖЕ ЗАПРОСА

   ALTER TABLE [TableName] ADD UNIQUE ([Column1], [Column2]);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...