Fre*_*eit 4 sql postgresql foreign-keys
给定这样的结构:
CREATE TABLE reference_table (
reference_table_key numeric NOT NULL,
reference_value numeric,
CONSTRAINT reference_table_pk PRIMARY KEY (reference_table_key)
);
CREATE TABLE other_table (
other_table_key numeric NOT NULL,
reference_table_key numeric,
CONSTRAINT other_table_pk PRIMARY KEY (other_table_key),
ONSTRAINT other_table_reference_fk FOREIGN KEY (reference_table_key)
REFERENCES reference_table (reference_table_key) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE SET NULL
);
CREATE TABLE another_table (
another_table_key numeric NOT NULL,
do_stuff_key numeric,
CONSTRAINT another_table_pk PRIMARY KEY (another_table_key),
ONSTRAINT another_table_reference_fk FOREIGN KEY (do_stuff_key)
REFERENCES reference_table (reference_table_key) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE SET NULL
);
--there are 50-60 tables which have similar foreign key references to reference_table
Run Code Online (Sandbox Code Playgroud)
我想编写一个查询,告诉我other_table和another_table中的主键以及可能更多的表,其中reference_value为NULL.
在伪代码中:
SELECT table_name, table_primary_key, table_fk_column_name
FROM ?????? some PG table ???????, reference_table
WHERE reference_table.reference_value IS NULL;
Run Code Online (Sandbox Code Playgroud)
结果看起来像:
table_name | table_primary_key | table_fk_column_name | reference_table_pk
---------------------------------------------------------------------------
other_table | 2 | reference_table_key | 7
other_table | 4 | reference_table_key | 56
other_table | 45 | reference_table_key | 454
other_table | 65765 | reference_table_key | 987987
other_table | 11 | reference_table_key | 3213
another_table | 3 | do_stuff_key | 4645
another_table | 5 | do_stuff_key | 43546
another_table | 7 | do_stuff_key | 464356
unknown_table | 1 | unkown_column_key | 435435
unknown_table | 1 | some_other_column_key | 34543
unknown_table | 3 | unkown_column_key | 124
unknown_table | 3 | some_other_column_key | 123
Run Code Online (Sandbox Code Playgroud)
这与Postgres类似,但不重复:SQL列出表外键.那个问题显示了表结构.我想找到具体的实例.
基本上如果我DELETE FROM reference_table WHERE reference_value IS NULL;这样做,postgres必须在内部做一些事情来弄清楚它需要reference_table_key在第2行设置other_table为NULL.我想看看那些行是什么.
有查询可以做到这一点吗?是否有一个修饰符,我可以传递给DELETE调用,它会告诉我DELETE会影响哪些表/行/列?
此查询生成DML语句以查找所有表中的所有行,其中列具有引用另一个表但NULL在该列中保留值的外键约束:
WITH x AS (
SELECT c.conrelid::regclass AS tbl
, c.confrelid::regclass AS ftbl
, quote_ident(k.attname) AS fk
, quote_ident(pf.attname) AS pk
FROM pg_constraint c
JOIN pg_attribute k ON (k.attrelid, k.attnum) = (c.conrelid, c.conkey[1])
JOIN pg_attribute f ON (f.attrelid, f.attnum) = (c.confrelid, c.confkey[1])
LEFT JOIN pg_constraint p ON p.conrelid = c.conrelid AND p.contype = 'p'
LEFT JOIN pg_attribute pf ON (pf.attrelid, pf.attnum)
= (p.conrelid, p.conkey[1])
WHERE c.contype = 'f'
AND c.confrelid = 'fk_tbl'::regclass -- references to this tbl
AND f.attname = 'fk_tbl_id' -- and only to this column
)
SELECT string_agg(format(
'SELECT %L AS tbl
, %L AS pk
, %s::text AS pk_val
, %L AS fk
, %L AS ftbl
FROM %1$s WHERE %4$s IS NULL'
, tbl
, COALESCE(pk 'NONE')
, COALESCE(pk 'NULL')
, fk
, ftbl), '
UNION ALL
') || ';'
FROM x;
Run Code Online (Sandbox Code Playgroud)
生成这样的查询:
SELECT 'some_tbl' AS tbl
, 'some_tbl_id' AS pk
, some_tbl_id::text AS pk_val
, 'fk_tbl_id' AS fk
, 'fk_tbl' AS ftbl
FROM some_tbl WHERE fk_tbl_id IS NULL
UNION ALL
SELECT 'other_tbl' AS tbl
, 'other_tbl_id' AS pk
, other_tbl_id::text AS pk_val
, 'some_name_id' AS fk
, 'fk_tbl' AS ftbl
FROM other_tbl WHERE some_name_id IS NULL;
Run Code Online (Sandbox Code Playgroud)
产生这样的输出:
tbl | pk | pk_val | fk | ftbl
-----------+--------------+--------+--------------+--------
some_tbl | some_tbl_id | 49 | fk_tbl_id | fk_tbl
some_tbl | some_tbl_id | 58 | fk_tbl_id | fk_tbl
other_tbl | other_tbl_id | 66 | some_name_id | fk_tbl
other_tbl | other_tbl_id | 67 | some_name_id | fk_tbl
Run Code Online (Sandbox Code Playgroud)
不能可靠地覆盖多列外键或主键.您必须使查询更复杂.
我投所有主键的值,以text涵盖所有类型.
调整或删除这些行以查找指向其他列或任何列/表的外键:
AND c.confrelid = 'fk_tbl'::regclass
AND f.attname = 'fk_tbl_id' -- and only this column
Run Code Online (Sandbox Code Playgroud)使用PostgreSQL 9.1.4进行测试.我用这些pg_catalog表.实际上,我在这里使用的内容没有任何改变,但在主要版本中无法保证.information_schema如果需要它可以在更新中可靠地工作,请使用表重写它.这比较慢,但确定.
我没有在生成的DML脚本中清理表名,因为它quote_ident()会因模式限定名而失败.您有责任避免使用有害的表名"users; DELETE * FROM users;".通过更多努力,您可以单独检索模式名称和表名并使用quote_ident().
我的第一个解决方案与你提出的问题略有不同,因为你所描述的(我理解的)是不存在的.值为NULL"未知",无法引用.如果你真的想找到一个排NULL在具有FK约束指着一列值到它(不特定行与NULL价值,当然),那么查询可以大大简化:
WITH x AS (
SELECT c.confrelid::regclass AS ftbl
,quote_ident(f.attname) AS fk
,quote_ident(pf.attname) AS pk
,string_agg(c.conrelid::regclass::text, ', ') AS referencing_tbls
FROM pg_constraint c
JOIN pg_attribute f ON (f.attrelid, f.attnum) = (c.confrelid, c.confkey[1])
LEFT JOIN pg_constraint p ON p.conrelid = c.confrelid AND p.contype = 'p'
LEFT JOIN pg_attribute pf ON (pf.attrelid, pf.attnum)
= (p.conrelid, p.conkey[1])
WHERE c.contype = 'f'
-- AND c.confrelid = 'fk_tbl'::regclass -- only referring this tbl
GROUP BY 1, 2, 3
)
SELECT string_agg(format(
'SELECT %L AS ftbl
, %L AS pk
, %s::text AS pk_val
, %L AS fk
, %L AS referencing_tbls
FROM %1$s WHERE %4$s IS NULL'
, ftbl
, COALESCE(pk, 'NONE')
, COALESCE(pk, 'NULL')
, fk
, referencing_tbls), '
UNION ALL
') || ';'
FROM x;
Run Code Online (Sandbox Code Playgroud)
查找整个数据库中的所有此类行(将限制注释到一个表).使用Postgres 9.1.4进行测试并为我工作.
我将引用相同外部列的多个表分组到一个查询中,并添加一个引用表列表以提供概述.