给定这样的结构:
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
我想写一个查询,告诉我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;
结果将类似于:
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
这类似于但不是Postgres:SQL列出表外键的副本。这个问题说明了表的结构。我想找到特定的实例。
本质上,如果我愿意的话DELETE FROM reference_table WHERE reference_value IS NULL;,postgres必须在内部做一些事情以弄清楚它需要将reference_table_key第2行设置other_table为NULL。我想看看这些行将是什么。
DELETE FROM reference_table WHERE reference_value IS NULL;
reference_table_key
other_table
有查询可以做到这一点吗?有没有可以传递给DELETE调用的修饰符,该修饰符可以告诉我该DELETE将影响哪些表/行/列?
该查询生成DML语句以查找 所有 表中的 所有行 ,其中一列具有 引用另一个表 但NULL在该列中保留值的外键约束:
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;
产生这样的查询:
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;
产生如下输出:
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
不能可靠地覆盖多列外键或主键 。您必须为此使查询更复杂。
我投所有主键 的值 ,以text涵盖所有类型。
text
调整或删除这些行,以找到指向另一个或 任何 列/表的外键:
AND c.confrelid = 'fk_tbl'::regclass
AND f.attname = ‘fk_tbl_id’ – and only this column
已在PostgreSQL 9.1.4中测试。我用pg_catalog桌子。实际上,我在这里使用的所有内容都不会改变,但是在主要版本中不能保证这一点。information_schema如果您需要它在更新之间可靠地工作,请使用表格将其重写。那比较慢,但是可以肯定。
pg_catalog
information_schema
我没有在生成的DML脚本中清除表名,因为quote_ident()使用模式限定名将失败。避免使用有害的表名是您的责任"users; DELETE * FROM users;"。付出更多的努力,您可以分别检索schema-name和table name并使用quote_ident()。
quote_ident()
"users; DELETE * FROM users;"
我的第一个解决方案与您的要求有一些细微的差别,因为您所描述的(根据我的理解)不存在。该值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;
查找整个数据库中的所有此类行(注释了对一个表的限制)。经过Postgres 9.1.4的测试,可以为我工作。
我将多个引用同一外来列的表归为一个查询,并添加了一个引用表列表以进行概述。