4

ここ数週間で PyParsing を学んでいます。これを使用して、SQL ステートメントからテーブル名を取得する予定です。http://pyparsing.wikispaces.com/file/view/simpleSQL.pyを見てきました。ただし、select ステートメントのすべての部分を解析しようとしているのではなく、テーブル名だけを探しているので、文法を単純に保つつもりです。また、Teradata のような市販の最新データベースの完全な文法を定義することも非常に複雑です。

#!/usr/bin/env python

from pyparsing import *
import sys

semicolon = Combine(Literal(';') + lineEnd)
comma = Literal(',')
lparen = Literal('(')
rparen = Literal(')')

# Keyword definition
update_kw, volatile_kw, create_kw, table_kw, as_kw, from_kw, \
where_kw, join_kw, left_kw, right_kw, cross_kw, outer_kw, \
on_kw , insert_kw , into_kw= \
    map(lambda x: Keyword(x, caseless=True), \
        ['UPDATE', 'VOLATILE', 'CREATE', 'TABLE', 'AS', 'FROM',
         'WHERE', 'JOIN' , 'LEFT', 'RIGHT' , \
         'CROSS', 'OUTER', 'ON', 'INSERT', 'INTO'])

# Teradata SQL allows SELECT and well as SEL keyword
select_kw = Keyword('SELECT', caseless=True) | Keyword('SEL' , caseless=True)

# list of reserved keywords
reserved_words = (update_kw | volatile_kw | create_kw | table_kw | as_kw |
                  select_kw | from_kw | where_kw | join_kw |
                  left_kw | right_kw | cross_kw | on_kw | insert_kw |
                  into_kw)

# Identifier can be used as table or column names. They can't be reserved words
ident = ~reserved_words + Word(alphas, alphanums + '_')

# Recursive definition for table
table = Forward()
# simple table name can be identifer or qualified identifier e.g. schema.table
simple_table = Combine(Optional(ident + Literal('.')) + ident)
# table name can also a complete select statement used as table
nested_table = lparen.suppress() + select_kw.suppress() + SkipTo(from_kw).suppress() + \   
               from_kw.suppress() + table + rparen.suppress()
# table can be simple table or nested table
table << (nested_table | simple_table)
# comma delimited list of tables
table_list = delimitedList(table)
# Building from clause only because table name(s) will always appears after that
from_clause = from_kw.suppress() + table_list


txt = """
SELECT p, (SELECT * FROM foo),e FROM a, d, (SELECT * FROM z), b
"""
for token, start, end in from_clause.scanString(txt):
    print token

ここで言及する価値のあること。「SkipTo(from_kw)」を使用して、SQL ステートメントの列リストをジャンプします。これは主に、コンマで区切られた識別子のリスト、多くの関数名、DW 分析関数などである可能性がある列リストの文法を定義することを避けるためです。この文法を使用すると、上記のステートメントだけでなく、SELECT 列リストまたはテーブル リストの任意のレベルのネストを解析できます。

['foo']
['a', 'd', 'z', 'b']

SELECT に where 句がある場合、問題に直面しています:

nested_table = lparen.suppress() + select_kw.suppress() + SkipTo(from_kw).suppress() + \   
               from_kw.suppress() + table + rparen.suppress()

WHERE 句がある場合、同じステートメントは次のようになります: SELECT ... FROM a,d , (SELECT * FROM z WHERE (c1 = 1) and (c2 = 3)), pに:

nested_table = lparen.suppress() + select_kw.suppress() + SkipTo(from_kw).suppress() + \   
               from_kw.suppress() + table + Optional(where_kw + SkipTo(rparen)) + rparen

しかし、これは「c = 1」に続く右括弧に一致するため、機能しません。私が知りたいのは、「SELECT * FROM z...」の直前の左括弧に一致する右括弧にスキップする方法です.PyParsingを使用してそれを行う方法がわかりません.

また、複雑なネストされた SQL からテーブル名を取得する最善の方法についてアドバイスを求めます。どんな助けでも本当に感謝しています。

ありがとうアビジット

4

1 に答える 1

9

ネストされた SELECT も解析しようとしていることを考えると、かなり完全な SQL パーサーを作成せずにはいられないと思います。幸いなことに、Pyparsing wiki の例ページ select_parser.py に、より完全な例があります。それがあなたをさらに前進させることを願っています。

于 2013-06-04T20:09:09.990 に答える