C#实现SQL Server数据血缘关系生成程序

要在现有的C#程序中添加功能,输出SQL Server数据血缘关系的三张表到Excel文件,我们需要进行以下几个步骤:

分析存储过程、视图和函数中的引用关系,构建数据血缘关系。
按依赖性从小到大排序表的顺序。
找出对应生成表的数据的存储过程。
将结果输出到Excel文件。

以下是完整的代码实现:

using Microsoft.SqlServer.TransactSql.ScriptDom;
using OfficeOpenXml;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;class Program
{static void Main(){string directoryPath = @"<搜索的目录路径>";var dataRelations = new Dictionary<string, List<string>>();var tableProcedures = new Dictionary<string, List<string>>();var allTablesAndViews = new HashSet<string>();ProcessSqlFiles(directoryPath, dataRelations, tableProcedures, allTablesAndViews);var sortedTables = SortTablesByDependency(dataRelations, allTablesAndViews);WriteToExcel(dataRelations, sortedTables, tableProcedures);}static void ProcessSqlFiles(string directoryPath, Dictionary<string, List<string>> dataRelations, Dictionary<string, List<string>> tableProcedures, HashSet<string> allTablesAndViews){foreach (string filePath in Directory.EnumerateFiles(directoryPath, "*.sql", SearchOption.AllDirectories)){Console.WriteLine($"Processing file: {filePath}");string sqlContent = File.ReadAllText(filePath);ProcessSqlContent(sqlContent, dataRelations, tableProcedures, allTablesAndViews);CheckCreateStatements(sqlContent, tableProcedures);}}static void ProcessSqlContent(string sqlContent, Dictionary<string, List<string>> dataRelations, Dictionary<string, List<string>> tableProcedures, HashSet<string> allTablesAndViews){TSql150Parser parser = new TSql150Parser(false);IList<ParseError> errors;TSqlFragment fragment = parser.Parse(new StringReader(sqlContent), out errors);if (errors.Count == 0){var referenceVisitor = new ReferenceVisitor(dataRelations, allTablesAndViews);fragment.Accept(referenceVisitor);}else{foreach (var error in errors){Console.WriteLine($"Parse error: {error.Message}");}}}static void CheckCreateStatements(string sqlContent, Dictionary<string, List<string>> tableProcedures){// 匹配创建视图的语句MatchCollection viewMatches = Regex.Matches(sqlContent, @"CREATE\s+VIEW\s+([^\s(]+)", RegexOptions.IgnoreCase);foreach (Match match in viewMatches){Console.WriteLine($"View: {match.Groups[1].Value}");}// 匹配创建存储过程的语句MatchCollection sprocMatches = Regex.Matches(sqlContent, @"CREATE\s+PROC(?:EDURE)?\s+([^\s(]+)", RegexOptions.IgnoreCase);foreach (Match match in sprocMatches){Console.WriteLine($"Stored Procedure: {match.Groups[1].Value}");tableProcedures[match.Groups[1].Value] = new List<string>();}// 匹配创建函数的语句MatchCollection functionMatches = Regex.Matches(sqlContent, @"CREATE\s+(?:FUNCTION|AGGREGATE)\s+([^\s(]+)", RegexOptions.IgnoreCase);foreach (Match match in functionMatches){Console.WriteLine($"User Defined Function: {match.Groups[1].Value}");}}static List<string> SortTablesByDependency(Dictionary<string, List<string>> dataRelations, HashSet<string> allTablesAndViews){var sorted = new List<string>();var visited = new HashSet<string>();foreach (var table in allTablesAndViews){TopologicalSort(table, dataRelations, sorted, visited);}return sorted;}static void TopologicalSort(string node, Dictionary<string, List<string>> dataRelations, List<string> sorted, HashSet<string> visited){if (visited.Contains(node)) return;visited.Add(node);if (dataRelations.ContainsKey(node)){foreach (var child in dataRelations[node]){TopologicalSort(child, dataRelations, sorted, visited);}}sorted.Insert(0, node);}static void WriteToExcel(Dictionary<string, List<string>> dataRelations, List<string> sortedTables, Dictionary<string, List<string>> tableProcedures){using (var package = new ExcelPackage()){var worksheet1 = package.Workbook.Worksheets.Add("Data Relations");worksheet1.Cells[1, 1].Value = "Table/View Name";worksheet1.Cells[1, 2].Value = "Generation Path";int row = 2;foreach (var table in sortedTables){worksheet1.Cells[row, 1].Value = table;worksheet1.Cells[row, 2].Value = GetGenerationPath(table, dataRelations);row++;}var worksheet2 = package.Workbook.Worksheets.Add("Sorted Tables");worksheet2.Cells[1, 1].Value = "Table/View Name";for (int i = 0; i < sortedTables.Count; i++){worksheet2.Cells[i + 2, 1].Value = sortedTables[i];}var worksheet3 = package.Workbook.Worksheets.Add("Table - Procedure Mapping");worksheet3.Cells[1, 1].Value = "Table/View Name";worksheet3.Cells[1, 2].Value = "Stored Procedures";row = 2;foreach (var table in sortedTables){worksheet3.Cells[row, 1].Value = table;worksheet3.Cells[row, 2].Value = string.Join(", ", tableProcedures.GetValueOrDefault(table, new List<string>()));row++;}FileInfo file = new FileInfo("DataBloodline.xlsx");package.SaveAs(file);}}static string GetGenerationPath(string table, Dictionary<string, List<string>> dataRelations){if (!dataRelations.ContainsKey(table)) return table;return table + "->" + string.Join("->", dataRelations[table].Select(t => GetGenerationPath(t, dataRelations)));}
}class ReferenceVisitor : TSqlFragmentVisitor
{private readonly Dictionary<string, List<string>> dataRelations;private readonly HashSet<string> allTablesAndViews;public ReferenceVisitor(Dictionary<string, List<string>> dataRelations, HashSet<string> allTablesAndViews){this.dataRelations = dataRelations;this.allTablesAndViews = allTablesAndViews;}public override void Visit(SelectQuerySpecification node){VisitTableReferences(node.FromClause);}public override void Visit(InsertStatement node){AddReference(node.TableReference);}public override void Visit(UpdateStatement node){AddReference(node.TableReference);}public override void Visit(DeleteStatement node){AddReference(node.TableReference);}public override void Visit(ViewDefinition node){AddReference(node.SchemaObjectName);}private void VisitTableReferences(FromClause fromClause){if (fromClause!= null){foreach (var tableReference in fromClause.TableReferences){AddReference(tableReference);}}}private void AddReference(TSqlFragment fragment){if (fragment is TableReference tableReference){var name = tableReference.SchemaObject.BaseIdentifier.Value;AddInfo(name);}else if (fragment is SchemaObjectName schemaObjectName){var name = schemaObjectName.BaseIdentifier.Value;AddInfo(name);}}private void AddInfo(string name){allTablesAndViews.Add(name);if (!dataRelations.ContainsKey(name)){dataRelations[name] = new List<string>();}}
}

代码说明:

Main方法:初始化变量并调用 ProcessSqlFiles 方法,最后调用 WriteToExcel 方法将结果输出到Excel文件。
ProcessSqlFiles方法:遍历指定目录及其子目录下的所有 .sql 文件,并对每个文件的内容执行 ProcessSqlContent 和 CheckCreateStatements 方法。
ProcessSqlContent方法:使用 Microsoft.SqlServer.TransactSql.ScriptDom 库解析SQL内容,获取引用关系。
CheckCreateStatements方法:使用正则表达式匹配SQL内容中的创建视图、存储过程和函数的语句,并更新 tableProcedures 字典。
SortTablesByDependency方法:使用拓扑排序按依赖性从小到大排序表的顺序。
WriteToExcel方法:使用EPPlus库将数据血缘关系的三张表输出到Excel文件。
ReferenceVisitor类:继承自 TSqlFragmentVisitor ,用于收集SQL语句中的表和视图引用关系。

请确保你已经安装了EPPlus库,可以通过NuGet安装:

Install-Package EPPlus

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/8101.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

日志收集Day007

1.配置ES集群TLS认证: (1)elk101节点生成证书文件 cd /usr/share/elasticsearch ./bin/elasticsearch-certutil cert -out config/elastic-certificates.p12 -pass "" --days 3650 (2)elk101节点为证书文件修改属主和属组 chown elasticsearch:elasticsearch con…

使用Python和Qt6创建GUI应用程序---GUI的一个非常简短的历史

GUI的一个非常简短的历史 图形用户界面有着悠久而可敬的历史&#xff0c;可以追溯到20世纪60年代。斯坦福大学的NLS&#xff08;在线系统&#xff09;引入了鼠标和Windows概念于1968年首次公开展示。接下来是施乐PARC的Smalltalk系统GUI 1973&#xff0c;这是最现代的基础通用g…

如何建设一个企业级的数据湖

建设一个企业级的数据湖是一项复杂且系统化的工程&#xff0c;需要从需求分析、技术选型、架构设计到实施运维等多个方面进行综合规划和实施。以下是基于我搜索到的资料&#xff0c;详细阐述如何建设企业级数据湖的步骤和关键要点&#xff1a; 一、需求分析与规划 明确业务需…

xxl-job分布式定时任务

1 前言 1.1 业务场景 业务数据同步 ( 线上数据同步到线下&#xff0c;新平台老平台数据的同步 ) &#xff0c;消息通知&#xff0c;业务数据的补偿。 1.2 什么是定时任务 定时任务是指基于给定的时间点&#xff0c;给定的时间间隔或者给定执行次数自动的执行程序。 任务调度…

FLTK - FLTK1.4.1 - demo - adjuster.exe

文章目录 FLTK - FLTK1.4.1 - demo - adjuster.exe概述笔记根据代码&#xff0c;用fluid重建一个adjuster.fl 备注 - fluid生成的代码作为参考代码好了修改后可用的代码END FLTK - FLTK1.4.1 - demo - adjuster.exe 概述 想过一遍 FLTK1.4.1的demo和测试工程&#xff0c;工程…

Cursor的简单使用

目录 一、下载与配置 1.1、下载 1.2、汉化 1.3、模型选择 1.4、规则设置 二、Chat&#xff08;聊天&#xff09;和Composer&#xff08;编写助手&#xff09; 三、快捷键 3.1、tab(代码自动补全) 3.2、CtrlL、CtrlI 3.3、系列 3.4、预防、检测、回滚 四、无限登录 …

剥离情绪的内耗

情绪的内耗&#xff0c;指的是我们内心对于某些情绪的过度反应、反复纠结&#xff0c;或者对情感的压抑所产生的心理消耗。这种内耗通常会让我们感到疲惫、焦虑、无力&#xff0c;甚至影响到我们的行为和决策。要真正剥离情绪的内耗&#xff0c;核心在于如何认识、接受并合理处…

android的gradle

资料&#xff1a; GitHub - ChenSWD/CopyGradleInAction: 备份《Gradle IN Action》书中的源码&#xff0c;添加了部分注释 //github上一个开源项目&#xff0c;外加pdf书 Gradle User Manual gradle官网 讲的挺好的博客 Gradle之重新认识Gradle(项目结构、命令行、tas…

Python 之 Excel 表格常用操作

示例文件 test.xlsx 将各个表单拆分成单独的 Excel 文件 import os.pathimport openpyxl import pandasdef handle_excel(file_path):dirname os.path.dirname(file_path)basename os.path.basename(file_path).split(".")[0]wb openpyxl.load_workbook(file_pat…

【C语言系列】深入理解指针(4)

深入理解指针&#xff08;4&#xff09; 一、回调函数是什么&#xff1f;二、qsort使用举例2.1使用qsort函数排序整型数据2.2使用qsort排序结构数据 三、qsort函数的模拟实现四、总结 一、回调函数是什么&#xff1f; 回调函数就是一个通过函数指针调用的函数。 如果你把函数的…

计算机网络 (56)交互式音频/视频

一、定义与特点 定义&#xff1a;交互式音频/视频是指用户使用互联网和其他人进行实时交互式通信的技术&#xff0c;包括语音、视频图像等多媒体实时通信。 特点&#xff1a; 实时性&#xff1a;音频和视频数据是实时传输和播放的&#xff0c;用户之间可以进行即时的交流。交互…

FFmpeg 头文件完美翻译之 libavcodec 模块

前言 众所周知&#xff0c;FFmpeg 的代码开发上手难度较高&#xff0c;源于官方提供的文档很少有包含代码教程相关的。要想熟练掌握 FFmpeg 的代码库开发&#xff0c;需要借助它的头文件&#xff0c;FFmpeg 把很多代码库教程都写在头文件里面。因此&#xff0c;熟读头文件的内…

组件框架漏洞

一.基础概念 1.组件 定义&#xff1a;组件是软件开发中具有特定功能或特性的可重用部件或模块&#xff0c;能独立使用或集成到更大系统。 类型 前端 UI 组件&#xff1a;像按钮、下拉菜单、导航栏等&#xff0c;负责构建用户界面&#xff0c;提升用户交互体验。例如在电商 AP…

【C++图论】1761. 一个图中连通三元组的最小度数|2005

本文涉及知识点 C图论 LeetCode1761. 一个图中连通三元组的最小度数 给你一个无向图&#xff0c;整数 n 表示图中节点的数目&#xff0c;edges 数组表示图中的边&#xff0c;其中 edges[i] [ui, vi] &#xff0c;表示 ui 和 vi 之间有一条无向边。 一个 连通三元组 指的是 …

C语言编译过程全面解析

今天是2025年1月26日&#xff0c;农历腊月二十七&#xff0c;一个距离新春佳节仅一步之遥的日子。城市的喧嚣中&#xff0c;年味已悄然弥漫——能在这个时候坚持上班的人&#xff0c;真可称为“牛人”了吧&#xff0c;哈哈。。。。 此刻&#xff0c;我在重新审视那些曾被遗忘的…

【橘子Kibana】Kibana的分析能力Analytics简易分析

一、kibana是啥&#xff0c;能干嘛 我们经常会用es来实现一些关于检索&#xff0c;关于分析的业务。但是es本身并没有UI,我们只能通过调用api来完成一些能力。而kibana就是他的一个外置UI&#xff0c;你完全可以这么理解。 当我们进入kibana的主页的时候你可以看到这样的布局。…

生信软件管家——conda vs pip

pip vs conda&#xff1a; 安装过python包的人自然两种管理软件都用过&#xff0c; Pip install和Conda install在Python环境中用于安装第三方库和软件包&#xff0c;但它们在多个方面存在显著的区别 总的来说&#xff1a; pip是包管理软件&#xff0c;conda既是包管理软件&…

代码随想录——二叉树(二)

文章目录 前言二叉树最大深度二叉树的最小深度翻转二叉树对称二叉树完全二叉树的节点个数平衡二叉树二叉树的所有路径左叶子之和找左下角的值路径总和从中序与后序序列构造二叉树最大二叉树合并二叉树二叉搜索树中的搜索验证二叉搜索树二叉搜索树的最小绝对差二叉树中的众数二叉…

深入剖析 Adam 优化器:原理、优势与应用

在深度学习领域&#xff0c;优化器的选择对模型的训练效率和性能起着决定性作用。Adam优化器作为一种自适应优化算法&#xff0c;凭借其根据历史梯度信息动态调整学习率的特性&#xff0c;备受研究者和工程师的青睐。它巧妙融合了RMSProp和Momentum两种优化算法的理念&#xff…

Mybatis入门

Mybatis入门 一、mybatis的快速入门 1、创建springboot项目 直接选择必须的依赖&#xff1a;MyBatis Framework和MySQL Driver在项目下创建pojo包&#xff0c;用来存放数据库表对应的实体类 2、配置连接信息 在springboot项目的配置文件中application.properties写入一下信…