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.mzph.cn/news/893912.shtml

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

相关文章

Oracle迁移DM数据库

Oracle迁移DM数据库 本文记录使用达梦官方数据迁移工具DTS&#xff0c;将Oracle数据库的数据迁移至达梦数据库。 1 数据准备 2 DTS工具操作步骤 2.1 创建工程 打开DTS迁移工具&#xff0c;点击新建工程&#xff0c;填写好工程信息&#xff0c;如图&#xff1a; 2.2 新建迁…

微服务(一)

文章目录 项目地址一、微服务1.1 分析User的Domian Verb和Nouns 二、运行docker和k8s2.1 Docker1. 编写dockerfile2. 创建docker image3. 运行docker使用指定端口4. 查看当前运行的镜像5. 停止当前所有运行的docker6. 删除不用的docker images7. 将本地的image上传到hub里 2.2 …

分享|instructionfine-tuning 指令微调是提高LLM性能和泛化能力的通用方法

《生成式AI导论》课程中&#xff0c;李宏毅老师提到一篇关于“ instruction fine-tuning” 指令微调的论文&#xff1a; 《Scaling Instruction-Finetuned Language Models》 摘要分享&#xff1a; 事实证明&#xff0c; 在一组以指令形式表达的数据集上微调语言模型可以提…

python生成图片和pdf,快速

1、下载安装 pip install imgkit pip install pdfkit2、wkhtmltopdf工具包&#xff0c;下载安装 下载地址&#xff1a;https://wkhtmltopdf.org/downloads.html 3、生成图片 import imgkit path_wkimg rD:\app\wkhtmltopdf\bin\wkhtmltoimage.exe # 工具路径&#xff0c;安…

Hive:基本查询语法

和oracle一致的部分 和oracle不一样的部分 排序 oracle中,在升序排序中&#xff0c;NULL 值被视为最大的值&#xff1b;在降序排序中&#xff0c;NULL 值被视为最小的值。 在MySQL中&#xff0c;NULL 被视为小于任何非空值。 在Hive中, NULL是最小的; Hive除了可以用order…

Python GUI 开发 | PySide6 辅助工具简介

关注这个框架的其他相关笔记&#xff1a;Python GUI 开发 | PySide6 & PyQt6 学习手册-CSDN博客 在上一章中&#xff0c;我们介绍了如何搭建 PySide6 & PyQt6 的开发环境。在搭建环境的时候我们配置了几个几个快捷工具&#xff0c;很多小伙伴可能都不知道是干啥用的。那…

hive:数据导入,数据导出,加载数据到Hive,复制表结构

hive不建议用insert,因为Hive是建立在Hadoop之上的数据仓库工具&#xff0c;主要用于批处理和大数据分析&#xff0c;而不是为OLTP&#xff08;在线事务处理&#xff09;操作设计的。INSERT操作会非常慢 数据导入 命令行界面:建一个文件 查询数据>>复制>>粘贴到新…

HarmonyOS:ForEach:循环渲染

一、前言 ForEach接口基于数组类型数据来进行循环渲染&#xff0c;需要与容器组件配合使用&#xff0c;且接口返回的组件应当是允许包含在ForEach父容器组件中的子组件。例如&#xff0c;ListItem组件要求ForEach的父容器组件必须为List组件。 API参数说明见&#xff1a;ForEa…

基于PostgreSQL的自然语义解析电子病历编程实践与探索(上)

一、引言 1.1研究目标与内容 本研究旨在构建一个基于 PostgreSQL 的自然语义解析电子病历编程体系,实现从电子病历文本中提取结构化信息,并将其存储于 PostgreSQL 数据库中,以支持高效的查询和分析。具体研究内容包括: 电子病历的预处理与自然语言处理:对电子病历文本进…

安装 docker 详解

在平常的开发工作中&#xff0c;我们经常需要部署项目。随着 Docker 容器的出现&#xff0c;大大提高了部署效率。Docker 容器包含了应用程序运行所需的所有依赖&#xff0c;避免了换环境运行问题。可以在短时间内创建、启动和停止容器&#xff0c;大大提高了应用的部署速度&am…

深度学习项目--基于LSTM的糖尿病预测探究(pytorch实现)

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 前言 LSTM模型一直是一个很经典的模型&#xff0c;一般用于序列数据预测&#xff0c;这个可以很好的挖掘数据上下文信息&#xff0c;本文将使用LSTM进行糖尿病…

初阶1 入门

本章重点 C的关键字命名空间C的输入输出缺省参数函数重载引用内联函数auto关键字基于范围的for循环指针的空值nullptr 1.C的关键字 c总共有63个关键字&#xff0c;其中包含c语言的32个 这些关键字不需要特意去记&#xff0c;在我们日后写代码的过程中会慢慢用到并记住。 2.…

动态规划DP 数字三角形模型(模型分析+例题分析+C++代码实现)(数字三角形、摘花生、最低通行费用、方格取数、传纸条)

总体概览 数字三角形 原题链接 AcWing 898.数字三角形 题目描述 给定一个如下图所示的数字三角形&#xff0c;从顶部出发&#xff0c;在每一结点可以选择移动至其左下方的结点或移动至其右下方的结点&#xff0c;一直走到底层&#xff0c;要求找出一条路径&#xff0c;使路…

C#/.NET/.NET Core技术前沿周刊 | 第 23 期(2025年1.20-1.26)

前言 C#/.NET/.NET Core技术前沿周刊&#xff0c;你的每周技术指南针&#xff01;记录、追踪C#/.NET/.NET Core领域、生态的每周最新、最实用、最有价值的技术文章、社区动态、优质项目和学习资源等。让你时刻站在技术前沿&#xff0c;助力技术成长与视野拓宽。 欢迎投稿、推荐…

力扣hot100链表总结-day0127

目录 206.反转链表&#xff08;0127&#xff09; 19.删除链表的倒数第N个节点&#xff08;0127&#xff09; 206.反转链表&#xff08;0127&#xff09; 总结&#xff1a; 1.迭代实现&#xff1a;将给出的头节点head固定&#xff0c;始终把head的下一个节点给安装成新头节点…

安宝特方案 | AR在供应链管理中的应用:提升效率与透明度

随着全球化的不断深入和市场需求的快速变化&#xff0c;企业对供应链管理的要求也日益提高。如何在复杂的供应链环境中提升效率、降低成本&#xff0c;并确保信息的透明度&#xff0c;成为了各大行业亟待解决的问题。而增强现实&#xff08;AR&#xff09;技术&#xff0c;特别…

力扣111二叉树的最小深度(DFS)

Problem: 111. 二叉树的最小深度 文章目录 题目描述思路复杂度Code 题目描述 思路 1.欲望求出最短的路径&#xff0c;先可以记录一个变量minDepth&#xff0c;同时记录每次当前节点所在的层数currentDepth 2.在递的过程中&#xff0c;每次递一层&#xff0c;也即使当前又往下走…

python -m pip和pip的主要区别

python -m pip和pip的主要区别在于它们与Python环境的关联方式和安装路径。‌ ‌与Python环境的关联方式‌&#xff1a; pip 是直接使用命令行工具来安装Python包&#xff0c;不指定特定的Python解释器。如果系统中存在多个Python版本&#xff0c;可能会导致安装的包被安装到…

RHEL封闭环境部署zabbix

背景&#xff1a;client端操作系统升级.然后安装了该操作系统基础版本的zabbix-client&#xff0c;与原来的zabbix-server版本不匹配&#xff0c;需要重新部署一台zabbix-server zabbix-server GUI上该client采集不到系统数据 说明&#xff1a;下文的 x.x.x.x 代表服务端的IP…

vim 中粘贴内容时提示: -- (insert) VISUAL --

目录 问题现象&#xff1a;解决方法&#xff1a;问题原因&#xff1a; 问题现象&#xff1a; 使用 vim 打开一个文本文件&#xff0c;切换到编辑模式后&#xff0c;复制内容进行粘贴时有以下提示&#xff1a; 解决方法&#xff1a; 在命令行模式下禁用鼠标支持 :set mouse …