2009-11-22 3 views
18

J'essaye d'analyser SQL en utilisant le TSql100Parser fourni par microsoft. En ce moment, j'ai un peu de mal à l'utiliser de la façon dont il semble être destiné à être utilisé. En outre, le manque de documentation n'aide pas. (exemple: http://msdn.microsoft.com/en-us/library/microsoft.data.schema.scriptdom.sql.tsql100parser.aspx)en utilisant le TSqlParser

Lorsque j'exécute une instruction SELECT simple via l'analyseur, elle renvoie une collection de TSqlStatements qui contient une instruction SELECT. Le problème est que l'instruction TSqlSelect ne contient pas d'attributs tels qu'une clause WHERE, même si la clause est implémentée en tant que classe. http://msdn.microsoft.com/en-us/library/microsoft.data.schema.scriptdom.sql.whereclause.aspx L'analyseur reconnaît la clause WHERE en tant que telle, en examinant le flux de jetons. Donc, ma question est, est-ce que j'utilise correctement l'analyseur? En ce moment, le flux symbolique semble être la caractéristique la plus utile de l'analyseur ...

Mon projet d'essai:

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); 
      } 
} 
+0

Pouvez-vous poster le fragment de code que vous utilisez pour créer le lecteur/liste que vous .Parse inG avec la façon dont vous itérez la déclarations analysées? Plus vous pouvez poster, mieux c'est ... – chadhoc

+0

Vous pouvez utiliser les dernières [TSql110Parser] (http://technet.microsoft.com/fr-fr/library/microsoft.sqlserver.transactsql.scriptdom.aspx) –

Répondre

25

Oui, vous utilisez correctement l'analyseur.

Comme Damien_The_Unbeliever souligne, dans le SelectStatement il y a une propriété QueryExpression qui sera un QuerySpecification objet pour votre troisième instruction select (avec la clause WHERE). Ceci représente le vrai bit SELECT de la requête (alors que l'objet SelectStatement externe que vous regardez a juste la clause 'WITH' (pour les CTE), la clause 'FOR' (pour XML), 'ORDER BY »et d'autres bits)

le QuerySpecification objet est l'objet avec les FromClauses, WhereClause, GroupByClause etc.

vous pouvez accéder à votre clause WHERE en utilisant:

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

qui a une si quelqu'un atterrit ici propriété SearchCondition etc., etc.

+0

Merci! J'ai eu du mal à trouver des informations utiles sur le sujet, donc cette réponse sera probablement utile à beaucoup d'autres développeurs en détresse aussi. – Opflash

+0

@doza Aide génial. Cette distribution m'a sauvé des heures de travail à travers les jetons manuellement. – Bora

+0

Comment mettez-vous d'abord votre instruction sql dans l'instruction (SelectStatement)? – Demodave

4

Coup d'oeil rapide autour indiquerait qu'il contient un QueryExpression, qui pourrait être un QuerySpecification, qui a la clause Where attachée à elle.

5

et veut savoir comment obtenir l'ensemble des éléments d'une instruction select le code suivant expliquent que:

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); 
+0

Il n'y a pas d'option "spec.FromClauses" – Demodave

1

J'ai dû diviser une instruction SELECT en morceaux. Mon but était de COMPTER le nombre d'enregistrements qu'une requête retournera. Ma première solution était de construire une sous requête telle que

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

Le problème est que, dans ce cas, la clause ORDER soulève l'erreur « La clause ORDER BY n'est pas valable dans les vues, fonctions en ligne, les tables dérivées, sous requêtes, et expressions de table communes, sauf si TOP ou FOR XML est également spécifié "

J'ai donc construit un analyseur qui divisait une instruction SELECT en fragments à l'aide de la classe 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; 
    } 
} 

Et pour utiliser cette classe simplement:

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); 
Questions connexes