人力検索はてな
モバイル版を表示しています。PC版はこちら
i-mobile

javaプログラムでテキストを一行ずつ読み込んでSQL文がある行を検出して
そのSQL文中から複数テーブル指定時にテーブル別名の無いがあるがチェックしてテーブル別名が無い行を検出したいです。

正規表現でSQL文を検出するところまではできましだが、

SQL文中からテーブル別名を指定してある行を検索する事ができません。
例えば;
List.add("select * from test1;");
List.add("select * from test1 t1,test2;");
List.add("select * from test1 t1 ,test2 t2 where testId=1;");
List.add("select * from test1 ,test2 ORDER BY testId;");
List.add("select * from test1 as t1, test2 as t2;");

こんな行がListの中に入ってるとして、出力したいのは2,3番目の行、
つまりテーブル別名を指定してない行を検出したいです。

正規表現でやろとしてましたがうまくできなくて、
for (int j = 0; j < list.size(); j++) {
//とりあえずSQL文からfrom句がある行を検出
if (((String) list.get(j)).toLowerCase().indexOf("from") != -1) {
System.out.println(list.get(j));
}
}
この後からとうすればいいかわかりません
どなたかご教授していただければ幸いです。

よろしくお願いいたします。

●質問者: kimu_507
●カテゴリ:ウェブ制作
✍キーワード:ADD as Java Out SELECT
○ 状態 :終了
└ 回答数 : 3/3件

▽最新の回答へ

1 ● aside
●27ポイント

これででました

List lst = new ArrayList();
lst.add("select * from test1;");
lst.add("select * from test1 t1,test2;");
lst.add("select * from test1 t1 ,test2 t2 where testId=1;");
lst.add("select * from test1 ,test2 ORDER BY testId;");
lst.add("select * from test1 as t1, test2 as t2;");
for (int j = 0; j < lst.size(); j++) {
String sVal = (String) lst.get(j);
int iStart = sVal.toLowerCase().indexOf("from");
String sTemp = sVal.substring(iStart+5);
int iTemp = sTemp.indexOf(",");
String sTemp1 = "";
if (iTemp == -1) {
sTemp1 = sTemp.substring(0);
} else {
sTemp1 = sTemp.substring(0, iTemp);
}
int iTemp1 = sTemp1.indexOf(" ");
if ( iTemp1 == -1 || sTemp1.replaceAll(" ", "").length() == sTemp.substring(0, iTemp1).length()) {
System.out.println(sVal);
}
}
◎質問者からの返答

本当に有難うございます、助かりました。早速参考させて頂きます。


2 ● kn1967
●27ポイント

一度には無理そうなので、FROM句要素の抜出と、要素内の調査の二段階にしています。

(動作確認用出力の部分は、それぞれの部分が何をやっているかという確認が済んだら削除してください。)

import java.lang.*;
import java.io.*;
import java.util.*;
import java.util.regex.Pattern;
import java.util.regex.Matcher;

class TableAlias {
 public static void main(String[] args) throws IOException {
 ArrayList List = new ArrayList(); // サンプルデータ準備
 List.add("select * from test1;");
 List.add("select * from test1 t1,test2;");
 List.add("select * from test1 t1 ,test2 t2 where testId=1;");
 List.add("select * from test1 ,test2 ,test3 ORDER BY testId;");
 List.add("select * from test1 as t1, test2 as t2;");

 String p1Str = "from\\s(.*?)(\\s+where|\\s+order|\\s*;)"; // FROM句要素抜出用パターン
 Pattern p1 = Pattern.compile(p1Str, Pattern.CASE_INSENSITIVE);
 String p2Str = "^\\s*[^\\s]+\\s+[^\\s]+"; // Alias存在確認用パターン
 Pattern p2 = Pattern.compile(p2Str, Pattern.CASE_INSENSITIVE);
 
 int ListCount = List.size(); // リスト件数
 for (int j = 0; j < ListCount; j++) { // FROM句要素の抜き出し
 System.out.println("\nSQL => " + List.get(j)); // 動作確認用出力(SQL全体)
 Matcher m1 = p1.matcher((String)List.get(j));
 if (m1.find()) {
 System.out.println("FROM => " + (String)m1.group(1)); // 動作確認用出力(FROM句要素全体)
 String[] E = m1.group(1).split(","); // FROM句要素分解
 int ECount = E.length; // FROM句要素数取得
 for (int k = 0; k < ECount; k++) { // FROM句要素抜出
 Matcher m2 = p2.matcher(E[k]);
 if (m2.find()) {
 System.out.println("Match! " + List.get(j)); // AliasがあればSQLを出力してループ脱出
 break;
 }
 }
 }
 }
 }
}
◎質問者からの返答

分かりやすいコード書いて頂き、有難うございます。すごく勉強になりました。

もう一つうかがいしてよろしいでしょうか、、

例えば from句の 後ろに GROUP BY 、HABING BY 、INNER JOIN、

LEFT JOIN 、RIGHT JOIN 、ON 、UNION などがあるときもテーブル別名が無いもが検出

できますが?すみません こんな質問で、まだSQL文に対して詳しく知らない物で、、、

よろしくお願いいたします。


3 ● kn1967
●26ポイント ベストアンサー

「FROM句要素抜出用パターン」の部分に適宜追加してやることで対応しますが、

SQL構文はRDBMS毎に構文が異なるため、それぞれに合わせて、

「FROMの次に来るであろう予約語」を列挙しておく必要があります。

havingはgroup byの次に来るものなので「FROMの次」には当てはまりません。

String p1Str = "from\\s(.*?)(\\s+where|\\s+order|\\s*;)"; // FROM句要素抜出用パターン// MySQL向け
String p1Str = "(from|join)\\s(.*?)"
 + "(\\s+inner|\\s+cross|\\s+natural|\\s+straight_join|\\s+left|\\s+right|\\s+on|\\s+using|\\s+where|\\s+group"
 + "|\\s+union|\\s+order|\\s+limit|\\s+procedure|\\s+for|\\s*;)";
   ↓
// PostgreSQL向け
String p1Str = "(from|join)\\s(.*?)"
 + "(\\s+inner|\\s+cross|\\s+left|\\s+right|\\s+full|\\s+on|\\s+using|\\s+where|\\s+group"
 + "|\\s+window|\\s+union|\\s+intersect|\\s+except|\\s+order|\\s+limit|\\s+offset|\\s+fetch|\\s+for|\\s*;)";

他にもORACLE、SQLServerなど、それぞれ異なりますが、

RDBMS毎に違いがあるという点をしめしたかっただけなので、以上2点だけにします。

※以上2点、簡易的動作テストしか行っておらず、考えうる全ての組み合わせに対応できるかどうか未確認。



その他にも

・FROM句が何度出てきても良いように繰り返し検索するように変更。

・末尾が ; で終わってないと検索してくれない場合があるので修正。

・別名があるものを抜き出したいのか(質問文の「出力したいのは2,3番目の行」、

「テーブル別名を指定してある行を検索する事ができません」)、

無いものを抜き出したいのか(質問文の「テーブル別名を指定してない行を検出したい」や、

コメント文の「テーブル別名が無いも(の)」)判らなくなってきたので、

どちらでも対応できるように変更。AliasCountとNoAliasCountの値で適宜対応してください。

if (m1.find()) {
  中略
}
   ↓
int AliasCount = 0; // Alias有の数
int NoAliasCount = 0; // Alias無の数
while (m1.find()) {
 System.out.println("FROM => " + (String)m1.group(1)); // 動作確認用出力(FROM句要素全体)
 String[] E = m1.group(1).split(","); // FROM句要素分解
 int ECount = E.length; // FROM句要素数取得
 for (int k = 0; k < ECount; k++) { // FROM句要素抜出
 Matcher m2 = p2.matcher(E[k]);
 if (m2.find()) {
 System.out.println("Element => " + E[k] + "有"); // 動作確認用出力
 AliasCount++;
 } else {
 System.out.println("Element => " + E[k] + "無"); // 動作確認用出力
 NoAliasCount++;
 }
 }
}
System.out.println("有" + AliasCount + ":無" + NoAliasCount + ":" + List.get(j));
◎質問者からの返答

ソースを見て感動しました。本当にすごい方ですね。!!

やっぱり完璧の検出する事は結構難しですね。

でもおかげ様ですごく勉強になりました。

頑張って完璧に出力できるようにします。

本当に有難うございました。

これからもよろしくお願いいたします

関連質問


●質問をもっと探す●



0.人力検索はてなトップ
8.このページを友達に紹介
9.このページの先頭へ
対応機種一覧
お問い合わせ
ヘルプ/お知らせ
ログイン
無料ユーザー登録
はてなトップ