csharp 解析通过T-SQL从连接子句推断外键 - 注意:不完整。只支持seroc中的sprocs和一些语法结构

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了csharp 解析通过T-SQL从连接子句推断外键 - 注意:不完整。只支持seroc中的sprocs和一些语法结构相关的知识,希望对你有一定的参考价值。

using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.Data.Schema.ScriptDom;
using Microsoft.Data.Schema.ScriptDom.Sql;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Joins
{
    class ForeignKey
    {
        public string Table1 { get; set; }
        public string Key1 { get; set; }
        public string Table2 { get; set; }
        public string Key2 { get; set; }
    }

    class StatementContext
    {
        public Dictionary<string, string> Aliases {get;set;}
        public List<ForeignKey> FKs { get; set; }
        
        public StatementContext()
        {
            Aliases = new Dictionary<string, string>();
            FKs = new List<ForeignKey>();
        }

        public void Alias(TableSource ts)
        {
            if (ts.GetType() == typeof(SchemaObjectTableSource))
            {
                var source = ts as SchemaObjectTableSource;
                if (source.Alias != null)
                {
                    var alias = source.Alias.Value.ToLower();
                    if (!Aliases.ContainsKey(alias))
                        Aliases.Add(alias, source.SchemaObject.BaseIdentifier.Value);
                }
            }
        }


        public string UnAlias(string s)
        {
            if (Aliases.ContainsKey(s.ToLower()))
                return Aliases[s.ToLower()];
            else
                return s;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            string filePath = args[0];
            string Script = string.Empty;
            //System.Diagnostics.Debugger.Break();

            using (StreamReader streamReader = new StreamReader(filePath))
            {
                Script = streamReader.ReadToEnd();
            }

            IList<string> errorsList;
            var TSqlScript = ParseScript(Script, out errorsList);

            foreach (var b in TSqlScript.Batches)
                SearchStatmentList(b.Statements);

        }

        private static void SearchStatement(TSqlStatement s)
        {
            if (s != null)
            {
                var t = TextFrom(s);
                var st = s.GetType();

                if (st == typeof(AlterProcedureStatement))
                    SearchStatmentList((((AlterProcedureStatement)s).StatementList.Statements));
                else if (st == typeof(CreateProcedureStatement))
                    SearchStatmentList((((CreateProcedureStatement)s).StatementList.Statements));
                else if (st == typeof(BeginEndBlockStatement))
                    SearchStatmentList((((BeginEndBlockStatement)s).StatementList.Statements));
                else if (st == typeof(InsertStatement))
                {
                    var i = (InsertStatement)s;
                    if (i.InsertSource.GetType() == typeof(SelectStatement))
                        SearchStatement(i.InsertSource as SelectStatement);
                }

                else if (st == typeof(IfStatement))
                {
                    var i = s as IfStatement;

                    SearchStatement(i.ThenStatement);
                    SearchStatement(i.ElseStatement);

                }
                else if (st == typeof(SelectStatement))
                {

                    FindFromClauses(((SelectStatement)s).QueryExpression);
                }
            }
        }

        private static void SearchStatmentList(IList<TSqlStatement> statements)
        {
            foreach (var s in statements)
                SearchStatement(s);
        }

        private static void FindFromClauses(QueryExpression q)
        {
            if (q.GetType() == typeof(BinaryQueryExpression))
            {
                var binary = q as BinaryQueryExpression;


                FindFromClauses(binary.FirstQueryExpression);
                FindFromClauses(binary.SecondQueryExpression);
            }
            else if (q.GetType() == typeof(QuerySpecification))
            {
                var qs = q as QuerySpecification;

                var t = TextFrom(q);
                foreach (var from in qs.FromClauses)
                {
                    if (from.GetType() == typeof(QualifiedJoin))
                    {
                        StatementContext ctx = new StatementContext();
                        WalkJoin(from as QualifiedJoin, ctx);

                        foreach (var r in ctx.FKs)
                        {
                            Console.WriteLine(string.Format("{0}.{1} = {2}.{3}", ctx.UnAlias(r.Table1), r.Key1, ctx.UnAlias(r.Table2), r.Key2));
                        }
                    }
                }
            }
        }

        private static void WalkJoin(QualifiedJoin j, StatementContext context)
        {
            var t = TextFrom(j);
            if (j.SearchCondition.GetType() == typeof(BinaryExpression))
            {
                var b = j.SearchCondition as BinaryExpression;
                t = TextFrom(b);
                context.Alias(j.FirstTableSource);
                context.Alias(j.SecondTableSource);
                if (b.BinaryExpressionType == BinaryExpressionType.Equals)
                {
                    if (b.FirstExpression.GetType() == typeof(Column) &&
                        b.SecondExpression.GetType() == typeof(Column))
                    {
                        context.FKs.Add(new ForeignKey()
                        {
                            Table1 = ((Column)b.FirstExpression).Identifiers[0].Value,
                            Key1 = ((Column)b.FirstExpression).Identifiers[1].Value,
                            Table2 = ((Column)b.SecondExpression).Identifiers[0].Value,
                            Key2 = ((Column)b.SecondExpression).Identifiers[1].Value
                        });
                    }
                }
                if (j.FirstTableSource.GetType() == typeof(QualifiedJoin))
                {
                    WalkJoin((QualifiedJoin)j.FirstTableSource, context);
                }
                if (j.SecondTableSource.GetType() == typeof(QualifiedJoin))
                {
                    WalkJoin((QualifiedJoin)j.SecondTableSource, context);
                }
            }
        }

        private static string TextFrom(TSqlFragment f)
        {
            if (f == null) return null;

            var sb = new StringBuilder();
            for (int i = f.FirstTokenIndex; i <= f.LastTokenIndex; i++)
            {
                sb.Append(f.ScriptTokenStream[i].Text);
            }
            return sb.ToString();
        }

        public static TSqlScript ParseScript(string script, out IList<string> errorsList)
        {
            IList<ParseError> parseErrors;
            TSql100Parser tsqlParser = new TSql100Parser(true);
            TSqlFragment fragment;
            using (StringReader stringReader = new StringReader(script))
            {
                fragment = (TSqlFragment)tsqlParser.Parse(stringReader, out parseErrors);
            }
            errorsList = new List<string>();
            if (parseErrors.Count > 0)
            {
                var retMessage = string.Empty;
                foreach (var error in parseErrors)
                {
                    retMessage += error.Identifier + " - " + error.Message + " - position: " + error.Offset + "; ";
                }

            }
            return (TSqlScript)fragment;
        }
    }
}

以上是关于csharp 解析通过T-SQL从连接子句推断外键 - 注意:不完整。只支持seroc中的sprocs和一些语法结构的主要内容,如果未能解决你的问题,请参考以下文章

如何将数据从一个表移动到另一个表并更新外键 (T-SQL 2008)

关于 Where 子句的 T-Sql 多重标准

T-SQL基础--TOP

如何缩短 T-SQL 中的 WHERE 子句

T-SQL:在 OUTPUT 子句中插入原始值

sql 使用T-SQL禁用外键约束?