Спасибо за все ответы и комментарии.Эта проблема заставила меня узнать что-то новое, и это хорошо.@philipxy дал мне большую подсказку.Я хотел бы повторить то, что я узнал, так как это, вероятно, будет полезно кому-то еще, и это хорошее место, чтобы записать это.
Этот вопрос имеет две стороны: во-первых, какой частично нулевой внешний ключ означает , а во-вторых, как это реализовано .
Значение частично нулевых внешних ключей
Существует много споров о том, чтоэто значит - как указывает @ agiles231.NULL
может означать:
- значение неизвестно .
- другие говорят, что это означает, что неверно .
- другие говорят, что
NULL
- это истинное значение как таковое.
Короче говоря, пока нет однозначного ответа на его значение.
Я полагаю, в зависимости от того, как люди интерпретируют нулевые значения, затем стратегию их использования во внешних ключах (идля проверки их) может быть другим.
Реализация частично нулевых внешних ключей
Стандарт SQL-92 определяет (раздел 4.10.2)три различных способа сопоставления составных внешних ключей со значениями, допускающими значение NULL:
Match SIMPLE : если какой-либо столбец составного внешнего ключа имеет значение NULL, тогда внешний ключ принимается, сохранено, но не проверено по указанной таблице.Обычно это предложение баз данных в режиме по умолчанию.В стандарте SQL-92 этот режим описан, но не назван.
Match PARTIAL : если какой-либо столбец составного внешнего ключа имеет значение null, то каждый неСтолбец NULL сопоставляется с указанной таблицей, чтобы проверить наличие хотя бы одной строки, в которой присутствует значение.Я не видел ни одной базы данных, реализующей этот режим.
Match FULL : частично нулевые внешние ключи не принимаются.Либо внешний ключ является полностью нулевым, либо полностью не нулевым.Когда ноль, нет проверки по ссылочной таблице.Когда не ноль, это полностью проверено относительно ссылочной таблицы.Это то, что я ожидал как поведение по умолчанию (в моем блаженном невежестве).
Хорошо, я проверил, как 10 различных баз данных реализовали эти режимы, и вот что я нашел:
Database Engine Match SIMPLE Match PARTIAL Match FULL
--------------- ------------ ------------- ----------
Oracle 12c1 YES*1 NO NO
DB2 10.5 YES*1 NO NO
PostgreSQL 10 YES*1 NO YES
SQL Server 2014 YES*1 NO NO
MariaDB 10.3 YES*1 NO*2 NO*2
MySQL 8.0 YES*1 NO*2 NO*2
Sybase ASE 16 YES*1 NO YES
H2 1.4 YES*1 NO NO
Derby 10.13 YES*1 NO NO
HyperSQL 2.3 YES*1 NO YES
* 1 Это режим по умолчанию.
* 2 Принимается при создании таблицы, но игнорируется.
Короче:
Все протестированные базы данных по умолчанию ведут себя одинаково: по умолчанию они соответствуют SIMPLE.
Ни одна из протестированных баз данных не поддерживает Match PARTIAL.Я думаю, что это имеет смысл, так как я лично мало пользуюсь этим.Кроме того, может оказаться чрезмерно дорогостоящим выполнение частичной проверки для отдельных столбцов внешнего ключа без создания всех возможных комбинаций индексов в ссылочной таблице.
PostgreSQL реализует Match FULL, а также Sybase ASE.Это прекрасные новости!Удивительно, но HyperSQL (эта крошечная база данных) тоже делает это.
Обходной путь для реализации Match FULL
Хорошая новость заключается в том, что существует довольно простой обходной путь для реализацииПодберите FULL, если вам это нужно, в любой из протестированных баз данных.Просто добавьте ограничение таблицы, которое допускает либо все пустые столбцы, либо все ненулевые.Что-то вроде:
create table farm (
id int,
region_id int,
area_id int,
name varchar(50),
constraint fk_region_area foreign key (region_id, area_id)
references quadrant (region, area),
constraint fkfull_region_area check ( -- here's the workaround
region_id is null and area_id is null or
region_id is not null and area_id is not null)
);
insert into farm (id, region_id, area_id, name) values (5, 10, null, 'farm 1'); -- fails
insert into farm (id, region_id, area_id, name) values (6, 11, null, 'farm 2'); -- fails
insert into farm (id, region_id, area_id, name) values (7, 10, 125, 'farm 3'); -- succeeds
insert into farm (id, region_id, area_id, name) values (8, null, null, 'farm 4'); -- succeeds
Работает довольно аккуратно.
Наконец, и, как очень личное мнение, я бы ожидал, что Match FULL будет стратегией сопоставления по умолчанию.Может быть, просто для меня использование (по умолчанию) внешних ключей, которые не указывают на другие строки, вызывает ошибки в приложениях, использующих базу данных.
Я думаю, что большинство разработчиков поймут ПОЛНОСТЬЮ легко, по сравнению с ПРОСТОЙ.И ЧАСТИЧНЫЙ способ намного сложнее и потенциально подвержен ошибкам.Просто мое мнение.