まだまだ ScriptDom ネタです。
TSqlParser でパースした結果は、TSqlFragment として返ってきます。
TSqlFragment クラス (Microsoft.SqlServer.TransactSql.ScriptDom)
TSqlFragment の子階層のインスタンスは、Visitor を使うことで簡単にアクセス出来ます。
Visitor は下記の2種類あり、どちらかを継承して必要な Visit メソッドを override します。
TSqlFragmentVisitor クラス (Microsoft.SqlServer.TransactSql.ScriptDom)
TSqlConcreteFragmentVisitor クラス (Microsoft.SqlServer.TransactSql.ScriptDom)
TSqlFragmentVisitor と TSqlConcreteFragmentVisitor の違いは、Visitor メソッドのパラメータに渡されるインスタンスが、パラメータの型だけか、そこから派生している型も含むかの違いになっています。
Visitor | 派生型を含む |
TSqlFragmentVisitor | ○ |
TSqlConcreteFragmentVisitor | × |
実際に試してみましょう。
select で指定している項目の数と、"*" の数を数えてみます。
項目の数は、
SelectElement クラス (Microsoft.SqlServer.TransactSql.ScriptDom)
の派生型が出てきた回数を数えれば OK です。
"*" の数は、SelectElement を継承している
SelectStarExpression クラス (Microsoft.SqlServer.TransactSql.ScriptDom)
を数えれば OK です。
SelectElement の数を数える Visitor
public class SelectElementVisitor : TSqlFragmentVisitor { public int Count { get; set; } public override void Visit(SelectElement node) { Count++; base.Visit(node); } }SelectStarExpression の数を数える Visitor
public class SelectStarVisitor : TSqlConcreteFragmentVisitor { public int Count { get; set; } public override void Visit(SelectStarExpression node) { Count++; base.Visit(node); } }using Microsoft.SqlServer.TransactSql.ScriptDom; using System; using System.Collections.Generic; using System.IO; class Program { static void Main(string[] args) { var parser = new TSql110Parser(false); IList<ParseError> errors; TSqlFragment f; using (var reader = new StringReader(@"SELECT @Id = A.Id, @Name = B.Name FROM ( SELECT * FROM Table1 WHERE Id = 1) A INNER JOIN Table2 B ON ( A.USERID = B.ID )")) { f = parser.Parse(reader, out errors); } var v1 = new SelectElementVisitor(); var v2 = new SelectStarVisitor(); f.Accept(v1); f.Accept(v2); Console.WriteLine(v1.Count); // 3 Console.WriteLine(v2.Count); // 1 Console.ReadKey(); } }
クラス名からも想像出来ると思いますが、Visitor パターン を使っています。これを使えばパースした結果に対して色々出来そうですね!