在没有定义外键的情况下搜索和反向工程数据库中的外键关系
Search for and reverse engineer foreign key relationships in DB without foreign keys defined
我继承了一个非常旧的数据库,大约有 100 tables。我通过查看 table 知道存在外键关系,但我还确定数据库中没有实际定义的关系。此外,没有一致的命名约定。
我已经尝试通过查看 tables 并尝试加入来解决这个问题,但这很耗时,而且我没有那么多时间。所以现在我正在研究一些自动化的想法。
有没有人试过运行查询数据库来查找外键?
我对查询的一般逻辑的看法是:
对于每个 table:
如果它有主键,请检查该键名是否存在于所有其他 table 中。如果是这样,请检查两组 ID 之间是否存在高百分比匹配。如果是这样,将其输出为潜在的 FK 关系。指定是一对一还是一对多。
如果其他 table 中不存在相同的名称,请尝试在所有 table 中搜索具有完全相同数据类型的字段。尝试查看两组 ID 中是否存在高百分比匹配。如果是这样,将其输出为潜在的 FK 关系。指定是一对一还是一对多。
我知道这可能会导致很多误报,但这比手动搜索要好。
我的逻辑是否合理,或者我在尝试自动执行此搜索时完全没有根据?
我的最终目标是生成一个 ER 图,我可以在构建一些新查询时使用它。
使用 MSSQL
让我们找对:
SELECT * FROM
information_schema.columns cl
INNER JOIN
information_schema.columns cr
ON
cl.table_name < cr.table_name AND
cl.data_type = cr.data_type
这会生成一个列表,其中每个 table 中的每一列都与其他 table 中的所有其他列交叉,其中数据类型相同。希望在 table 名称上使用 < 意味着 tableA.somenumber
将与 tableB.someothernumber
配对但不是相反(除非你真的想要相反) - 没有太多意义询问数据库如何a 中的许多值等于 b,然后询问 b 中有多少等于 a
现在让它写一个SQL:
SELECT
REPLACE(REPLACE(REPLACE(REPLACE(
'SELECT ''{Ltable}.{Lcol}'' as lefty, ''{Rtable}.{Rcol}'' as righty, count(l.{Lcol}) as countLefty, count(r.{Rcol}) as countRighty, case when count(r.{Rcol}) = 0 then 0 else count(l.{Lcol})/count(r.{Rcol}) end as percenty
FROM {Ltable} l LEFT JOIN {Rtable} r ON l.{Lcol} = r.{Rcol} UNION ALL',
'{Ltable}', cl.table_name),
'{Rtable}', cr.table_name),
'{Lcol}', cl.column_name),
'{Rcol}', cr.column_name)
FROM
information_schema.columns cl
INNER JOIN
information_schema.columns cr
ON
cl.table_name < cr.table_name AND
cl.data_type = cr.data_type
如果您 运行 这个 SQL,它会生成一个结果网格,每行都有一个 SQL - 将其复制出网格并将其粘贴回查询编辑器,删除最终的 UNION ALL 然后 运行 它
在我相当小的 90 tables 数据库上,每列约 8 列,它生成了 62,000 个它想要做的组合;谨慎使用.. 或者设置它在备份服务器上运行一晚并在第二天回来
感谢 Caius Jard 出色的解决方案。我已经扩展它以根据我的需要对其进行自定义。有兴趣的可以看这里:
-- Setup types to ignore
DECLARE @ignore table (ignorefield varchar(20));
-- Note: Must ignore 'text', must also ignore any other blob data types used in db. Others are optional.
INSERT @ignore(ignorefield) values('char'),('datetime'),('money'),('image'),('bit'),('binary'),('text');
-- Write queries to find fields that are potential foreign keys
SELECT
REPLACE(REPLACE(REPLACE(REPLACE(
'SELECT ''{Ltable}.{Lcol}'' as lefty, ''{Rtable}.{Rcol}'' as righty, count(l.{Lcol}) as countLefty, count(r.{Rcol}) as countRighty, case when count(r.{Rcol}) = 0 then 0 else count(l.{Lcol})/count(r.{Rcol}) end as percenty
FROM {Ltable} l LEFT JOIN {Rtable} r ON l.{Lcol} = r.{Rcol} UNION ALL',
'{Ltable}', QUOTENAME(cl.table_name)),
'{Rtable}', QUOTENAME(cr.table_name)),
'{Lcol}', QUOTENAME(cl.column_name)),
'{Rcol}', QUOTENAME(cr.column_name))
FROM
information_schema.columns cl
INNER JOIN
information_schema.columns cr
ON
cl.table_name < cr.table_name AND
cl.data_type = cr.data_type
WHERE
cl.data_type NOT IN (SELECT ignorefield from @ignore)
AND cl.is_nullable = 'NO' -- Maybe remove for some db designs.
ORDER BY
cl.data_type ASC
我继承了一个非常旧的数据库,大约有 100 tables。我通过查看 table 知道存在外键关系,但我还确定数据库中没有实际定义的关系。此外,没有一致的命名约定。
我已经尝试通过查看 tables 并尝试加入来解决这个问题,但这很耗时,而且我没有那么多时间。所以现在我正在研究一些自动化的想法。
有没有人试过运行查询数据库来查找外键?
我对查询的一般逻辑的看法是:
对于每个 table: 如果它有主键,请检查该键名是否存在于所有其他 table 中。如果是这样,请检查两组 ID 之间是否存在高百分比匹配。如果是这样,将其输出为潜在的 FK 关系。指定是一对一还是一对多。
如果其他 table 中不存在相同的名称,请尝试在所有 table 中搜索具有完全相同数据类型的字段。尝试查看两组 ID 中是否存在高百分比匹配。如果是这样,将其输出为潜在的 FK 关系。指定是一对一还是一对多。
我知道这可能会导致很多误报,但这比手动搜索要好。
我的逻辑是否合理,或者我在尝试自动执行此搜索时完全没有根据?
我的最终目标是生成一个 ER 图,我可以在构建一些新查询时使用它。
使用 MSSQL
让我们找对:
SELECT * FROM
information_schema.columns cl
INNER JOIN
information_schema.columns cr
ON
cl.table_name < cr.table_name AND
cl.data_type = cr.data_type
这会生成一个列表,其中每个 table 中的每一列都与其他 table 中的所有其他列交叉,其中数据类型相同。希望在 table 名称上使用 < 意味着 tableA.somenumber
将与 tableB.someothernumber
配对但不是相反(除非你真的想要相反) - 没有太多意义询问数据库如何a 中的许多值等于 b,然后询问 b 中有多少等于 a
现在让它写一个SQL:
SELECT
REPLACE(REPLACE(REPLACE(REPLACE(
'SELECT ''{Ltable}.{Lcol}'' as lefty, ''{Rtable}.{Rcol}'' as righty, count(l.{Lcol}) as countLefty, count(r.{Rcol}) as countRighty, case when count(r.{Rcol}) = 0 then 0 else count(l.{Lcol})/count(r.{Rcol}) end as percenty
FROM {Ltable} l LEFT JOIN {Rtable} r ON l.{Lcol} = r.{Rcol} UNION ALL',
'{Ltable}', cl.table_name),
'{Rtable}', cr.table_name),
'{Lcol}', cl.column_name),
'{Rcol}', cr.column_name)
FROM
information_schema.columns cl
INNER JOIN
information_schema.columns cr
ON
cl.table_name < cr.table_name AND
cl.data_type = cr.data_type
如果您 运行 这个 SQL,它会生成一个结果网格,每行都有一个 SQL - 将其复制出网格并将其粘贴回查询编辑器,删除最终的 UNION ALL 然后 运行 它
在我相当小的 90 tables 数据库上,每列约 8 列,它生成了 62,000 个它想要做的组合;谨慎使用.. 或者设置它在备份服务器上运行一晚并在第二天回来
感谢 Caius Jard 出色的解决方案。我已经扩展它以根据我的需要对其进行自定义。有兴趣的可以看这里:
-- Setup types to ignore
DECLARE @ignore table (ignorefield varchar(20));
-- Note: Must ignore 'text', must also ignore any other blob data types used in db. Others are optional.
INSERT @ignore(ignorefield) values('char'),('datetime'),('money'),('image'),('bit'),('binary'),('text');
-- Write queries to find fields that are potential foreign keys
SELECT
REPLACE(REPLACE(REPLACE(REPLACE(
'SELECT ''{Ltable}.{Lcol}'' as lefty, ''{Rtable}.{Rcol}'' as righty, count(l.{Lcol}) as countLefty, count(r.{Rcol}) as countRighty, case when count(r.{Rcol}) = 0 then 0 else count(l.{Lcol})/count(r.{Rcol}) end as percenty
FROM {Ltable} l LEFT JOIN {Rtable} r ON l.{Lcol} = r.{Rcol} UNION ALL',
'{Ltable}', QUOTENAME(cl.table_name)),
'{Rtable}', QUOTENAME(cr.table_name)),
'{Lcol}', QUOTENAME(cl.column_name)),
'{Rcol}', QUOTENAME(cr.column_name))
FROM
information_schema.columns cl
INNER JOIN
information_schema.columns cr
ON
cl.table_name < cr.table_name AND
cl.data_type = cr.data_type
WHERE
cl.data_type NOT IN (SELECT ignorefield from @ignore)
AND cl.is_nullable = 'NO' -- Maybe remove for some db designs.
ORDER BY
cl.data_type ASC