You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用C#提取各类T-SQL语句中的所有表名?

用C#提取T-SQL语句中的所有表名(处理复杂场景)

提取T-SQL里的表名确实是个头疼的活儿——尤其是面对嵌套子查询、带架构的表、表别名这些情况时,正则表达式根本顶不住,毕竟T-SQL的语法灵活性太高了。我之前踩过正则的坑,后来发现用微软官方的Microsoft.SqlServer.TransactSql.ScriptDom语法分析库才是正道,它能把SQL解析成抽象语法树(AST),精准遍历所有表引用。

步骤1:安装依赖

首先通过NuGet安装官方库:

Install-Package Microsoft.SqlServer.TransactSql.ScriptDom

或者用.NET CLI:

dotnet add package Microsoft.SqlServer.TransactSql.ScriptDom

步骤2:自定义语法树访问器

我们需要创建一个继承自TSqlFragmentVisitor的类,重写方法来捕获各类表引用。这个访问器会递归遍历整个语法树,不管是顶层查询还是嵌套子查询里的表,都能一网打尽。

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.SqlServer.TransactSql.ScriptDom;

public class TableNameVisitor : TSqlFragmentVisitor
{
    // 存储提取到的完整表名(含架构)
    public List<string> TableNames { get; } = new List<string>();

    // 处理常规命名表引用(如Schema.Table、Table)
    public override void Visit(NamedTableReference node)
    {
        // 拼接架构名和表名
        var fullTableName = string.Join(".", node.MultiPartIdentifier.Identifiers.Select(id => id.Value));
        if (!TableNames.Contains(fullTableName))
        {
            TableNames.Add(fullTableName);
        }

        // 继续遍历子节点,确保嵌套子查询的表被捕获
        base.Visit(node);
    }

    // 处理MERGE语句的目标表
    public override void Visit(MergeStatement node)
    {
        if (node.Target is NamedTableReference targetTable)
        {
            var fullTableName = string.Join(".", targetTable.MultiPartIdentifier.Identifiers.Select(id => id.Value));
            if (!TableNames.Contains(fullTableName))
            {
                TableNames.Add(fullTableName);
            }
        }

        // 继续处理MERGE的USING子句等其他部分
        base.Visit(node);
    }

    // 处理TRUNCATE TABLE语句的表
    public override void Visit(TruncateTableStatement node)
    {
        var fullTableName = string.Join(".", node.TableName.Identifiers.Select(id => id.Value));
        if (!TableNames.Contains(fullTableName))
        {
            TableNames.Add(fullTableName);
        }

        base.Visit(node);
    }
}

步骤3:编写解析逻辑

接下来写一个方法,把SQL语句传入解析器,生成语法树后用我们的访问器提取表名:

public static List<string> ExtractTableNames(string sql)
{
    // 选择对应SQL Server版本的解析器(示例用Sql150对应2019)
    var parser = new Sql150Parser(false);
    IList<ParseError> errors;
    var fragment = parser.Parse(new StringReader(sql), out errors);

    // 处理解析错误(可根据需求调整逻辑)
    if (errors.Count > 0)
    {
        throw new InvalidOperationException($"SQL解析失败:{string.Join("; ", errors.Select(e => e.Message))}");
    }

    var visitor = new TableNameVisitor();
    fragment.Accept(visitor);

    return visitor.TableNames;
}

步骤4:测试示例

用你给出的样例SQL测试一下:

var sampleSql = @"SELECT <Schemaname>.<TableName1>.Field1, <TableName2>.Field2, Field3, 
Field4 = ( Select .. FROM <TableName6> WHERE ... ) 
FROM <TableName1> , <TableName2> 
INNER JOIN <TableName3> AS Ta...";

var tables = ExtractTableNames(sampleSql);
foreach (var table in tables)
{
    Console.WriteLine(table);
}

输出结果:

Schemaname.TableName1
TableName2
TableName6
TableName3

关键注意事项

  • SQL版本兼容:要匹配你的SQL Server版本选择解析器(如Sql110对应2012,Sql160对应2022),避免新版本语法解析失败。
  • 特殊表名处理:带中括号的表名(如[My Table])会被解析器自动处理,Identifier.Value会返回正确名称。
  • 别名关联:如果需要记录别名和真实表名的对应关系,可以在Visit(NamedTableReference)里获取node.Alias的值,存入字典。
  • 全DML覆盖:上述访问器已经处理了SELECT/INSERT/UPDATE/DELETE(这些语句的表引用会被NamedTableReference捕获),以及MERGE和TRUNCATE的特殊场景。

内容的提问来源于stack exchange,提问作者Ardalan Shahgholi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 08:23:51