如何按依赖顺序列出表(基于外键)?
How to list tables in their dependency order (based on foreign keys)?
这个问题最初是由@PrateekGupta 提出的
背景
@PrateekGupta 想对多个 table 执行批量插入操作。
table 之间有外键关系。
如果在插入引用的 table 之前使用外键对 table 执行 INSERT 操作,则操作可能会因违反外键而失败。
要求
根据依赖关系在数据库中生成 table 的列表。
没有依赖项(没有外键)的表将排在第一位。
仅在第一组 table 中具有依赖关系的表将排在第二位。
仅在第 1 组或第 2 组 table 中具有依赖关系的表将排在第 3 位。
等等...
example:
create table t1 (i int primary key,j int unique)
create table t2 (i int primary key references t1 (i));
create table t3 (i int,j int,primary key (i,j));
create table t4 (i int,j int, foreign key (i,j) references t3 (i,j));
create table t5 (i int references t1 (i),j int,foreign key (i,j) references t3 (i,j));
create table t6 (i int references t2 (i));
with cte (lvl,object_id,name)
as
(
select 1
,object_id
,name
from sys.tables
where type_desc = 'USER_TABLE'
and is_ms_shipped = 0
union all
select cte.lvl + 1
,t.object_id
,t.name
from cte
join sys.tables as t
on exists
(
select null
from sys.foreign_keys as fk
where fk.parent_object_id = t.object_id
and fk.referenced_object_id = cte.object_id
)
and t.object_id <> cte.object_id
and cte.lvl < 30
where t.type_desc = 'USER_TABLE'
and t.is_ms_shipped = 0
)
select name
,max (lvl) as dependency_level
from cte
group by name
order by dependency_level
,name
;
感谢大卫。
如果有人需要,我刚刚将架构名称添加到他的查询中
WITH cte (lvl, object_id, name, schema_Name) AS
(SELECT 1, object_id, sys.tables.name, sys.schemas.name as schema_Name
FROM sys.tables Inner Join sys.schemas on sys.tables.schema_id = sys.schemas.schema_id
WHERE type_desc = 'USER_TABLE'
AND is_ms_shipped = 0
UNION ALL SELECT cte.lvl + 1, t.object_id, t.name, S.name as schema_Name
FROM cte
JOIN sys.tables AS t ON EXISTS
(SELECT NULL FROM sys.foreign_keys AS fk
WHERE fk.parent_object_id = t.object_id
AND fk.referenced_object_id = cte.object_id )
JOIN sys.schemas as S on t.schema_id = S.schema_id
AND t.object_id <> cte.object_id
AND cte.lvl < 30
WHERE t.type_desc = 'USER_TABLE'
AND t.is_ms_shipped = 0 )
SELECT schema_Name, name, MAX (lvl) AS dependency_level
FROM cte
GROUP BY schema_Name, name
ORDER BY dependency_level,schema_Name, name;
我需要自己做这件事,希望有人已经为 Postgres 做过,但没有找到任何东西,所以我就把它留在这里:
WITH RECURSIVE t AS (
SELECT relnamespace as nsp, oid as tbl, null::regclass as source, 1 as level
FROM pg_class
WHERE relkind = 'r'
AND relnamespace not in ('pg_catalog'::regnamespace, 'information_schema'::regnamespace)
UNION ALL
SELECT c.connamespace as nsp, c.conrelid as tbl, c.confrelid as source, p.level + 1
FROM pg_constraint c
INNER JOIN t p ON (c.confrelid = p.tbl AND c.connamespace = p.nsp)
WHERE c.contype = 'f'
AND c.connamespace not in ('pg_catalog'::regnamespace, 'information_schema'::regnamespace)
)
SELECT nsp::regnamespace, tbl::regclass
FROM t
GROUP BY nsp, tbl
ORDER BY max(level) DESC;
这是两个用例之间的混合查询。如果您需要查看外键指向哪个 table,您可以删除 GROUP BY
和 SELECT source::regclass
。
脚本的Oracle版本:
with
foreign_keys as (
select
src_cc.owner as src_owner,
src_cc.table_name as src_table,
src_cc.column_name as src_column,
dest_cc.owner as dest_owner,
dest_cc.table_name as dest_table,
dest_cc.column_name as dest_column,
c.constraint_name
from
all_constraints c
inner join all_cons_columns dest_cc on
c.r_constraint_name = dest_cc.constraint_name
and c.r_owner = dest_cc.owner
inner join all_cons_columns src_cc on
c.constraint_name = src_cc.constraint_name
and c.owner = src_cc.owner
where
c.constraint_type = 'R'
and dest_cc.owner = :owner),
cte (lvl,table_name) as (
select 1, table_name
from all_tables
where
owner = :owner
union all
select cte.lvl + 1, t.table_name
from cte
join all_tables t on exists (
select null
from foreign_keys fk
where
fk.src_table = t.table_name
and fk.dest_table = cte.table_name
)
and t.table_name <> cte.table_name
AND cte.lvl < 30
)
select table_name, max (lvl) as dependency_level
from cte
group by table_name
order by dependency_level desc, table_name
;
以上答案不适用于循环引用。您可以改用此存储过程。
EXEC sp_msdependencies @flags = 8
可以在这里看到标志选项
EXEC sp_msdependencies '?'
遗憾的是,SQLAzure 不提供此功能。
这个问题最初是由@PrateekGupta 提出的
背景
@PrateekGupta 想对多个 table 执行批量插入操作。
table 之间有外键关系。
如果在插入引用的 table 之前使用外键对 table 执行 INSERT 操作,则操作可能会因违反外键而失败。
要求
根据依赖关系在数据库中生成 table 的列表。
没有依赖项(没有外键)的表将排在第一位。
仅在第一组 table 中具有依赖关系的表将排在第二位。
仅在第 1 组或第 2 组 table 中具有依赖关系的表将排在第 3 位。
等等...
example:
create table t1 (i int primary key,j int unique)
create table t2 (i int primary key references t1 (i));
create table t3 (i int,j int,primary key (i,j));
create table t4 (i int,j int, foreign key (i,j) references t3 (i,j));
create table t5 (i int references t1 (i),j int,foreign key (i,j) references t3 (i,j));
create table t6 (i int references t2 (i));
with cte (lvl,object_id,name)
as
(
select 1
,object_id
,name
from sys.tables
where type_desc = 'USER_TABLE'
and is_ms_shipped = 0
union all
select cte.lvl + 1
,t.object_id
,t.name
from cte
join sys.tables as t
on exists
(
select null
from sys.foreign_keys as fk
where fk.parent_object_id = t.object_id
and fk.referenced_object_id = cte.object_id
)
and t.object_id <> cte.object_id
and cte.lvl < 30
where t.type_desc = 'USER_TABLE'
and t.is_ms_shipped = 0
)
select name
,max (lvl) as dependency_level
from cte
group by name
order by dependency_level
,name
;
感谢大卫。
如果有人需要,我刚刚将架构名称添加到他的查询中
WITH cte (lvl, object_id, name, schema_Name) AS
(SELECT 1, object_id, sys.tables.name, sys.schemas.name as schema_Name
FROM sys.tables Inner Join sys.schemas on sys.tables.schema_id = sys.schemas.schema_id
WHERE type_desc = 'USER_TABLE'
AND is_ms_shipped = 0
UNION ALL SELECT cte.lvl + 1, t.object_id, t.name, S.name as schema_Name
FROM cte
JOIN sys.tables AS t ON EXISTS
(SELECT NULL FROM sys.foreign_keys AS fk
WHERE fk.parent_object_id = t.object_id
AND fk.referenced_object_id = cte.object_id )
JOIN sys.schemas as S on t.schema_id = S.schema_id
AND t.object_id <> cte.object_id
AND cte.lvl < 30
WHERE t.type_desc = 'USER_TABLE'
AND t.is_ms_shipped = 0 )
SELECT schema_Name, name, MAX (lvl) AS dependency_level
FROM cte
GROUP BY schema_Name, name
ORDER BY dependency_level,schema_Name, name;
我需要自己做这件事,希望有人已经为 Postgres 做过,但没有找到任何东西,所以我就把它留在这里:
WITH RECURSIVE t AS (
SELECT relnamespace as nsp, oid as tbl, null::regclass as source, 1 as level
FROM pg_class
WHERE relkind = 'r'
AND relnamespace not in ('pg_catalog'::regnamespace, 'information_schema'::regnamespace)
UNION ALL
SELECT c.connamespace as nsp, c.conrelid as tbl, c.confrelid as source, p.level + 1
FROM pg_constraint c
INNER JOIN t p ON (c.confrelid = p.tbl AND c.connamespace = p.nsp)
WHERE c.contype = 'f'
AND c.connamespace not in ('pg_catalog'::regnamespace, 'information_schema'::regnamespace)
)
SELECT nsp::regnamespace, tbl::regclass
FROM t
GROUP BY nsp, tbl
ORDER BY max(level) DESC;
这是两个用例之间的混合查询。如果您需要查看外键指向哪个 table,您可以删除 GROUP BY
和 SELECT source::regclass
。
脚本的Oracle版本:
with
foreign_keys as (
select
src_cc.owner as src_owner,
src_cc.table_name as src_table,
src_cc.column_name as src_column,
dest_cc.owner as dest_owner,
dest_cc.table_name as dest_table,
dest_cc.column_name as dest_column,
c.constraint_name
from
all_constraints c
inner join all_cons_columns dest_cc on
c.r_constraint_name = dest_cc.constraint_name
and c.r_owner = dest_cc.owner
inner join all_cons_columns src_cc on
c.constraint_name = src_cc.constraint_name
and c.owner = src_cc.owner
where
c.constraint_type = 'R'
and dest_cc.owner = :owner),
cte (lvl,table_name) as (
select 1, table_name
from all_tables
where
owner = :owner
union all
select cte.lvl + 1, t.table_name
from cte
join all_tables t on exists (
select null
from foreign_keys fk
where
fk.src_table = t.table_name
and fk.dest_table = cte.table_name
)
and t.table_name <> cte.table_name
AND cte.lvl < 30
)
select table_name, max (lvl) as dependency_level
from cte
group by table_name
order by dependency_level desc, table_name
;
以上答案不适用于循环引用。您可以改用此存储过程。
EXEC sp_msdependencies @flags = 8
可以在这里看到标志选项
EXEC sp_msdependencies '?'
遗憾的是,SQLAzure 不提供此功能。