たくさんのSELECT文を表にまとめたかったんだ。

こんにちは!こんにちは!

肌寒くなってきましたね!

これを!

select
  a.name as 商品名,
  a.code as 商品コード,
  b.price as 最新価格
from
  cdb..商品 a
  left join ddb..価格 b
      on a.code = b.code
order by 商品コード

こうしたい!

Table
Database,Scheme,Table,Alias
,cscheme,商品,
,dscheme,価格,

Columns
Table,Column,Alias,Statement
a,name,商品名,a.name as 商品名
a,code,商品コード,a.code as 商品コード
b,price,最新価格,b.price as 最新価格

Microsoft.SqlServer.TransactSql.ScriptDom を使う。

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

/**
 * SELECT文を整理してCSV形式で出力する。
 * 
 * Usage: dotnet run file_name
 *
 * 入力ファイルの文字コードはUTF-8
 *
 */
namespace selectToCsv
{
    class Program
    {
        static void Main(string[] args)
        {
            StreamReader sr = new StreamReader(args[0], Encoding.GetEncoding("UTF-8"));
            string query = sr.ReadToEnd();
            sr.Close();
            Parse(query);
        }
        static void Parse(string query)
        {
            var parser = new TSql130Parser(false);
            IList<ParseError> errors;
            var fragment = parser.Parse(new StringReader(query), out errors);
            if (errors.Count != 0)
            {
                throw new Exception("パース失敗");
            }
            TSqlScript sqlScript = fragment as TSqlScript;
            iter(sqlScript.Batches);
        }
        static void iter(IList<TSqlBatch> sqlBatches)
        {
            foreach (TSqlBatch sqlBatch in sqlBatches)
            {
                iter(sqlBatch.Statements);
            }
        }
        static void iter(IList<TSqlStatement> st)
        {
            foreach (TSqlStatement sts in st)
            {
                if (sts is SelectStatement)
                {
                    ParseSelectStatement(sts as SelectStatement);
                }
            }
        }
        static void ParseSelectStatement(SelectStatement st)
        {
            ParseSelectToken(st.ScriptTokenStream);
        }
        static void ParseSelectToken(IList<TSqlParserToken> tokens)
        {
            Select statement = new Select(tokens);
            Identifier identifier = new Identifier(statement);
            print(identifier);
        }
        static void print(object o)
        {
            Console.WriteLine(o);
        }
        class Select
        {
            public TSqlParserToken State { get; set; }
            public List<List<TSqlParserToken>> SelectList { get; set; }
            public List<List<TSqlParserToken>> TableSource { get; set; }
            public List<List<TSqlParserToken>> SearchCondition { get; set; }
            public List<List<TSqlParserToken>> OrderExpression { get; set; }
            public Select(IList<TSqlParserToken> tokens)
            {
                SelectList = new List<List<TSqlParserToken>>();
                TableSource = new List<List<TSqlParserToken>>();
                SearchCondition = new List<List<TSqlParserToken>>();
                OrderExpression = new List<List<TSqlParserToken>>();

                foreach (TSqlParserToken token in tokens)
                {
                    Add(token);
                }
            }
            public void Add(TSqlParserToken token)
            {
                switch (token.TokenType)
                {
                    case TSqlTokenType.Select:
                        NewSelect();
                        State = token;
                        return;
                    case TSqlTokenType.From:
                        NewTable();
                        State = token;
                        return;
                    case TSqlTokenType.Where:
                        NewCondition();
                        State = token;
                        return;
                    case TSqlTokenType.Order:
                        NewOrder();
                        State = token;
                        return;
                    case TSqlTokenType.WhiteSpace:
                    case TSqlTokenType.SingleLineComment:
                    case TSqlTokenType.MultilineComment:
                        return;
                }
                if (State == null)
                {
                    return;
                }
                if (State.TokenType == TSqlTokenType.Select)
                {
                    if (token.TokenType == TSqlTokenType.Comma)
                    {
                        NewSelect();
                        return;
                    }
                    SelectList[SelectList.Count - 1].Add(token);
                }
                if (State.TokenType == TSqlTokenType.From)
                {
                    if (token.TokenType == TSqlTokenType.Inner || token.TokenType == TSqlTokenType.Join)
                    {
                        NewTable();
                        return;
                    }
                    TableSource[TableSource.Count - 1].Add(token);
                }
                if (State.TokenType == TSqlTokenType.Where)
                {
                    SearchCondition[SearchCondition.Count - 1].Add(token);
                }
                if (State.TokenType == TSqlTokenType.Order)
                {
                    OrderExpression[OrderExpression.Count - 1].Add(token);
                }
            }

            public List<TSqlParserToken> NewSelect()
            {
                List<TSqlParserToken> select = new List<TSqlParserToken>();
                SelectList.Add(select);
                return select;
            }
            public List<TSqlParserToken> NewTable()
            {
                List<TSqlParserToken> table = new List<TSqlParserToken>();
                TableSource.Add(table);
                return table;
            }
            public List<TSqlParserToken> NewCondition()
            {
                List<TSqlParserToken> condition = new List<TSqlParserToken>();
                SearchCondition.Add(condition);
                return condition;
            }
            public List<TSqlParserToken> NewOrder()
            {
                List<TSqlParserToken> order = new List<TSqlParserToken>();
                OrderExpression.Add(order);
                return order;
            }
        }
        class SelectIdentifier
        {
            public string Table { get; set; }
            public string Column { get; set; }
            public string Alias { get; set; }
            public string Statement { get; set; }
            public SelectIdentifier(List<TSqlParserToken> tokens)
            {

                if (tokens.Count >= 2)
                {
                    if (tokens[1].TokenType == TSqlTokenType.Dot)
                    {
                        Table = tokens[0].Text;
                        Column = tokens[2].Text;
                    }
                }
                if (Column == null)
                {
                    Column = tokens[0].Text;
                }
                var indexOfAs = tokens.FindLastIndex((token) => token.TokenType == TSqlTokenType.As);
                if (indexOfAs != -1)
                {
                    Alias = tokens[indexOfAs + 1].Text;
                }
                Statement = String.Join("", tokens.ConvertAll((token) =>
                {
                    if (token.TokenType == TSqlTokenType.Dot)
                    {
                        return token.Text;
                    }
                    if (token.TokenType == TSqlTokenType.Identifier)
                    {
                        return token.Text;
                    }
                    return " " + token.Text + " ";
                }));
            }

            public override string ToString()
            {
                return Table + "," + Column + "," + Alias + "," + Statement;
            }

            public static string Header()
            {
                return "Table,Column,Alias,Statement";
            }
        }
        class TableIdentifier
        {
            public string Database { get; set; }
            public string Scheme { get; set; }
            public string Table { get; set; }
            public string Alias { get; set; }
            public string Statement { get; set; }
            public TableIdentifier(List<TSqlParserToken> tokens)
            {
                var indexOfOn = tokens.FindIndex((token) => token.TokenType == TSqlTokenType.On);
                if (indexOfOn != -1)
                {
                    tokens = tokens = tokens.GetRange(0, indexOfOn);
                }
                tokens = tokens.FindAll((token) =>
                {
                    return token.TokenType == TSqlTokenType.Dot || token.TokenType == TSqlTokenType.Identifier;
                });
                var dotCount = tokens.FindAll((token) => token.TokenType == TSqlTokenType.Dot).Count;
                switch (dotCount)
                {
                    case 0:
                        Table = tokens[0].Text;
                        break;
                    case 1:
                        Scheme = tokens[0].Text;
                        Table = tokens[2].Text;
                        break;
                    case 2:
                        var i = tokens.FindIndex((token) => token.TokenType == TSqlTokenType.Dot);
                        Database = tokens[i - 1].Text;
                        var i2 = tokens.FindIndex(i + 1, (token) => token.TokenType == TSqlTokenType.Dot);
                        if (i != (i2 - 1))
                        {
                            Scheme = tokens[i2 - i].Text;
                        }
                        Table = tokens[i2 + 1].Text;
                        if (tokens.Count > i2 + 2)
                        {
                            Alias = tokens[i2 + 2].Text;
                        }
                        break;
                }
            }
            public override string ToString()
            {
                return Database + "," + Scheme + "," + Table + "," + Alias;
            }
            public static string Header()
            {
                return "Database,Scheme,Table,Alias";
            }
        }
        class Identifier
        {
            public List<SelectIdentifier> SelectIdentifiers { get; set; }
            public List<TableIdentifier> TableIdentifiers { get; set; }
            public Identifier(Select statement)
            {
                SelectIdentifiers = new List<SelectIdentifier>();
                TableIdentifiers = new List<TableIdentifier>();
                foreach (var list in statement.SelectList)
                {
                    SelectIdentifiers.Add(new SelectIdentifier(list));
                }
                foreach (var list in statement.TableSource)
                {
                    if (list.Count == 0)
                    {
                        continue;
                    }
                    TableIdentifiers.Add(new TableIdentifier(list));
                }
                foreach (var table in TableIdentifiers)
                {
                    foreach (var select in SelectIdentifiers)
                    {
                        if (select.Table == table.Alias)
                        {
                            select.Table = table.Table;
                        }
                    }
                }
            }
            public override string ToString()
            {
                return
                    "Table\r\n" +
                    TableIdentifier.Header() + "\r\n" +
                    String.Join("\r\n", TableIdentifiers.ConvertAll((table) => table.ToString())) + "\r\n\r\n" +
                    "Columns\r\n" +
                    SelectIdentifier.Header() + "\r\n" +
                    String.Join("\r\n", SelectIdentifiers.ConvertAll((select) => select.ToString()));
            }
        }
    }

}

ライブラリを呼び出すだけの簡単なお仕事! Enjoy!