Подтвердить что ты не робот

Используя TSqlParser

Я пытаюсь разобрать SQL, используя TSql100Parser, предоставленный Microsoft. Сейчас у меня есть небольшая проблема, когда я использую его так, как будто он предназначен для использования. Кроме того, отсутствие документации не помогает. (пример: http://msdn.microsoft.com/en-us/library/microsoft.data.schema.scriptdom.sql.tsql100parser.aspx)

Когда я запускаю простой оператор SELECT через парсер, он возвращает коллекцию TSqlStatements, которая содержит инструкцию SELECT. Проблема заключается в том, что оператор TSqlSelect не содержит атрибутов, таких как предложение WHERE, даже если предложение реализовано как класс. http://msdn.microsoft.com/en-us/library/microsoft.data.schema.scriptdom.sql.whereclause.aspx Парсер действительно признает предложение WHERE как таковое, глядя на токен. Итак, мой вопрос: правильно ли я использую парсер? Прямо сейчас поток токенов представляется наиболее полезной функцией анализатора...

Мой тестовый проект:

public static void Main(string[] args)
{
    var parser = new TSql100Parser(false);

            IList<ParseError> Errors;
            IScriptFragment result = parser.Parse(
                new StringReader("Select col from T1 where 1 = 1 group by 1;" +
                    "select col2 from T2;" +
                    "select col1 from tbl1 where id in (select id from tbl);"),
                    out Errors);

            var Script = result as TSqlScript;

            foreach (var ts in Script.Batches)
            {
                Console.WriteLine("new batch");

                foreach (var st in ts.Statements)
                {
                    IterateStatement(st);
                }
            }
}

static void IterateStatement(TSqlStatement statement)
{
            Console.WriteLine("New Statement");

            if (statement is SelectStatement)
            {
                PrintStatement(sstmnt);
            }
}
4b9b3361

Ответ 1

Да, вы правильно используете парсер.

Как указывает Damien_The_Unbeliever, внутри SelectStatement есть свойство QueryExpression, которое будет QuerySpecification для вашего третьего оператора select (с предложением WHERE).

Это представляет собой "реальный" бит SELECT запроса (тогда как внешний объект SelectStatement, который вы просматриваете, только что получил предложение "WITH" (для CTE), предложение "FOR" (для XML), "ORDER BY", и другие биты)

Объект QuerySpecification - это объект с FromClauses, WhereClause, GroupByClause и т.д.

Итак, вы можете перейти к своему предложению WHERE, используя:

((QuerySpecification)((SelectStatement)statement).QueryExpression).WhereClause

у которого есть свойство SearchCondition и т.д. и т.д.

Ответ 2

если кто-то приземлится здесь и хочет знать, как получить все элементы оператора select в следующем коде, объясните, что:

QuerySpecification spec = (QuerySpecification)(((SelectStatement)st).QueryExpression);
StringBuilder sb = new StringBuilder();
sb.AppendLine("Select Elements");
foreach (var elm in spec.SelectElements)                            
  sb.Append(((Identifier)((Column)((SelectColumn)elm).Expression).Identifiers[0]).Value);

sb.AppendLine();

sb.AppendLine("From Elements");
foreach (var elm in spec.FromClauses)
  sb.Append(((SchemaObjectTableSource)elm).SchemaObject.BaseIdentifier.Value);
sb.AppendLine();

sb.AppendLine("Where Elements");
BinaryExpression binaryexp = (BinaryExpression)spec.WhereClause.SearchCondition;
sb.Append("operator is " + binaryexp.BinaryExpressionType);
if (binaryexp.FirstExpression is Column)
  sb.Append(" First exp is " + ((Identifier)((Column)binaryexp.FirstExpression).Identifiers[0]).Value);

if (binaryexp.SecondExpression is Literal)
  sb.Append(" Second exp is " + ((Literal)binaryexp.SecondExpression).Value);

Ответ 3

Быстрый взгляд будет означать, что он содержит QueryExpression, который может быть QuerySpecification, который имеет прикрепленное к нему предложение Where.

Ответ 4

Мне пришлось разбить оператор SELECT на куски. Моя цель состояла в том, чтобы указать, сколько записей будет возвращено. Моим первым решением было построить дополнительный запрос, например

SELECT COUNT(*) FROM (select id, name from T where cat='A' order by id) as QUERY

Проблема заключалась в том, что в этом случае предложение порядка вызывает ошибку "Предложение ORDER BY недопустимо в представлениях, встроенных функциях, производных таблицах, подзапросах и общих табличных выражениях, если также не указано TOP или FOR XML"

Итак, я построил парсер, который разбил элемент SELECT на фрагменты, используя класс TSql100Parser.

using Microsoft.Data.Schema.ScriptDom.Sql;
using Microsoft.Data.Schema.ScriptDom;
using System.IO;
...
public class SelectParser
{
    public string Parse(string sqlSelect, out string fields, out string from, out string groupby, out string where, out string having, out string orderby)
    {
        TSql100Parser parser = new TSql100Parser(false);
        TextReader rd = new StringReader(sqlSelect);
        IList<ParseError> errors;
        var fragments = parser.Parse(rd, out errors);

        fields = string.Empty;
        from = string.Empty;
        groupby = string.Empty;
        where = string.Empty;
        orderby = string.Empty;
        having = string.Empty;

        if (errors.Count > 0)
        {
            var retMessage = string.Empty;
            foreach (var error in errors)
            {
                retMessage += error.Identifier + " - " + error.Message + " - position: " + error.Offset + "; ";
            }

            return retMessage;
        }

        try
        {
            // Extract the query assuming it is a SelectStatement
            var query = ((fragments as TSqlScript).Batches[0].Statements[0] as SelectStatement).QueryExpression;

            // Constructs the From clause with the optional joins
            from = (query as QuerySpecification).FromClauses[0].GetString();

            // Extract the where clause
            where = (query as QuerySpecification).WhereClause.GetString();

            // Get the field list
            var fieldList = new List<string>();
            foreach (var f in (query as QuerySpecification).SelectElements)
                fieldList.Add((f as SelectColumn).GetString());
            fields = string.Join(", ", fieldList.ToArray());

            // Get The group by clause
            groupby = (query as QuerySpecification).GroupByClause.GetString();

            // Get the having clause of the query
            having = (query as QuerySpecification).HavingClause.GetString();

            // Get the order by clause
            orderby = ((fragments as TSqlScript).Batches[0].Statements[0] as SelectStatement).OrderByClause.GetString();
        }
        catch (Exception ex)
        {
            return ex.ToString();
        }

        return string.Empty;
    }
}


public static class Extension
{
    /// <summary>
    /// Get a string representing the SQL source fragment
    /// </summary>
    /// <param name="statement">The SQL Statement to get the string from, can be any derived class</param>
    /// <returns>The SQL that represents the object</returns>
    public static string GetString(this TSqlFragment statement)
    {
        string s = string.Empty;
        if (statement == null) return string.Empty;

        for (int i = statement.FirstTokenIndex; i <= statement.LastTokenIndex; i++)
        {
            s += statement.ScriptTokenStream[i].Text;
        }

        return s;
    }
}

И использовать этот класс просто:

string fields, from, groupby, where, having, orderby;
SelectParser selectParser = new SelectParser(); 
var retMessage = selectParser.Parse("SELECT * FROM T where cat='A' Order by Id desc",
   out fields, out from, out groupby, out where, out having, out orderby);