Тестовая настройка
Вы принимаете имя ограничения test_def_abc_id_fkey
, имя по умолчанию, полученное в результате вашей настройки в Postgres 11 или более ранней версии. Стоит отметить, однако, что имена по умолчанию были улучшены для Postgres 12, где такая же настройка приводит к test_def_abc_id_abc_id2_fkey
. Примечания к выпуску для Postgres 12:
Использовать имена всех ключевых столбцов при выборе имен ограничений по умолчанию для внешних ключей (Питер Эйзентраут)
Ранее в имя ограничения было включено только имя первого столбца, что приводило к неоднозначности для внешних ключей из нескольких столбцов.
См .:
db <> fiddle здесь
Итак, давайте используем явное имя test_def_abc_fkey
для ограничения FK, чтобы избежать путаницы:
CREATE TABLE test_abc (
pk int PRIMARY KEY
, id int NOT NULL
, id2 int NOT NULL
);
CREATE UNIQUE INDEX test_abc_ids ON test_abc(id,id2);
CREATE TABLE test_def (
id int PRIMARY KEY
, abc_id int
, abc_id2 int
, CONSTRAINT test_def_abc_fkey -- !
FOREIGN KEY (abc_id,abc_id2) REFERENCES test_abc(id,id2)
);
И что работает в Postgres 9,5 - Postgres 12.
Даже в Postgres 9,3.
(у меня было неверное впечатление, фактическое потребуется ограничение .)
Ответ
Ваше наблюдение по запросу информационной схемы имеет вид:
SELECT *
FROM information_schema.referential_constraints
WHERE constraint_name = 'test_def_abc_fkey'; -- unequivocal name
Мы получаем строку, но три поля unique_constraint_catalog
, unique_constraint_schema
и unique_constraint_name
равны NULL
.
Объяснение кажется простым. Эти столбцы описывают, как сказано в руководстве:
... ограничение уникального или первичного ключа, на которое ссылается ограничение внешнего ключа
Но нет UNIQUE
ограничение , просто UNIQUE
индекс . Ограничение UNIQUE
реализовано с использованием индекса UNIQUE
в Postgres. Ограничения определены стандартом SQL, индексы - это детали реализации. Есть различия, подобные той, которую вы обнаружили. Связанный:
Тот же тест с фактическим UNIQUE
ограничение показывает данные, как и ожидалось:
дБ <> fiddle здесь
Так что это, кажется, имеет смысл , Тем более что информационная схема также определена комитетом по стандартам SQL, а индексы не стандартизированы, только ограничения. (Нет информации об индексе в представлениях информационной схемы.)
Все ясно? Не совсем.
Однако
Существует еще одно представление информационной схемы key_column_usage
. Его последний столбец описывается следующим образом:
position_in_unique_constraint
... Для ограничения по внешнему ключу - порядковый номер ссылочного столбца в его уникальном ограничении (отсчет начинается с 1 ); в противном случае null
Жирный выделение мое. Здесь в любом случае указывается порядковый номер столбца в index :
SELECT *
FROM information_schema.key_column_usage
WHERE constraint_name = 'test_def_abc_fkey';
См .:
db <> fiddle here
Кажется несовместимым.
Что еще хуже, руководство утверждает, что для создания фактического ограничения PRIMARY KEY
или UNIQUE
потребуется FOREIGN KEY
ограничение:
Внешний ключ должен ссылаться на столбцы, которые либо являются первичным ключом, либо образуют уникальное ограничение. Это означает, что указанные столбцы всегда имеют индекс (тот, который лежит в основе первичного ключа или ограничения уникальности); поэтому проверка на наличие совпадения в строке ссылок будет эффективной.
Кажется, это ошибка документации ? Если никто не может указать, где я иду не так, я отправлю отчет об ошибке.
Связанный:
Решение
Я использую referential_constraints
с некоторыми объединениями для получения информации о столбцах, на которые ссылаются мои внешние ключи, но таким образом я пропускаю все те, где ограничение уникальности установлено с индексом.
В Postgres системный каталог является фактическим источником правды. См .:
Таким образом, вы можете использовать что-то вроде этого (как я также добавил в скрипку выше):
SELECT c.conname
, c.conrelid::regclass AS fk_table, k1.fk_columns
, c.confrelid::regclass AS ref_table, k2.ref_key_columns
FROM pg_catalog.pg_constraint c
LEFT JOIN LATERAL (
SELECT ARRAY (
SELECT a.attname
FROM pg_catalog.pg_attribute a
, unnest(c.conkey) WITH ORDINALITY AS k(attnum, ord)
WHERE a.attrelid = c.conrelid
AND a.attnum = k.attnum
ORDER BY k.ord
) AS fk_columns
) k1 ON true
LEFT JOIN LATERAL (
SELECT ARRAY (
SELECT a.attname
FROM pg_catalog.pg_attribute a
, unnest(c.confkey) WITH ORDINALITY AS k(attnum, ord)
WHERE a.attrelid = c.confrelid
AND a.attnum = k.attnum
ORDER BY k.ord
) AS ref_key_columns
) k2 ON true
WHERE conname = 'test_def_abc_fkey';
Возвращает:
conname | fk_table | fk_columns | ref_table | ref_key_columns
:---------------- | :------- | :--------------- | :-------- | :--------------
test_def_abc_fkey | test_def | {abc_id,abc_id2} | test_abc | {id,id2}
Связано: