gue*_*tli 3 postgresql foreign-key metadata postgresql-10
我有一个表my_tables
,它在几个表中被引用为外键。
我想选择my_table
其他表中未引用的所有行。
AFAIK,应该可以以通用方式(带有一些内省魔法)来做到这一点。
外键必须引用作为主键或形成唯一约束的列。
所以这不一定限于PK。但是如果我们从 开始pg_constraint
,我们会自动获得指向目标表的所有 FK 约束。无需提供任何键列 - 除非您想限制为某些 FK。
使用对象标识符类型regclass
和表别名,我们可以保持函数简短,结果安全无歧义:
SELECT format(E'SELECT * FROM %s t\nWHERE NOT EXISTS (SELECT FROM %s'
, c.confrelid::regclass
, string_agg(format('%s WHERE %s = %s)', c.conrelid::regclass, src.cols, tgt.cols)
, E'\nAND NOT EXISTS (SELECT FROM '))
FROM pg_constraint c
, cardinality(c.conkey) AS col_ct
, LATERAL (
SELECT concat(CASE WHEN col_ct > 1 THEN '(' END
, string_agg(quote_ident(attname), ', ' ORDER BY fld.ord) -- original order
, CASE WHEN col_ct > 1 THEN ')' END) AS cols
FROM unnest(c.conkey) WITH ORDINALITY fld(attnum, ord) -- possibly n cols
JOIN pg_catalog.pg_attribute a ON (a.attrelid, a.attnum) = (c.conrelid, fld.attnum)
) src
, LATERAL (
SELECT concat(CASE WHEN col_ct > 1 THEN '(' END -- parentheses for multiple columns
, string_agg('t.' || quote_ident(attname), ', t.' ORDER BY fld.ord)
, CASE WHEN col_ct > 1 THEN ')' END) AS cols
FROM unnest(c.confkey) WITH ORDINALITY fld(attnum, ord)
JOIN pg_catalog.pg_attribute a ON (a.attrelid, a.attnum) = (c.confrelid, fld.attnum)
) tgt
WHERE c.confrelid = 'my_table'::regclass -- target table name, optionally schema-qualified
AND c.contype = 'f' -- FK constraints
GROUP BY c.confrelid;
Run Code Online (Sandbox Code Playgroud)
生成以下形式的查询:
SELECT * FROM my_table t
WHERE NOT EXISTS (SELECT FROM schema1.tbl1 WHERE col1 = t.id)
AND NOT EXISTS (SELECT FROM "tB-l2" WHERE ("COL2", col3) = (t.col4, t.col5));
Run Code Online (Sandbox Code Playgroud)
它返回当前未被任何FK 约束引用的所有行。
如果cardinality(c.conkey) > 1
那样的话,也可以安全地假设cardinality(c.confkey) > 1
. 所以只计算一次来决定是否加括号。
要动态地对任何输入表进行此操作,请创建一个采用表的行值的多态函数:
CREATE OR REPLACE FUNCTION f_orphans(_tbl anyelement)
RETURNS SETOF anyelement AS
$func$
BEGIN
RETURN QUERY EXECUTE ( -- exactly the query from above
SELECT format(E'SELECT * FROM %s t\nWHERE NOT EXISTS (SELECT FROM %s'
, c.confrelid::regclass
, string_agg(format('%s WHERE %s = %s)', c.conrelid::regclass, src.cols, tgt.cols)
, E'\nAND NOT EXISTS (SELECT FROM '))
FROM pg_constraint c
, cardinality(c.conkey) AS col_ct
, LATERAL (
SELECT concat(CASE WHEN col_ct > 1 THEN '(' END
, string_agg(quote_ident(attname), ', ' ORDER BY fld.ord)
, CASE WHEN col_ct > 1 THEN ')' END) AS cols
FROM unnest(c.conkey) WITH ORDINALITY fld(attnum, ord)
JOIN pg_catalog.pg_attribute a ON (a.attrelid, a.attnum) = (c.conrelid, fld.attnum)
) src
, LATERAL (
SELECT concat(CASE WHEN col_ct > 1 THEN '(' END
, string_agg('t.' || quote_ident(attname), ', t.' ORDER BY fld.ord)
, CASE WHEN col_ct > 1 THEN ')' END) AS cols
FROM unnest(c.confkey) WITH ORDINALITY fld(attnum, ord)
JOIN pg_catalog.pg_attribute a ON (a.attrelid, a.attnum) = (c.confrelid, fld.attnum)
) tgt
WHERE c.confrelid = pg_typeof(_tbl)::text::regclass -- input goes here!
AND c.contype = 'f'
GROUP BY c.confrelid
);
END
$func$ LANGUAGE plpgsql;
Run Code Online (Sandbox Code Playgroud)
打电话(重要)!
SELECT * FROM f_orphans(NULL::my_table);
Run Code Online (Sandbox Code Playgroud)
或者:
SELECT * FROM f_orphans(NULL::myschema.my_table);
Run Code Online (Sandbox Code Playgroud)
有关的:
归档时间: |
|
查看次数: |
1298 次 |
最近记录: |