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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

飞牛NAS新增虚拟机功能,如果使用虚拟机网卡直通安装ikuai软路由(如何解决OVS网桥绑定失败以及打开ovs后无法访问飞牛nas等问题)

文章目录 📖 介绍 📖🏡 演示环境 🏡📒 飞牛NAS虚拟机安装爱快教程 📒🛠️ 前期准备🌐 网络要求💾 下载爱快镜像🚀 开始安装💻 开启IOMMU直通🌐 配置网络🚨 解决OVS网桥绑定失败以及打开ovs后无法访问飞牛nas等问题➕ 创建虚拟机🎯 安装ikuai💻 进…

嵌入式蓝桥杯电子赛嵌入式(第14届国赛真题)总结

打开systic 生成工程编译查看是否有问题同时打开对应需要的文档 修改名称的要求 5.简单浏览赛题 选择题&#xff0c;跟单片机有关的可以查相关手册 答题顺序 先从显示开始看 1,2 所以先打开PA1的定时器这次选TIM2 从模式、TI2FP2二通道、内部时钟、1通道设为直接2通道设置…

C# volatile 使用详解

总目录 前言 在多线程编程中&#xff0c;确保线程之间的正确同步和可见性是一个关键挑战。C# 提供了多种机制来处理这些挑战&#xff0c;其中之一就是 volatile 关键字。它用于指示编译器和运行时环境不要对特定变量进行某些优化&#xff0c;以保证该变量的读写操作是线程安全…

基于OSAL的嵌入式裸机事件驱动框架——整体架构调度机制

参考B站up主【架构分析】嵌入式祼机事件驱动框架 感谢大佬分享 任务ID &#xff1a; TASK_XXX TASK_XXX 在系统中每个任务的ID是唯一的&#xff0c;范围是 0 to 0xFFFE&#xff0c;0xFFFF保留为SYS_TSK_INIT。 同时任务ID的大小也充当任务调度的优先级&#xff0c;ID越大&#…

理解C++中的右值引用

右值引用&#xff0c;顾名思义&#xff0c;就是对一个右值进行引用&#xff0c;或者说给右值一个别名。右值引用的规则和左值一用一模一样&#xff0c;都是对一个值或者对象起个别名。 1. 右值引用和左值引用一样&#xff0c;在定义的同时必须立即赋值&#xff0c;如果不立即赋…

寒假1.23

题解 web&#xff1a;[极客大挑战 2019]Secret File&#xff08;文件包含漏洞&#xff09; 打开链接是一个普通的文字界面 查看一下源代码 发现一个链接&#xff0c;点进去看看 再点一次看看&#xff0c;没什么用 仔细看&#xff0c;有一个问题&#xff0c;当点击./action.ph…

分布式版本控制系统:Git

1 Git概述 Git官网&#xff1a;https://git-scm.com/ Git是一个免费的、开源的分布式版本控制系统&#xff0c;可以快速高效地处理从小型到大型的各种项目Git易于学习&#xff0c;占地面积小&#xff0c;性能极快。它具有廉价的本地库、方便的暂存区域和多个工作流分支等特性…

< OS 有关 > 阿里云:轻量应用服务器 的使用 :轻量化 阿里云 vpm 主机

原因&#xff1a; &#xff1c; OS 有关 &#xff1e; 阿里云&#xff1a;轻量应用服务器 的使用 &#xff1a;从新开始 配置 SSH 主机名 DNS Tailscale 更新OS安装包 最主要是 清除阿里云客户端这个性能杀手-CSDN博客 防止 I/O 祸害系统 操作&#xff1a; 查看进程&#x…

工业相机 SDK 二次开发-Sherlock插件

本文介绍了 sherlock 连接相机时的插件使用。通过本套插件可连接海康的工业相机。 一&#xff0e;环境配置 1. 拷贝动态库 在用户安装 MVS 目录下按照如下路径 Development\ThirdPartyPlatformAdapter 找到目 录为 DalsaSherlock 的文件夹&#xff0c;根据 Sherlock 版本找到…

为什么机器学习中梯度下降是减去斜率,而不是按照其数学意义减去斜率的倒数

做个简单假设&#xff0c;Loss函数的某一个参数的函数曲线是二次方程&#xff0c;其导数函数为 r 2 ∗ w r 2*w r2∗w 按照斜率意义来看&#xff0c;要减去斜率倒数 降低LOSS需要将w1更新为w2&#xff0c;所以更新公式为 w w − Δ L Δ w w w - \frac{\Delta L}{\Delta w…

Linux高级--3.3.2 自定义协议设计--ProtoBuf

一、自定义协议设计的必要性 自定义通信协议通常有以下几个原因&#xff0c;尤其在IM即时通信、节点服务器、HTTP协议、Nginx、Redis协议、SOME/IP协议和DoIP协议等场景中&#xff0c;设计和使用自定义协议能带来特定的优势&#xff1a; 1. 性能优化 更高效的资源利用&#…

技术总结:FPGA基于GTX+RIFFA架构实现多功能SDI视频转PCIE采集卡设计方案

目录 1、前言工程概述免责声明 3、详细设计方案设计框图SDI 输入设备Gv8601a 均衡器GTX 解串与串化SMPTE SD/HD/3G SDI IP核BT1120转RGBFDMA图像缓存RIFFA用户数据控制RIFFA架构详解Xilinx 7 Series Integrated Block for PCI ExpressRIFFA驱动及其安装QT上位机HDMI输出RGB转BT…

docker 部署 java 项目详解

在平常的开发工作中&#xff0c;我们经常需要部署项目&#xff0c;开发测试完成后&#xff0c;最关键的一步就是部署。今天我们以若依项目为例&#xff0c;总结下部署项目的整体流程。简单来说&#xff0c;第一步&#xff1a;安装项目所需的中间件&#xff1b;第二步&#xff1…

动手学图神经网络(2):跆拳道俱乐部案例实战

动手学图神经网络&#xff08;2&#xff09;&#xff1a;跆拳道俱乐部案例实战 在深度学习领域&#xff0c;图神经网络&#xff08;GNNs&#xff09;能将传统深度学习概念推广到不规则的图结构数据&#xff0c;使神经网络能够处理对象及其关系。将基于 PyTorch Geometric 库&a…

Elastic Agent 对 Kafka 的新输出:数据收集和流式传输的无限可能性

作者&#xff1a;来 Elastic Valerio Arvizzigno, Geetha Anne 及 Jeremy Hogan 介绍 Elastic Agent 的新功能&#xff1a;原生输出到 Kafka。借助这一最新功能&#xff0c;Elastic 用户现在可以轻松地将数据路由到 Kafka 集群&#xff0c;从而实现数据流和处理中无与伦比的可扩…

1.25学习

web bugku-源代码 打开环境后看到了一个提交的界面&#xff0c;我们根据题目查看源代码&#xff0c;看到了js代码&#xff0c;其中有几处是url编码&#xff0c;我们对其进行解码&#xff0c;后面的unescape&#xff08;&#xff09;函数就是将p1解码以及%35%34%61%61%32p2解码…

Hive详细讲解-基础语法快速入门

文章目录 1.DDL数据库相关操作1.1创建数据库1.2指定路径下创建数据库1.3添加额外信息创建with dbproperties1.4查看数据库 结合like模糊查询 2.查看某一个数据库的相关信息2.1.如何查看数据库信息&#xff0c;extended可选2.2修改数据库 3.Hive基本数据类型4.复杂数据类型5.类型…

深度解析:基于Vue 3与Element Plus的学校管理系统技术实现

一、项目架构分析 1.1 技术栈全景 核心框架&#xff1a;Vue 3 TypeScript UI组件库&#xff1a;Element Plus&#xff08;含图标动态注册&#xff09; 状态管理&#xff1a;Pinia&#xff08;用户状态持久化&#xff09; 路由方案&#xff1a;Vue Router&#xff08;动态路…

基于Django的个人博客系统的设计与实现

【Django】基于Django的个人博客系统的设计与实现&#xff08;完整系统源码开发笔记详细部署教程&#xff09;✅ 目录 一、项目简介二、项目界面展示三、项目视频展示 一、项目简介 系统采用Python作为主要开发语言&#xff0c;结合Django框架构建后端逻辑&#xff0c;并运用J…

【架构面试】一、架构设计认知

涉及分布式锁、中间件、数据库、分布式缓存、系统高可用等多个技术领域&#xff0c;旨在考查候选人的技术深度、架构设计能力与解决实际问题的能力。 1. 以 Redis 是否可以作为分布式锁为例&#xff1a; 用 Redis 实现分布式锁会存在哪些问题&#xff1f; 死锁&#xff1a;如果…