SQL Foreign Key
FOREIGN KEY — в одной таблице указывает на другой PRIMARY KEY.
Посмотрите на две следующие таблицы:
Есть таблица «Persons»:
P_Id | LastName | FirstName | Address | City |
---|---|---|---|---|
1 | Hansen | Ola | Timoteivn 10 | Sandnes |
2 | Svendson | Tove | Borgvn 23 | Sandnes |
3 | Pettersen | Kari | Storgt 20 | Stavanger |
Есть таблица «Orders»:
O_Id | OrderNo | P_Id |
---|---|---|
1 | 77895 | 3 |
2 | 44678 | 3 |
3 | 22456 | 2 |
4 | 24562 | 1 |
Заметим что колонка «P_Id» в таблице «Orders» указывает на «P_Id» в таблице «Persons».
Колонка «P_Id» в таблице «Persons» является PRIMARY KEY.
Колонка «P_Id» в таблице «Orders» является FOREIGN KEY.
FOREIGN KEY не позволяет вставлять неверные данные в колонку «P_Id» в таблице «Orders» и «Persons».
SQL FOREIGN KEY Ограничения при CREATE TABLE
Следующие SQL создает FOREIGN KEY в колонке «P_Id», при создании таблицы «Orders»:
MySQL:
SQL Server / Oracle / MS Access:
Можно распределить ограничитель FOREIGN KEY на несколько столбцов, для этого используйте следующий синтаксис SQL:
MySQL / SQL Server / Oracle / MS Access:
SQL FOREIGN KEY Ограничения при ALTER TABLE
Следующие SQL создает FOREIGN KEY в колонке «P_Id», когда таблица «Orders» уже создан:
MySQL / SQL Server / Oracle / MS Access:
Можно распределить ограничитель FOREIGN KEY на несколько столбцов, для этого используйте следующий синтаксис SQL:
MySQL / SQL Server / Oracle / MS Access:
Удаление FOREIGN KEY
Для удаления ограничителя FOREIGN KEY используйте следующий SQL:
MySQL:
SQL Server / Oracle / MS Access:
dimonchik.com
1.2.5. Первичный и внешний ключ
Вот так вот незаметно мы подошли к очень важной теме – первичных и внешних ключей. Если первые используются почти всеми, то вторые почему-то игнорируются. А зря. Внешние ключи – это не проблема, это реальная помощь в целостности данных.
1.2.5. Первичный ключ
Мы уже достаточно много говорили про ключевые поля, но ни разу их не использовали. Самое интересное, что все работало. Это преимущество, а может недостаток базы данных Microsoft SQL Server и MS Access. В таблицах Paradox такой трюк не пройдет и без наличия ключевого поля таблица будет доступна только для чтения.
В какой-то степени ключи являются ограничениями, и их можно было рассматривать вместе с оператором CHECK, потому что объявление происходит схожим образом и даже используется оператор CONSTRAINT. Давайте посмотрим на этот процесс на примере. Для этого создадим таблицу из двух полей «guid» и «vcName». При этом поле «guid» устанавливается как первичный ключ:
CREATE TABLE Globally_Unique_Data ( guid uniqueidentifier DEFAULT NEWID(), vcName varchar(50), CONSTRAINT PK_guid PRIMARY KEY (Guid) )
Самое вкусное здесь это строка CONSTRAINT. Как мы знаем, после этого ключевого слова идет название ограничения, и объявления ключа не является исключением. Для именования первичного ключа, я рекомендую использовать именование типа PK_имя, где имя – это имя поля, которое должно стать главным ключом. Сокращение PK происходит от Primary Key (первичный ключ).
После этого, вместо ключевого слова CHECK, которое мы использовали в ограничениях, стоит оператор PRIMARY KEY, Именно это указывает на то, что нам необходима не проверка, а первичный ключ. В скобках указывается одно, или несколько полей, которые будут составлять ключ.
Помните, что в ключевом поле не может быть одинакового значения у двух строк, в этом ограничение первичного ключа идентично ограничению уникальности. Это значит, что если сделать поле для хранения фамилии первичным ключом, то в такую таблицу нельзя будет записать двух Ивановых с разными именами. Это нарушает ограничение первичного ключа. Именно поэтому ключи являются ограничениями и объявляются также как и ограничение CHECK. Но это не верно только для первичных ключей и вторичных с уникальностью.
В данном примере, в качестве первичного ключа выступает поле типа uniqueidentifier (GUID). Значение по умолчанию для этого поля – результат выполнения серверной процедуры NEWID.
Внимание
Только один первичный ключ может быть создан для таблицы
Для простоты примеров, в качестве ключа желательно использовать численный тип и если позволяет база данных, то будет лучше, если он будет типа «autoincrement» (автоматически увеличивающееся/уменьшающееся число). В MS SQL Server таким полем является IDENTITY, а в MS Access это поле типа «счетчик».
Следующий пример показывает, как создать таблицу товаров, в которой в качестве первичного ключа выступает целочисленное поле с автоматическим увеличением:
CREATE TABLE Товары ( id int IDENTITY(1, 1), товар varchar(50), Цена money, Количество numeric(10, 2), CONSTRAINT PK_id PRIMARY KEY (id) )
Именно такой тип ключа мы будем использовать чаще всего, потому что в ключевом поле будут храниться легкие для восприятия числа и с ними проще и нагляднее работать.
Первичный ключ может состоять из более, чем одной колонки. Следующий пример создает таблицу, в которой поля «id» и «Товар» образуют первичный ключ, а значит, будет создан индекс уникальности на оба поля:
CREATE TABLE Товары1 ( id int IDENTITY(1, 1), Товар varchar(50), Цена money, Количество numeric(10, 2), CONSTRAINT PK_id PRIMARY KEY (id, [Название товара]) )
Очень часто программисты создают базу данных с ключевым полем в виде целого числа, но при этом в задаче четко стоит, что определенные поля должны быть уникальными. А почему не создать сразу первичный ключ из тех полей, которые должны быть уникальны и не надо будет создавать отдельные решения для данной проблемы.
Единственный недостаток первичного ключа из нескольких колонок – проблемы создания связей. Тут приходиться выкручиваться различными методами, но проблема все же решаема. Достаточно только ввести поле типа uniqueidentifier и производить связь по нему. Да, в этом случае у нас получаются уникальными первичный ключ и поле типа uniqueidentifier, но эта избыточность в результате не будет больше, чем та же таблица, где первичный ключ uniqueidentifier, а на поля, которые должны быть уникальными установлено ограничение уникальности. Что выбрать? Зависит от конкретной задачи и от того, с чем вам удобнее работать.
1.2.6. Внешний ключ
Внешний ключ также является ограничением CONSTRAINT и отображает связь между двумя таблицами. Допустим, что у вас есть две таблицы:
- Names – содержит имена людей и состоит из полей идентификатора (ключевое поле), имя.
- Phones – таблица телефонов, которая состоит из идентификатора (ключевое поле), внешний ключ для связи с таблицей names и строковое поле для хранения номера телефона.
У одного человека может быть несколько телефонов, поэтому мы разделили хранение данных в разные таблицы. На рисунке 1.4 визуально показана связь между двумя таблицами. Если вы уже работали со связанными таблицами, то этого для вас будет достаточно. Если вы слышите о связях впервые, то попробуем посмотреть на проблему поближе.
Рис. 1.4. Связь между таблицами
Для примера возьмем таблицу из трех человек. В таблице 1.3 показано содержимое таблицы «Names». Здесь всего три строки и у каждой свой уникальный главный ключ. Для уникальности, когда будем создавать таблицу, сделаем ключ автоматически увеличиваемым полем.
Таблица 1.3 Содержимое таблицы Names
Главный ключ | Фамилия |
1 | Петров |
2 | Иванов |
3 | Сидоров |
Таблица 1.4. Содержимое таблицы Phones
Главный ключ | Внешний ключ | Телефон |
1 | 1 | 678689687 |
2 | 1 | 2324234 |
3 | 2 | 324234 |
4 | 3 | 32432423 |
5 | 3 | 2 |
6 | 3 | 12312312 |
В таблице 1.4 находится пять номеров телефонов. В поле главный ключ также уникальный главный ключ, которой также можно сделать автоматически увеличиваемым. Вторичный ключ – это связь с главным ключом таблицы Names. Как работает эта связь? У Петрова в таблице Names в качестве главного ключа стоит число 1. В таблице Phones во вторичном ключе ищем число 1 и получаем номера телефонов Петрова. То же самое и с остальными записями. Визуально связь можно увидеть на рисунке 1.5.
Рис. 1.5. Связь между строками табли
Такое хранение данных очень удобно. Если бы не было возможности создавать связанные таблицы, то в таблице Names пришлось бы забивать все номера телефонов в одно поле. Это неудобно с точки зрения использования, поддержки и поиска данных.
Можно создать в таблице несколько полей Names, но возникает вопрос – сколько. У одного человека может быть только 1 телефон, а у меня, например, их 3, не считая рабочих. Большое количество полей приводит к избыточности данных.
Можно для каждого телефона в таблице Names заводить отдельную строку с фамилией, но это легко только для такого простого примера, когда нужно вводить только фамилию и легко можно внести несколько записей для Петрова с несколькими номерами телефонов. А если полей будет 10 или 20? Итак, создание двух таблиц связанных внешним ключом можно увидеть в листинге 1.6.
Листинг 1.6. Создание таблиц связанных внешним ключом
CREATE TABLE Names ( idName int IDENTITY(1,1), vcName varchar(50), CONSTRAINT PK_guid PRIMARY KEY (idName), ) CREATE TABLE Phones ( idPhone int IDENTITY(1,1), idName int, vcPhone varchar(10), CONSTRAINT PK_idPhone PRIMARY KEY (idPhone), CONSTRAINT FK_idName FOREIGN KEY (idName) REFERENCES Names (idName) )
Внимательно изучите содержимое листинга. Он достаточно интересен, потому что использует некоторые операторы, которые мы уже рассмотрели и дополнительный пример не помешает. Для обеих таблиц создается ключевое поле, которое стоит первым, имеет тип int и автоматически увеличивается, начиная с 1 с приращением в единицу. Ключевое поле делается главным ключом с помощью ограничение CONSTRAINT.
В описании таблицы Phones последняя строка содержит новое для нас объявление, а именно – объявление внешнего ключа с помощью оператора FOREIGN KEY. Как видите, это тоже ограничение и чуть позже вы увидите почему. В скобках указывается поле таблицы, которое должно быть связано с другой таблицей. После этого идет ключевое слово REFERENCES (ссылка), имя таблицы, с которой должна быть связь (Names) и в скобках имя поля («idName»). Таким образом, мы навели связь, которая отображена на рисунке 1.4.
Внимание!
Внешний ключ может ссылаться только на первичный ключ другой таблицы или на ограничение уникальности. Это значит, что после ключевого слова REFERENCES должно быть имя таблицы и в скобках можно указывать только первичный ключ или поле с ограничением UNIQUE. Другие поля указывать нельзя.
Теперь, если можно наполнять таблицы данными. Следующие три команды добавляют три фамилии, которые мы видели в таблице 1.3:
INSERT INTO Names(vcName) VALUES('Петров') INSERT INTO Names(vcName) VALUES('Иванов') INSERT INTO Names(vcName) VALUES('Сидоров')
Если вы уже работали с SQL то сможете добавить записи и для таблицы телефонов. Я опущу эти команды, а вы можете увидеть их в файле foreign_keys.sql директории Chapter1 на компакт диске.
Наша задача сейчас увидеть, в чем заключаются ограничительные действия внешнего ключа, давайте разберемся. Мы указали явную связь между двумя полями в разных таблицах. Если попытаться добавить в таблицу телефонов запись с идентификатором в поле «idName», не существующим в одноименном поле (имя можно было сделать и другим) таблице с фамилиями, то произойдет ошибка. Это нарушит связь между двумя таблицами, а ограничение внешнего ключа не позволит существовать записям без связи.
Ограничение действует и при изменении или удалении записей. Например, если попытаться удалить строку с фамилией Петров, то произойдет ошибка ограничения внешнего ключа. Нельзя удалять записи, для которых существуют внешне связанные строки. Для начала, нужно удалить все телефоны для данной записи и только после этого будет возможно удаление самой строки с фамилией Петров.
Во время создания внешнего ключа, можно указать ON DELETE CASCADE или ON UPDATE CASCADE. В этом случае, если удалить запись Петрова из таблице Names или изменить идентификатор, то все записи в таблице Phones, связанные со строкой Петрова будут автоматически обновлены. Никогда. Нет, нужно написать большими буквами: НИКОГДА не делайте этого. Все должно удаляться или изменяться вручную. Если пользователь случайно удалит запись из таблицы Names, то удаляться и соответствующие телефоны. Смысл тогда создавать внешний ключ, если половина его ограничительных возможностей исчезает! Все необходимо делать только вручную, а идентификаторы изменять не рекомендуется вообще никогда.
Удаление самих таблиц также должно начинаться с подчиненной таблицы, то есть с Phones, и только потом можно удалить главную таблицу Names.
Напоследок покажу, как красиво получить соответствие имен и телефонов из двух таблиц:
SELECT vcName, vcPhone FROM Names, Phones WHERE Names.idName=Phones.idName
Более подробно о подобных запросах мы поговорим в главе 2. Сейчас же я привел пример только для того, чтобы вы увидели мощь связанных таблиц.
Таблица может содержать до 253 внешних ключей, что вполне достаточно даже для построения самых сложных баз данных. Лично мне приходилось работать с базами данных, где количество внешних ключей не превышало 7 на одну таблицу. Если больше, то скорей всего база данных спроектирована неверно, хотя бывают и исключения.
Сама таблица также может иметь максимум 253 внешних ключей. Внешние ключи в таблице встречаются реже, в основном не более 3. Чаще всего в таблице может быть много ссылок на другие таблицы.
Внешний ключ может ссылаться на ту же таблицу, в которой он создается. Например, у вас есть таблица должностей в организации, как показано в таблице 1.5. Таблица состоит из трех полей: первичный ключ, внешний ключ и наименование должности. В любой организации может быть множество должностей, но вполне логичным будет в одной таблице отобразить их названия и структуру подчинения. Для этого внешний ключ нужно связать с первичным ключом таблицы должностей.
Таблица 1.5. Таблица с внутренней связью
Главный ключ | Внешний ключ | Должность |
1 | NULL | Генеральный директор |
2 | 1 | Коммерческий директор |
3 | 1 | Директор по общим вопросам |
4 | 2 | Начальник отдела снабжения |
5 | 2 | Начальник отдела сбыта |
6 | 3 | Начальник отдела кадров |
В результате мы получаем, что у генерального директора внешний ключ нулевой, т.е. эта должность стоит во главе всех остальных. У коммерческого директора и директора по общим вопросам внешний ключ указывает на строку генерального директора. Это значит, что эти две должности подчиняются непосредственно генеральному директору. И так далее.
Посмотрим, как можно создать все это в виде SQL запроса:
CREATE TABLE Positions ( idPosition int IDENTITY(1,1), idParentPosition int, vcName varchar(30), CONSTRAINT PK_idPosition PRIMARY KEY (idPosition), CONSTRAINT FK_idParentPosition FOREIGN KEY (idParentPosition) REFERENCES Positions (idPosition) )
Как видите, внешний ключ просто ссылается на ту же таблицу, которую мы создаем. На компакт диске, в директории Chapter1 можно увидеть в файле foreign_keys_to_self.sql пример создания этой таблицы, наполнения его данными и отображения должностей с учетом их подчинения. В следующей главе мы рассмотрим возможность работы с такими таблицами более подробно.
Отношение один к одному
Пока что мы рассмотрели классическую связь, когда одной строке основной таблицы данных соответствует одна строка из связанной таблицы. Такая связь называется один ко многим. Но существуют и другие связи, и сейчас мы рассмотрим еще одну – один к одному, когда одна запись основной таблице связана с одной записью другой. Чтобы это реализовать, достаточно связать первичные ключи обеих таблиц. Так как первичные ключи не могут повторяться, то в обеих таблицах связанными могут быть только одна строка.
Следующий пример создает две таблицы, у которых создана связь между первичными ключами:
CREATE TABLE Names ( idName uniqueidentifier DEFAULT NEWID(), vcName varchar(50), CONSTRAINT PK_guid PRIMARY KEY (idName) ) CREATE TABLE Phones ( idPhone uniqueidentifier DEFAULT NEWID(), vcPhone varchar(10), CONSTRAINT PK_idPhone PRIMARY KEY (idPhone), CONSTRAINT FK_idPhone FOREIGN KEY (idPhone) REFERENCES Names (idName) )
Внешний ключ нужен только у одной из таблиц. Так как связь идет один к одному, то не имеет значения, в какой таблице создать его.
Многие ко многим
Самая сложная связь – многие ко многим, когда много записей из одной таблицы соответствует многим записям из другой таблицы. Чтобы такое реализовать, двух таблиц мало, необходимо три таблицы.
Для начала нужно понять, когда может использоваться связь многие ко многим? Допустим, что у вас есть две таблицы: список жителей дома и список номеров телефона. В одной квартире может быть более одного номера, а значит, одной фамилии может принадлежать два телефона. Получается, связь один ко многим. С другой стороны, в одной квартире может быть две семьи (коммунальная квартира или просто квартиросъемщик, который пользуется телефоном владельца), а значит, связь между телефоном и жителем тоже один ко многим. И самый сложный вариант – в коммунальной квартире находиться два телефона. В этом случае обоими номерами пользуются несколько жителей квартире. Вот и получается, что «много» семей может пользоваться «многими» телефонами (связь многие ко многим).
Как реализовать связь многие ко многим? На первый взгляд, в реляционной модели это невозможно. Лет 10 назад я долго искал разные варианты и в результате просто создавал одну таблицу, которая была переполнена избыточностью данных. Но однажды, мне досталась одна задача, благодаря которой уже из условия на поверхность вышло отличное решение – нужно создать две таблицы жителей квартир и телефонов и реализовать в них только первичный ключ. Внешние ключи в этой таблице не нужны. А вот связь между таблицами должна быть через третью, связующую таблицу. На первый взгляд это сложно и не понятно, но один раз разобравшись с этим методом, вы увидите всю мощь этого решения.
В таблицах 1.6 и 1.7 показаны примеры таблиц фамилий и телефонов соответственно. А в таблице 1.8 показана связующая таблица.
Таблица 1.6. Таблица фамилий
Ключ | Имя |
1 | Иванов |
2 | Петров |
3 | Сидоров |
Таблица 1.7. Таблица телефонов
Ключ | Телефон |
1 | 68768678 |
2 | 658756785 |
3 | 567575677 |
Таблица 1.8. Таблица телефонов
Ключ | Связь с именем | Связь с телефоном |
1 | 1 | 1 |
2 | 1 | 2 |
3 | 2 | 1 |
4 | 2 | 3 |
5 | 3 | 3 |
Давайте теперь посмотрим, какая будет логика поиска данных при связи многие ко многим. Допустим, что нам нужно найти все телефоны, которые принадлежат Иванову. У Иванова первичный ключ равен 1. Находим в связующей таблице все записи, у которых поле «Связь с именем» равно 1. Это будут записи 1 и 2. В этих записях в поле «Связь с телефоном» находятся идентификаторы 1 и 2 соответственно, а значит, Иванову принадлежат номера из таблицы телефонов, которые расположены в строках 1 и 2.
Теперь решим обратную задачу – определим, кто имеет доступ к номеру телефона 567575677. Этот номер в таблице телефонов имеет ключ 3. Ищем все записи в связующей таблице, где в поле «Связь с телефоном» равно 3. Это записи с номерами 4 и 5, которые в поле «Связь с именем» содержат значения 2 и 3 соответственно. Если теперь посмотреть на таблицу фамилий, то вы увидите под номерами 2 и 3 Петрова и Сидорова. Значит, именно эти два жителя пользуются телефоном с номером 567575677.
Просмотрите все три таблицы и убедитесь, что вы поняли, какие номера телефонов принадлежат каким жителям и наоборот. Если вы увидите эту связь, то поймете, что она проста, как три копейки и сможете быстро реализовать ее в своих проектах.
CREATE TABLE Names ( idName uniqueidentifier DEFAULT NEWID(), vcName varchar(50), CONSTRAINT PK_guid PRIMARY KEY (idName) ) CREATE TABLE Phones ( idPhone uniqueidentifier DEFAULT NEWID(), vcPhone varchar(10), CONSTRAINT PK_idPhone PRIMARY KEY (idPhone) ) CREATE TABLE LinkTable ( idLinkTable uniqueidentifier DEFAULT NEWID(), idName uniqueidentifier, idPhone uniqueidentifier, CONSTRAINT PK_idLinkTable PRIMARY KEY (idLinkTable), CONSTRAINT FK_idPhone FOREIGN KEY (idPhone) REFERENCES Phones (idPhone), CONSTRAINT FK_idName FOREIGN KEY (idName) REFERENCES Names (idName) )
У связующей таблицы два внешних ключа, которые связываются с таблицами имен и телефонов и один первичный ключ, который обеспечивает уникальность записей.
В качестве первичного ключа я выбрал GUID поле, потому что для решения именно этой задачи он более удобен. Дело в том, что нам нужно вставлять записи в две таблицы и в обоих случаях нужно указывать один и тот же ключ. Значение GUID можно сгенерировать, а потом можно использовать при вставке данных в обе таблицы.
Вы можете использовать в качестве ключа и автоматически увеличиваемое поле, но в этом случае проблему решить немного сложнее, точнее сказать, решать проблему неудобно. Например, добавляя номер телефона, необходимо сначала вставить соответствующую строку в таблицу, потом найти ее, определить ключ, который был назначен строке, и после этого уже наводить связь.
На данном этапе мы ограничиваемся только созданием таблиц, а в разделе 2.8 мы вернемся к этой теме и научимся и научимся работать со связанными таблицами. Работа со связью один к одному и один ко многим отличается не сильно, потому что в этой схеме участвует только две таблицы. Связь многие ко многим немного сложнее из-за связующей таблицы, поэтому мы ее рассмотрим отдельно в разделе 2.27.
www.flenov.info
FOREIGN KEY, видео урок 13
Внешние ключи (FK) это
Внешние ключи (FK) реляционной базы данных это столбец, а может сочетание столбцов, используемые для принудительного установления связи между данными в двух таблицах. Внешний ключ можно создать, определив ограничение FOREIGN KEY при создании или изменении таблицы.
Внешний ключ и родительский ключ
Когда все значения в одном поле таблицы представлены в поле другой таблицы, мы говорим, что первое поле ссылается на второе. Это указывает на прямую связь между значениями двух полей.
Когда одно поле в таблице ссылается на другое, оно называется — внешним ключом, а поле, на которое оно ссылается, называется родительским ключом.
Имена внешнего ключа и родительского ключа не обязательно должны быть одинаковыми, это только соглашение, которому мы следуем, чтобы делать соединение более понятным.
Многостолбцовые внешние ключи
В действительности, внешний ключ не обязательно состоит только из одного поля. Подобно первичному ключу, внешний ключ может иметь любое число полей, которые все обрабатываются как единый модуль.
Смысл внешнего и родительского ключей
Когда поле является внешним ключом, оно определенным образом связано с таблицей, на которую он ссылается. Каждое значение в этом поле (внешнем ключе) непосредственно привязано к значению в другом поле (первичном ключе).
Каждое значение (каждая строка) внешнего ключа должно недвусмысленно ссылаться к одному и только этому значению (строке) родительского (первичного) ключа. Если это так, то фактически ваша система, как говорится, будет в состоянии справочной целостности.
Понятно, что каждое значение во внешнем ключе должно быть представлено один, и только один раз, в родительском ключе.
Видео урок: Внешние ключи SQL
Полезные ссылки
Учебник по базам данных тут: http://www.sql.ru/docs/sql/u_sql/index.shtml
Все видео уроки SQL
Похожие статьи:
- Представления SQL, урок 17
- Нормальные формы SQL, урок 16
- Подзапросы SQL, урок 15 — вложенные запросы
- Нужные SQL приемы, SQL, урок 12
- Функции для работы с числами SQL, урок 11
- 5 Видеоурок, Команда SQL SELECT
- Соединения SQL, урок 14 — соединение таблиц в одном запросе
- SQL ALTER TABLE — sql запрос на модификацию таблицы базы данных
- Строковые функции SQL, УРОК 9.
- 6 Видео Урок, команды DELETE и UPDATE, удалять и обновлять записи, языка SQL
- Урок 3, Установка MySQL
- Лекция о языке SQL
- Введение в SQL, видео урок 1
- Урок 7. Понятие нормализации в теории БД
(Просмотров всего: 226)
Поделиться ссылкой:
webonto.ru
.net — Как найти зависимости внешнего ключа в SQL Server?
После длительного поиска я нашел рабочее решение. Моя база данных не использует sys.foreign_key_columns, а информация_schema.key_column_usage содержит только первичные ключи.
Я использую SQL Server 2015
РЕШЕНИЕ 1 (редко используется)
Если другие решения не работают, это будет работать нормально:
WITH CTE AS
(
SELECT
TAB.schema_id,
TAB.name,
COL.name AS COLNAME,
COl.is_identity
FROM
sys.tables TAB INNER JOIN sys.columns COL
ON TAB.object_id = COL.object_id
)
SELECT
DB_NAME() AS [Database],
SCHEMA_NAME(Child.schema_id) AS 'Schema',
Child.name AS 'ChildTable',
Child.COLNAME AS 'ChildColumn',
Parent.name AS 'ParentTable',
Parent.COLNAME AS 'ParentColumn'
FROM
cte Child INNER JOIN CTE Parent
ON
Child.COLNAME=Parent.COLNAME AND
Child.name<>Parent.name AND
Child.is_identity+1=Parent.is_identity
РЕШЕНИЕ 2 (обычно используется)
В большинстве случаев это будет очень хорошо:
SELECT
DB_NAME() AS [Database],
SCHEMA_NAME(fk.schema_id) AS 'Schema',
fk.name 'Name',
tp.name 'ParentTable',
cp.name 'ParentColumn',
cp.column_id,
tr.name 'ChildTable',
cr.name 'ChildColumn',
cr.column_id
FROM
sys.foreign_keys fk
INNER JOIN
sys.tables tp ON fk.parent_object_id = tp.object_id
INNER JOIN
sys.tables tr ON fk.referenced_object_id = tr.object_id
INNER JOIN
sys.foreign_key_columns fkc ON fkc.constraint_object_id = fk.object_id
INNER JOIN
sys.columns cp ON fkc.parent_column_id = cp.column_id AND fkc.parent_object_id = cp.object_id
INNER JOIN
sys.columns cr ON fkc.referenced_column_id = cr.column_id AND fkc.referenced_object_id = cr.object_id
WHERE
-- CONCAT(SCHEMA_NAME(fk.schema_id), '.', tp.name, '.', cp.name) LIKE '%my_table_name%' OR
-- CONCAT(SCHEMA_NAME(fk.schema_id), '.', tr.name, '.', cr.name) LIKE '%my_table_name%'
ORDER BY
tp.name, cp.column_id
qaru.site
sql — Создание составного внешнего ключа в SQL Server 2008
Здесь есть хорошие ответы, но я хотел бы сделать это еще дальше — ради потомства.
Внешний ключ должен ссылаться на столбец Primary key
(уникальный, кластерный индекс) или столбец Unique
с ограничениями в другой таблице. В принципе, необходимым компонентом является ограничение Unique
. Я бы добавил, что у вас могут быть столбцы с нулевым значением в вашем внешнем ключе, НО, если вы разрешаете nulls в «составном» ключе, SQL пропускает проверку данных в отношении внешнего ключа. Это важный момент, который следует помнить, поскольку основная причина, по которой большинство из нас использует внешние ключи, — обеспечить целостность данных в наших базах данных.
В заключительном примечании я хотел бы явно объявить все мои ключевые имена. Почему, спросите вы? Если вам нужно использовать «полнотекстовое индексирование» в будущем для улучшения возможностей поиска, это не означает, что вы ссылаетесь на все «автогенерируемые» имена ключей. Это не может быть большой проблемой для небольших проектов, для которых не требуется преобразование данных или запланированные полнотекстовые обновления индексов, но если вы создаете сценарий этой функции, вы можете сделать вашу работу более сложной (например, нужно искать фактическое имя вашего основного Ключевое имя по умолчанию: pk_someTable_1248594832828495904
).
Вот что я сделал бы в написании SQL, чтобы избежать любых будущих ошибок:
- Не разрешайте NULL на составные внешние ключи, если это возможно.
- Явные имена явно используют согласованное соглашение об именах (например,
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 есть хорошая ссылка, обсуждающая это и многое другое по внешним ключам: Ограничение внешнего ключа.
qaru.site
sql — Внешние ключи TSQL для просмотров?
Мне потребовалось некоторое время, чтобы разобраться с непониманием здесь — не уверен, полностью ли я понимаю, но вот оно. Я буду использовать пример, близкий к вашему, но с некоторыми данными — мне легче думать в этих терминах.Итак, первые две таблицы; A = Отдел B = Сотрудник
CREATE TABLE Department
(
DepartmentID int PRIMARY KEY
,DepartmentName varchar(20)
,DepartmentColor varchar(10)
)
GO
CREATE TABLE Employee
(
EmployeeID int PRIMARY KEY
,EmployeeName varchar(20)
,DepartmentID int FOREIGN KEY REFERENCES Department ( DepartmentID )
)
GO
Теперь я подброшу некоторые данные в
INSERT INTO Department
( DepartmentID, DepartmentName, DepartmentColor )
SELECT 1, 'Accounting', 'RED' UNION
SELECT 2, 'Engineering', 'BLUE' UNION
SELECT 3, 'Sales', 'YELLOW' UNION
SELECT 4, 'Marketing', 'GREEN' ;
INSERT INTO Employee
( EmployeeID, EmployeeName, DepartmentID )
SELECT 1, 'Lyne', 1 UNION
SELECT 2, 'Damir', 2 UNION
SELECT 3, 'Sandy', 2 UNION
SELECT 4, 'Steve', 3 UNION
SELECT 5, 'Brian', 3 UNION
SELECT 6, 'Susan', 3 UNION
SELECT 7, 'Joe', 4 ;
Итак, теперь я создам представление для первой таблицы, чтобы отфильтровать некоторые отделы.
CREATE VIEW dbo.BlueDepartments
AS
SELECT * FROM dbo.Department
WHERE DepartmentColor = 'BLUE'
GO
Это возвращает
DepartmentID DepartmentName DepartmentColor
------------ -------------------- ---------------
2 Engineering BLUE
И в вашем примере я добавлю представление для второй таблицы, которая ничего не фильтрует.
CREATE VIEW dbo.AllEmployees
AS
SELECT * FROM dbo.Employee
GO
Это возвращает
EmployeeID EmployeeName DepartmentID
----------- -------------------- ------------
1 Lyne 1
2 Damir 2
3 Sandy 2
4 Steve 3
5 Brian 3
6 Susan 3
7 Joe 4
Мне кажется, что вы думаете, что Employee No 5, DepartmentID = 3 указывает на никуда?
«В итоге вы получите записи в вторая точка зрения нигде не указана.»
Ну, это указывает на таблицу Department
DepartmentID = 3
, как указано с помощью внешнего ключа. Даже если вы попытаетесь присоединиться к представлению в представлении, ничего не сломается:
SELECT e.EmployeeID
,e.EmployeeName
,d.DepartmentID
,d.DepartmentName
,d.DepartmentColor
FROM dbo.AllEmployees AS e
JOIN dbo.BlueDepartments AS d ON d.DepartmentID = e.DepartmentID
ORDER BY e.EmployeeID
Возвращает
EmployeeID EmployeeName DepartmentID DepartmentName DepartmentColor
----------- -------------------- ------------ -------------------- ---------------
2 Damir 2 Engineering BLUE
3 Sandy 2 Engineering BLUE
Итак, здесь ничего не сломалось, соединение просто не нашло совпадающих записей для DepartmentID <> 2
Это на самом деле то же самое, что если бы я присоединил таблицы, а затем включил фильтр как в первом представлении:
SELECT e.EmployeeID
,e.EmployeeName
,d.DepartmentID
,d.DepartmentName
,d.DepartmentColor
FROM dbo.Employee AS e
JOIN dbo.Department AS d ON d.DepartmentID = e.DepartmentID
WHERE d.DepartmentColor = 'BLUE'
ORDER BY e.EmployeeID
Возврат снова:
EmployeeID EmployeeName DepartmentID DepartmentName DepartmentColor
----------- -------------------- ------------ -------------------- ---------------
2 Damir 2 Engineering BLUE
3 Sandy 2 Engineering BLUE
В обоих случаях объединения не терпят неудачу, они просто делают так, как ожидалось.
Теперь я попытаюсь сломать ссылочную целостность через представление (нет DepartmentID = 127)
INSERT INTO dbo.AllEmployees
( EmployeeID, EmployeeName, DepartmentID )
VALUES( 10, 'Bob', 127 )
И это приводит к:
Msg 547, Level 16, State 0, Line 1
The INSERT statement conflicted with the FOREIGN KEY constraint "FK__Employee__Depart__0519C6AF". The conflict occurred in database "Tinker_2", table "dbo.Department", column 'DepartmentID'.
Если я попытаюсь удалить отдел через представление
DELETE FROM dbo.BlueDepartments
WHERE DepartmentID = 2
Результат:
Msg 547, Level 16, State 0, Line 1
The DELETE statement conflicted with the REFERENCE constraint "FK__Employee__Depart__0519C6AF". The conflict occurred in database "Tinker_2", table "dbo.Employee", column 'DepartmentID'.
Так что ограничения для базовых таблиц все еще применяются.
Надеюсь, что это поможет, но тогда, возможно, я неправильно понял вашу проблему.
qaru.site
sql — Внешний ключ, используемый в составном первичном ключе
Спасибо за чтение.
Можно ли использовать составной внешний ключ в качестве части основного составного первичного ключа таблицы?
Например, скажем, у меня есть две таблицы:
CREATE TABLE DB.dbo.Partners
(
CONSTRAINT pk_Partners_Id
PRIMARY KEY (Name, City, State, Country, PostalCode),
Name VARCHAR(100)
NOT NULL,
Address1 VARCHAR(100),
Address2 VARCHAR(100),
Address3 VARCHAR(100),
City VARCHAR(150)
NOT NULL,
State CHAR(2)
NOT NULL,
Country CHAR(2)
NOT NULL,
PostalCode VARCHAR(16)
NOT NULL,
Phone VARCHAR(20),
Fax VARCHAR(20),
Email VARCHAR(256)
)
… а затем во второй таблице я хотел бы ссылаться на внешний ключ во втором первичном ключе таблицы:
CREATE TABLE DB.dbo.PartnerContacts
(
CONSTRAINT pk_PartnerContacts_Id
PRIMARY KEY (fk_PartnerContacts_PartnerId, FirstName, LastName, PhoneNumber, Email),
CONSTRAINT fk_PartnerContacts_PartnerId
FOREIGN KEY REFERENCES Partners(Name, City, State, Country, PostalCode),
FirstName VARCHAR(75)
NOT NULL,
MiddleName VARCHAR(75),
LastName VARCHAR(75)
NOT NULL,
PhoneNumber VARCHAR(20)
NOT NULL,
MobileNumber VARCHAR(20),
FaxNumber VARCHAR(20),
Email VARCHAR(256)
NOT NULL,
MailTo VARCHAR(100),
Address1 VARCHAR(100),
Address2 VARCHAR(100),
Address3 VARCHAR(100),
City VARCHAR(150),
State CHAR(2),
Country CHAR(2),
PostalCode VARCHAR(16)
)
Есть ли способ, которым я могу это сделать? Да, проще было бы просто использовать столбцы IDENTITY в этих таблицах, но если я могу определить фактические отношения без IDENTITY, я бы хотел сделать это.
EDIT:
Я хотел предоставить окончательный рабочий SQL. Спасибо всем, кто ответил!
CREATE TABLE DB.dbo.Partners
(
CONSTRAINT pk_Partners_Id
PRIMARY KEY (Name, City, State, Country, PostalCode),
Id INT
NOT NULL
UNIQUE
IDENTITY(1, 1),
Name VARCHAR(100)
NOT NULL,
Address1 VARCHAR(100),
Address2 VARCHAR(100),
Address3 VARCHAR(100),
City VARCHAR(150)
NOT NULL,
State CHAR(2)
NOT NULL,
Country CHAR(2)
NOT NULL,
PostalCode VARCHAR(16)
NOT NULL,
Phone VARCHAR(20),
Fax VARCHAR(20),
Email VARCHAR(256)
)
CREATE TABLE DB.dbo.PartnerContacts
(
CONSTRAINT pk_PartnerContacts_Id
PRIMARY KEY
(PartnerId, FirstName, LastName, PhoneNumber, Email),
PartnerId INT
NOT NULL
CONSTRAINT fk_PartnerContacts_PartnerId
FOREIGN KEY REFERENCES Partners(Id),
FirstName VARCHAR(75)
NOT NULL,
MiddleName VARCHAR(75),
LastName VARCHAR(75)
NOT NULL,
PhoneNumber VARCHAR(20)
NOT NULL,
MobileNumber VARCHAR(20),
FaxNumber VARCHAR(20),
Email VARCHAR(256)
NOT NULL,
MailTo VARCHAR(100),
Address1 VARCHAR(100),
Address2 VARCHAR(100),
Address3 VARCHAR(100),
City VARCHAR(150),
State CHAR(2),
Country CHAR(2),
PostalCode VARCHAR(16)
)
Спасибо:)
qaru.site