2

SQL ステートメントで列名を変更する (テーブル名はそのままにする) シンプルなアプリを実装しています。ステートメントは として渡され、String変更されたものも として返されStringます。データベース接続は関係ありません。

これを実現するために、Apache Calcite の SQL パーサーを使用しています。SQL 文字列を に解析し、名前を変更した を作成する をSqlNode受け入れ、すべてを( を使用して) に書き戻します。SqlVisitorSqlNodeStringSqlNode.toSqlString()

SqlNode問題は、を受け入れながら、解析されたオブジェクトの列とテーブルの違いを見分ける方法がわからないことSqlVisitorです。どちらも として表されSqlIdentifier、同じ を持ちSqlKindます。したがって、が にSqlVisitorアクセスするSqlIdentifierと、それが列であろうとテーブルであろうと名前が変更されます。

private String changeNames(String str) throws SqlParseException {
    SqlShuttle visitor = new SqlShuttle() {
        private String rename(String str) {
            return str + "-test";
        }

        @Override
        public SqlNode visit(SqlIdentifier identifier) {
            SqlIdentifier output = new SqlIdentifier(rename(identifier.getSimple()), identifier.getCollation(), identifier.getParserPosition());
            return output;
        }
    };

    SqlParser.ConfigBuilder configBuilder =  SqlParser.configBuilder();
    configBuilder.setLex(Lex.MYSQL);
    SqlParser.Config config = configBuilder.build();

    SqlParser parser = SqlParser.create(str, config);
    SqlNode parsedStatement = parser.parseQuery(str);
    SqlNode outputNode = parsedStatement.accept(visitor);

    return outputNode.toSqlString(SqlDialect.DUMMY).getSql();
}

例えば

SELECT name, address, age FROM mytablename WHERE age = 23 AND name = 'John'

に変更されます

SELECT `name-test`, `address-test`, `age-test` FROM `mytablename-test` WHERE `age-test` = 23 AND `name-test` = 'John'

SqlIdentifier与えられたものが列かテーブルかをどうやって見分けることができますか?

4

2 に答える 2

1

識別子をテーブルと列に解決し、それらの型を把握するには、Calcite のバリデータ ( ) を使用する必要がありますSqlValidator。バリデーターは SQL の名前解決規則 (FROM 句のエイリアスをサブクエリで参照できるかどうかなど) を理解しますが、パーサーとSqlNodeそれが生成するデータ構造は意図的にそのようなことを認識しませんでした。

バリデーターの 2 つの重要な概念は、スコープ ( SqlValidatorScope) と名前空間 ( SqlValidatorNamespace) です。

スコープは、あなたが立って識別子を解決しようとしている場所です。たとえば、クエリの SELECT 句にいるとします。または、特定のサブクエリの WHERE 句で。さまざまなスコープでさまざまなテーブルと列のコレクションを表示できます。GROUP BY 句と ORDER BY 句でもスコープが異なります。

名前空間はテーブルのようなもので、列のリストがあります。それはテーブルかもしれませんし、FROM 句のサブクエリかもしれません。スコープ内にいる場合は、テーブルのエイリアスを検索し、名前空間を取得してから、そこにある列を確認できます。

SqlShuttleあなたの目的のために、あなたがどのスコープにいるのか、そして識別子をテーブルと列の参照に展開するように頼むことができる場所を正確に知っているバリアントがあれば便利です。残念ながら、まだ誰もそのようなものを構築していません。

于 2016-05-31T19:59:15.427 に答える
0

たまたまcalcite sqlParser少し使ったことがあります。以下に投稿されたスニペットの一部。

  public void convertSelect(SqlSelect root) {
    convertFrom(root.getFrom());
    convertWhere(root.getWhere());
  }

  public void convertFrom(SqlNode from) {
    if (from instanceof SqlJoin) {
      convertFromOfJoinExpression((SqlJoin)from);
    }
  }

  public String extractTableFromJoinNode(SqlNode jnn) {
    if (jnn instanceof SqlBasicCall) {
      SqlBasicCall asExp = (SqlBasicCall)jnn;
      if (asExp.getKind().equals(SqlKind.AS)) {
        extractTableFromJoinNodeAsExpression(asExp);
      }
    }
    return "SomeTableAlias";
  }

一般的に、あなたは声明を受け取りtableますfrom。そして、あなたはcolumns声明をselect出します。

最後になりましたが、calcite多くの最適化ルールを適用してクエリを最適化することを専門としています。必要なもの(列/テーブル名の変換)calciteによっては、最適ではない場合があります。

于 2016-05-23T11:09:53.183 に答える