信息架构中 referential_constraints.unique_constraint_* 列的 NULL 值
NULL values for referential_constraints.unique_constraint_* columns in information schema
在 Postgres 10 中,我声明了以下内容:
create table test_abc (
pk integer not null,
id integer not NULL,
id2 integer not null,
PRIMARY KEY (pk)
);
CREATE UNIQUE INDEX test_abc_ids ON test_abc(id,id2);
然后是第二个 table,FK 引用第一个:
create table test_def (
id integer not null,
abc_id integer,
abc_id2 integer,
PRIMARY KEY (id),
FOREIGN KEY (abc_id,abc_id2) references test_abc(id,id2)
);
现在考虑这个查询的输出:
SELECT unique_constraint_catalog, unique_constraint_schema, unique_constraint_name
FROM information_schema.referential_constraints r
WHERE r.constraint_name = 'test_def_abc_id_fkey'
----------------------
NULL NULL NULL
所有 unique_constraint_*
列都有空值。
从 Postgres documentation 看来,这些元列应该包含
name of the [object] that contains the unique or primary key constraint that the foreign key constraint references (always the current database)
问题:
我肯定在同一个数据库中,并且在 test_abc
table 上声明的唯一索引是一个唯一约束(否则我将无法声明 FK 开始),那么为什么这些列为空?
我正在使用 referential_constraints
和一些连接来获取有关我的外键引用的列的信息,但这样我就错过了所有使用索引设置唯一约束的列。
测试设置
您假定约束名称 test_def_abc_id_fkey
,这是您在 Postgres 11 或更早版本中设置的默认名称。不过,值得注意的是,默认名称已针对 Postgres 12 进行了改进,其中相同的设置会导致 test_def_abc_id_abc_id2_fkey
。 The release notes for Postgres 12:
Use all key columns' names when selecting default constraint names for foreign keys (Peter Eisentraut)
Previously, only the first column name was included in the constraint name, resulting in ambiguity for multi-column foreign keys.
参见:
db<>fiddle here
所以让我们为 FK 约束使用显式名称 test_def_abc_fkey
以避免混淆:
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)
);
还有那个works in Postgres 9.5 - Postgres 12.
Even in 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
。
解释似乎很简单。正如手册所述,这些列描述:
... the unique or primary key constraint that the foreign key constraint references
但是没有UNIQUE
constraint, just a UNIQUE
index。 UNIQUE
约束是使用 Postgres 中的 UNIQUE
索引实现的。约束由 SQL 标准定义,索引是实现细节。有像你发现的那样的差异。相关:
- How does PostgreSQL enforce the UNIQUE constraint / what type of index does it use?
具有实际 UNIQUE
约束 的相同测试显示数据符合预期:
db<>fiddle here
所以这似乎是有道理的。特别是因为 information schema 也是由 SQL 标准委员会定义的,索引没有标准化,只有约束。 (信息模式视图中没有索引信息。)
都清楚了吗?不完全是。
然而
还有另一个信息模式视图key_column_usage
。它的最后一列被描述为:
position_in_unique_constraint
... For a foreign-key constraint, ordinal position of the referenced column within its unique constraint (count starts at 1); otherwise null
粗体 强调我的。在这里,列在索引中的顺序位置无论如何都列出了:
SELECT *
FROM information_schema.key_column_usage
WHERE constraint_name = 'test_def_abc_fkey';
参见:
db<>fiddle here
似乎不一致。
更糟糕的是,the manual 声称创建 FOREIGN KEY
约束需要实际的 PRIMARY KEY
或 UNIQUE
约束:
A foreign key must reference columns that either are a primary key or
form a unique constraint. This means that the referenced columns
always have an index (the one underlying the primary key or unique
constraint); so checks on whether a referencing row has a match will
be efficient.
似乎是文档错误?如果没有人能指出我哪里出错了,我会提交错误报告。
相关:
- Postgres unique constraint vs index
解决方案
I'm using the referential_constraints
with some joins to get information about the columns referenced by my foreign keys, but this way I'm missing all those where the unique constraint is set with an index.
在 Postgres 中,系统目录是真实的真实来源。参见:
所以你可以使用这样的东西(就像我在上面的 fiddle 中添加的一样):
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';
Returns:
conname | fk_table | fk_columns | ref_table | ref_key_columns
:---------------- | :------- | :--------------- | :-------- | :--------------
test_def_abc_fkey | test_def | {abc_id,abc_id2} | test_abc | {id,id2}
相关:
- Find the referenced table name using table, field and schema name
- Find referenced field(s) of foreign key constraint
- How can I find tables which reference a particular row via a foreign key?
在 Postgres 10 中,我声明了以下内容:
create table test_abc (
pk integer not null,
id integer not NULL,
id2 integer not null,
PRIMARY KEY (pk)
);
CREATE UNIQUE INDEX test_abc_ids ON test_abc(id,id2);
然后是第二个 table,FK 引用第一个:
create table test_def (
id integer not null,
abc_id integer,
abc_id2 integer,
PRIMARY KEY (id),
FOREIGN KEY (abc_id,abc_id2) references test_abc(id,id2)
);
现在考虑这个查询的输出:
SELECT unique_constraint_catalog, unique_constraint_schema, unique_constraint_name
FROM information_schema.referential_constraints r
WHERE r.constraint_name = 'test_def_abc_id_fkey'
----------------------
NULL NULL NULL
所有 unique_constraint_*
列都有空值。
从 Postgres documentation 看来,这些元列应该包含
name of the [object] that contains the unique or primary key constraint that the foreign key constraint references (always the current database)
问题:
我肯定在同一个数据库中,并且在 test_abc
table 上声明的唯一索引是一个唯一约束(否则我将无法声明 FK 开始),那么为什么这些列为空?
我正在使用 referential_constraints
和一些连接来获取有关我的外键引用的列的信息,但这样我就错过了所有使用索引设置唯一约束的列。
测试设置
您假定约束名称 test_def_abc_id_fkey
,这是您在 Postgres 11 或更早版本中设置的默认名称。不过,值得注意的是,默认名称已针对 Postgres 12 进行了改进,其中相同的设置会导致 test_def_abc_id_abc_id2_fkey
。 The release notes for Postgres 12:
Use all key columns' names when selecting default constraint names for foreign keys (Peter Eisentraut)
Previously, only the first column name was included in the constraint name, resulting in ambiguity for multi-column foreign keys.
参见:
db<>fiddle here
所以让我们为 FK 约束使用显式名称 test_def_abc_fkey
以避免混淆:
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)
);
还有那个works in Postgres 9.5 - Postgres 12.
Even in 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
。
解释似乎很简单。正如手册所述,这些列描述:
... the unique or primary key constraint that the foreign key constraint references
但是没有UNIQUE
constraint, just a UNIQUE
index。 UNIQUE
约束是使用 Postgres 中的 UNIQUE
索引实现的。约束由 SQL 标准定义,索引是实现细节。有像你发现的那样的差异。相关:
- How does PostgreSQL enforce the UNIQUE constraint / what type of index does it use?
具有实际 UNIQUE
约束 的相同测试显示数据符合预期:
db<>fiddle here
所以这似乎是有道理的。特别是因为 information schema 也是由 SQL 标准委员会定义的,索引没有标准化,只有约束。 (信息模式视图中没有索引信息。)
都清楚了吗?不完全是。
然而
还有另一个信息模式视图key_column_usage
。它的最后一列被描述为:
position_in_unique_constraint
... For a foreign-key constraint, ordinal position of the referenced column within its unique constraint (count starts at 1); otherwise null
粗体 强调我的。在这里,列在索引中的顺序位置无论如何都列出了:
SELECT *
FROM information_schema.key_column_usage
WHERE constraint_name = 'test_def_abc_fkey';
参见:
db<>fiddle here
似乎不一致。
更糟糕的是,the manual 声称创建 FOREIGN KEY
约束需要实际的 PRIMARY KEY
或 UNIQUE
约束:
A foreign key must reference columns that either are a primary key or form a unique constraint. This means that the referenced columns always have an index (the one underlying the primary key or unique constraint); so checks on whether a referencing row has a match will be efficient.
似乎是文档错误?如果没有人能指出我哪里出错了,我会提交错误报告。
相关:
- Postgres unique constraint vs index
解决方案
I'm using the
referential_constraints
with some joins to get information about the columns referenced by my foreign keys, but this way I'm missing all those where the unique constraint is set with an index.
在 Postgres 中,系统目录是真实的真实来源。参见:
所以你可以使用这样的东西(就像我在上面的 fiddle 中添加的一样):
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';
Returns:
conname | fk_table | fk_columns | ref_table | ref_key_columns :---------------- | :------- | :--------------- | :-------- | :-------------- test_def_abc_fkey | test_def | {abc_id,abc_id2} | test_abc | {id,id2}
相关:
- Find the referenced table name using table, field and schema name
- Find referenced field(s) of foreign key constraint
- How can I find tables which reference a particular row via a foreign key?