在脚本中从连接中发现预期的外键

我inheritance了一个有400个表的数据库,并且只有150个外键约束被注册。 知道我对应用程序做了些什么,然后看着表格列,很容易就可以知道应该有更多。

如果我开始添加缺less的FKs,恐怕现在的应用软件会中断,因为开发者可能会依赖这个“自由”,但是解决问题的第一步是找出缺失的FK列表我们可以评估他们作为一个团队。

更糟糕的是,引用列不共享命名约定。

这些关系被非正式地编码到数百个即席查询和存储过程中,所以我希望通过编程的方式来parsing这些文件,寻找实际表(而不是表variables等)之间的连接。

我在这种方法中预见到的挑战是:换行符,可选别名和表格提示,别名parsing。

  • 任何更好的想法? (除了戒烟)
  • 有没有可以解决这个问题的预build工具?
  • 我不认为正则expression式可以处理这个问题。 你不同意吗?
  • SQLparsing器? 我尝试使用Microsoft.SqlServer.Management.SqlParser.Parser,但所有暴露的是词法分析器 – 无法得到一个AST – 所有这些东西是内部的。

function强大的SQLparsing器可以自动分析数百个临时查询和存储过程,并且可以从SQLparsing器生成的查询parsing树中轻松findvariables表/列的关系。

这是示例:

SELECT a.ASSMT_NO, b.LINK_PARAM, c.EXPL AS LINK_PG, (SELECT count() FROM GRAASPST t WHERE t.ASSMT_NO = a.ASSMT_NO AND t.ROLE != '02') AS PSN_CNT, (SELECT count() FROM GRAASPST t WHERE t.ASSMT_NO = a.ASSMT_NO AND t.ROLE != '02' AND ASSMT_FIN_YN = 'Y') AS PSN_FIN_CNT, (SELECT Avg(assmt_pts) FROM GRAASSMT t WHERE t.ASSMT_NO = a.ASSMT_NO AND t.ASSMT_TGT_SEQ_NO = a.ASSMT_TGT_SEQ_NO) AS ASSMT_PTS_AVG, a.ASSMT_RES, a.ASSMT_RPT_SUB_TITLE FROM GRAASTAT a JOIN GRAASRET b ON b.DELIB_REQ_NO = a.DELIB_REQ_NO JOIN GRTCODDT c ON c.DIV_CD = 'GR013' AND c.CD = b.DELIB_SLCT JOIN CMUSERMT d ON d.USERID = a.REGID WHERE a.ASSMT_NO = :ASSMT_NO ORDER BY a.ASSMT_TGT_SEQ_NO 

分析完这个查询后,你可能会得到这样的结果:

 JoinTable1 JoinColumn1 JoinTable2 JoinColumn2 GRAASRET DELIB_REQ_NO GRAASTAT DELIB_REQ_NO GRTCODDT CD GRAASRET DELIB_SLCT CMUSERMT USERID GRAASTAT REGID GRAASPST ASSMT_NO GRAASTAT ASSMT_NO GRAASSMT ASSMT_NO GRAASTAT ASSMT_NO GRAASSMT ASSMT_TGT_SEQ_NO GRAASTAT ASSMT_TGT_SEQ_NO 

你可以查看这个演示了解详细信息。

我感到你的痛苦。

免费的SQLsearch SSMS插件可能会对您有所帮助。

一般来说,是的,正则expression式可以处理这个问题,但是你应该意识到在试图召唤正则expression式魔法的时候,回报递减点。 在映射出关系的同时,您可能会更好地审查和search代码。

SQL Search可能会让你更容易。

这是我想出来的。 此查询查找不是表的主键的外键类列(int,bigint,guid),它们当前没有使用外键约束进行注册。 当然,我得到了一些sorting顺序和数量列,但是与parsingSQL脚本相比,它确实缩小了列表的范围。

 WITH ExistingFKCs AS ( SELECT CU.TABLE_NAME, -- Referencing Table CU.COLUMN_NAME -- Referencing Column FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS C JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE CU ON C.CONSTRAINT_NAME = CU.CONSTRAINT_NAME ) SELECT T.TABLE_NAME AS [Table Name], COL.COLUMN_NAME AS [Column Name] FROM INFORMATION_SCHEMA.TABLES T JOIN INFORMATION_SCHEMA.COLUMNS COL ON T.TABLE_TYPE = 'BASE TABLE' AND COL.TABLE_NAME = T.TABLE_NAME LEFT JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS PKC ON PKC.CONSTRAINT_TYPE = 'Primary Key' AND PKC.TABLE_NAME = COL.TABLE_NAME LEFT JOIN INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE PKCU ON PKCU.TABLE_NAME = PKC.TABLE_NAME AND PKCU.CONSTRAINT_NAME = PKC.CONSTRAINT_NAME AND PKCU.COLUMN_NAME = COL.COLUMN_NAME LEFT JOIN ExistingFKCs EFKS ON EFKS.TABLE_NAME = COL.TABLE_NAME AND EFKS.COLUMN_NAME = COL.COLUMN_NAME WHERE PKCU.COLUMN_NAME IS NULL AND EFKS.COLUMN_NAME IS NOT NULL AND COL.DATA_TYPE IN ('int', 'bigint','uniqueidentifier') ORDER BY T.TABLE_NAME, COL.COLUMN_NAME