2011-12-18 14 views
0

複数のSELECT文を持つSQLファイルがあります。必要な場合は、SELECT文のWHERE句があればそれを調べる必要があります。SELECTクエリのWHERE句を見つける

Select col1,col2 
from table t1 
where col1=2 and 
convert(something)='abc' 

は、クエリ上記の結果として、私は、where句完了必要と私は可能な限り一般的なように、(サブクエリは、今のように無視することができます)、それを作りたいです。私はこのコードや作業に利用できるソースコードから始めるためのいくつかの指針が必要です。どんな助けもありがとうございます。

ソリューション

私は、SQLスキーマのDOMパーサを使用して解決策を見つけ出すことができました。以下は、同じものに使用されているコードです。同じテストをした場合の動作上の問題は、必要に応じて変更できます。しかし、このコードは、指定されたSQLスクリプトが構文的に正しい場合にのみ機能します。

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

     namespace ConsoleApplication1 
    { 
     internal class Program 
     { 
      public static void Main(string[] args) 
      { 
       var sql = @" SELECT Count(*) AS 'hjkhskj' 
    FROM ABC 

    SELECT DISTINCT xx AS 'kljklj' 
    FROM XYZ 
    WHERE a NOT IN (SELECT b 
          FROM RST) 
    ORDER BY 1 

    BEGIN TRANSACTION 

    DELETE FROM ABC 
    WHERE a NOT IN (SELECT b 
          FROM XYZ) 

    IF @@ERROR <> 0 
     BEGIN 
      ROLLBACK TRANSACTION 

      GOTO GTY 
     END 
    ELSE 
     COMMIT TRANSACTION 

    SELECT DISTINCT x AS 'SRTT jfkfk' 
    FROM PQR a 
    WHERE NOT EXISTS (SELECT m 
         FROM MYTU u 
         WHERE u.x = m) 
    ORDER BY 1 "; 

       //StreamReader sr = new StreamReader(@"sqlFileName.sql"); 
       //Parse(sr.ReadToEnd()); 
       Parse(sql); 
       //Console.ReadKey(); 
      } 
      public static List<string> Parse(string sql) 
      { 
       TSql100Parser parser = new TSql100Parser(false); 
       IScriptFragment fragment; 
       IList<ParseError> errors; 
       fragment = parser.Parse(new StringReader(sql), out errors); 
       TSqlScript tsSs = (TSqlScript)fragment; 
       IList<TSqlParserToken> tknList = null; 

       var whereStartIndex = 0; 
       var whereEndIndex = 0; 
       var queryStartIndex = 0; 
       var queryEndIndex = 0; 
       WhereClause whereClause = null; 
       StreamWriter sw = new StreamWriter("test.txt"); 
       for (int i = 0; i < tsSs.Batches.Count; i++) 
       { 
        tknList = tsSs.Batches[i].ScriptTokenStream; 
        for (int j = 0; j < tsSs.Batches[i].Statements.Count; j++) 
        { 
         queryStartIndex = tsSs.Batches[i].Statements[j].FirstTokenIndex; 
         queryEndIndex = tsSs.Batches[i].Statements[j].LastTokenIndex; 
              switch (tsSs.Batches[i].Statements[j].GetType().Name) 
         { 
          case ("UpdateStatement"): 
           whereClause = ((UpdateStatement)(tsSs.Batches[i].Statements[j])).WhereClause; 
           break; 
          case ("SelectStatement"): 
           whereClause = ((QuerySpecification)((SelectStatement)(tsSs.Batches[i].Statements[j])).QueryExpression).WhereClause; 
           break; 
          case ("DeleteStatement"): 
           whereClause = ((DeleteStatement)(tsSs.Batches[i].Statements[j])).WhereClause; 
           break; 
          default: whereClause = null; 
           break; 
         } 
         if (whereClause != null) 
         { 
          whereStartIndex = whereClause.FirstTokenIndex; 
          whereEndIndex = whereClause.LastTokenIndex; 
          StringBuilder sbQuery = new StringBuilder(); 
          StringBuilder sbWhere = new StringBuilder(); 
          for (int k = queryStartIndex; k < queryEndIndex; k++) 
          { 
           sbQuery.Append(tknList[k].Text); 
           if (whereStartIndex <= k && k <= whereEndIndex) 
           { 
            sbWhere.Append(tknList[k].Text); 
           } 
          } 
          sw.WriteLine(sbQuery.ToString()); 
          sw.WriteLine("************************************************************************************************************"); 
          sw.WriteLine(sbWhere.ToString()); 
          sw.WriteLine("************************************************************************************************************"); 
          sbQuery = null; 
          sbWhere = null; 
         } 

        } 

       } 
       sw.Close(); 
       System.Diagnostics.Process.Start("test.txt"); 
       if (errors != null && errors.Count > 0) 
       { 
        List<string> errorList = new List<string>(); 
        foreach (var error in errors) 
        { 
         errorList.Add(error.Message); 
        } 
        return errorList; 
       } 
       return null; 
      } 
     } 
    } 
+2

したがって、SQL文を含むテキストファイルを解析しようとしていますか?もしそうなら、*パーサ*はどの言語で書かれますか? –

+0

コードの作成にどの言語を使用していますか? – Naval

+0

パラメータ化されたTSQL? –

答えて

1

サブクエリのないステートメントの場合は、単純なアルゴリズムから始めましょう。

まず、すべての空白(スペース、タブ、改行など)を1つのスペースに置き換えます。

次に、" where "(周囲のスペースに注意してください)の最初のオカレンスを見つけます。これが開始点です(もちろん、最初のスペースは除く)。

そこから、最初の" order by "または" group by "を探します。これが終了点です。そのテキストが存在しない場合、終点は文字列自体の終わりです。非サブクエリの広大な大部分をカバーする必要がありqueriesandが表示されたら、あなたは終了条件により多くの可能性を追加することができます

def getWhereClause (query): 
    query = query.replaceAll ("[ \t\n]*", " ") 
    whereClause = "" 
    while query is not "": 
     if query.startsWithIgnoreCase (" where "): 
      whereClause = "where " 
      query = query.substring (7) 
      next while 
     if query.startsWithIgnoreCase (" order by ") or 
     if query.startsWithIgnoreCase (" group by "): 
      return whereClause 
     if whereClause != "": 
      whereClause = whereClause + query.substring (0,1) 
     query = query.substring (1) 
    return whereClause 

:擬似コードで

、それは次のようになります。

これらのテキストの検索と操作は実際にはの外側で行われます.の引用符付きの領域です。これは、の適切な解決策が必要な場合は、擬似コードで示されているように単純な文字列検索ではなく、文のミニパーサーが必要であることを意味します。

私はそれがうまくあなたが私を信じて、完全なSQLパーサの合併症なしに必要な正確に何を行う可能性があるため、痛みを伴うから、些細な運動ではない(むしろ迅速かつ汚い)として1をこのソリューションを提唱経験:-)

+0

私はそれほど複雑でなくてもできるだけ簡単にしてください。それ以外の場合は非常に苦しくなります。または、SQLを解析するためのライブラリがありますか?私は考えていませんが、単なる考えです。もう少し完全な正規表現についてはどうでしょうか? – peter

+0

当初、私はRegExのみを考えましたが、適切でないRegExを見つけることは、より時間と精巧なテストを必要とするというステートメントの非決定的な性質を考慮すると、そして、私はこのようなものがいくつかの中間目的のためだけに必要なので、私はPaxdiabloを考え始めました。アイデア@paxdiabloをありがとう。第三者のパーサがありますが、私のニーズに応じて実現不可能です。 – AjayK

+0

最初はこの解決策は良く見えますが、それは簡単ですが、where節の終わりには、それが十分であればいくつかのキーワードを壊すことが提案されています。しかし、この場合、我々は多くの場合を経なければならない。私が考えていたのは、AND/ORキーワードの後に​​ペアがなくなったときにそのブレークを実行することでした。唯一の問題は単純なケースです。ペアを見つけるのは簡単ですが、これに関する考え。 – AjayK

0

文字列として考える場合は、 ""でトークンに分割し、各トークン(lowercased)を "where"と比較することができます。 "order"の後に "by"または "group"が続いて "by"が続く "group"/"order"トークンは、あなたの句が終わった後のトークンです(これが見つからない場合、あなたの句は文字列)。これらのトークンインデックスを文字列の文字インデックスに変換し、部分文字列を取得して節を取得します。

String query; 
boolean hasWhere = false; 
boolean hasGroupOrOrder = false; 
String[] query_tokens = query.split(" "); 

// ---------- FIRST GET THE TOKENS WHERE THE WHERE CLAUSE AND GROUP BY/ORDER BY START ---- 
// we want to find the tokens in the string where the where clause starts and where the group by/order by begins -- if there isn't one then endToken will remain -1 
int startToken = -1; 
int endToken = -1; 

for(int i = 0; i < query_tokens.length(); i++) 
{ 
    if(!hasWhere) 
    { 
     if(query[i].toLowerCase().equals("where")) 
{ 
      begin = i; 
      hasWhere = true; 
} 

    } 
    else 
    { 
     if(!hasGroupOrOrder) 
     { 
      if(query[i].toLowerCase().equals("group") || query[i].toLowerCase().equals("order")) 
       hasGroupOrOrder = true; 
     } 
     else 
     { 
      if(query[i].toLowerCase().equals("by")) 
      { 
       end = i - 1; 
      }else 
       { 
         hasGroupOrOrder = false; 
       } 
     } 
    } 
} 

// we now know the tokens necessary for the query, now we turn these into the character indexes that can be used to take the where clause substring from the query string 

String whereClause; 
int startIndex = 0; 
int endIndex = 0; 

// ---------- NOW GET START INDEX OF "WHERE" IN STRING ---- we want to find the beginIndex " " character as the where token starts here 
for(int i = 0; i < startToken; i++) 
{ 
     startIndex = String.indexOf(" ", startIndex); 
} 

startIndex += 1; 
// since the startIndex will refer the " " right before "where" we need to increment this by 1 
// if you don't need the "where " you can strip this off as well... 

// ---------- NOW GET END INDEX EITHER END OF STRING OR BEFORE WHERE/GROUP BY STARTS ---- 
if(endToken == -1) 
{ 
// this is the case when the where clause extends to end of string 
    endIndex = query.length() - 1; 
} 
else 
{ 
    // we want to find the endIndex " " character as the group by/order by starts here 
    for(int i = 0; i < endIToken; i++) 
    { 
     endIndex = String.indexOf(" ", endIndex); 
    } 
    endIndex -= 1; 
// since the endIndex will be at the " " before the where by/order by we must reduce this by 1 to be end of where clause 
} 

// ---------- NOW GET SUBSTRING! ---- 
whereClause = query.substring(startIndex, endIndex); 
+0

それは私のためのキーワードを見つけることができます。しかし、私の仕事はWHEREキーワードがないWHERE CLAUSEを完全に見つけることです。 – AjayK

+0

私は上記のpaxdiablosのコメントと、あなたの句の終わりが最初の "group by"または "order by"句によって通知されることになります。そうでない場合、where句は文字列の最後まで拡張されます。だから、グループのどこかでグループの前に、by、orderのあとに続けて、あなたのwhere句を作るトークンがあります。 – vassilo

+0

WHERE句の最後にORDER BYまたはGROUP BYを付けることは必須ではありません。それが唯一の問題です。本当に複雑な可能性のあるエンディングをすべて見つけてください。 – AjayK