SQL-Analyse mit Pyparsing
Ich lerne PyParsing in den letzten Wochen. Ich plane, es zu verwenden, um Tabellennamen aus SQL-Anweisungen zu erhalten. Ich habe angeschauthttp://pyparsing.wikispaces.com/file/view/simpleSQL.py. Aber ich beabsichtige, die Grammatik einfach zu halten, weil ich nicht jeden Teil der select-Anweisung analysieren möchte, sondern nur nach Tabellennamen suche. Es ist auch ziemlich kompliziert, die vollständige Grammatik für jede im Handel erhältliche moderne Datenbank wie Teradata zu definieren.
#!/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
Eine Sache, die hier erwähnenswert ist. Ich benutze "SkipTo (from_kw)", um über die Spaltenliste in der SQL-Anweisung zu springen. Dies dient in erster Linie dazu, die Definition der Grammatik für Spaltenlisten zu vermeiden, bei denen es sich um eine durch Kommas getrennte Liste von Bezeichnern, vielen Funktionsnamen, DW-Analysefunktionen und was nicht handeln kann. Mit dieser Grammatik kann ich die obige Anweisung sowie jede Verschachtelungsebene in der SELECT-Spaltenliste oder der Tabellenliste analysieren.
['foo']
['a', 'd', 'z', 'b']
Ich stehe vor einem Problem, wenn SELECT die where-Klausel hat:
nested_table = lparen.suppress() + select_kw.suppress() + SkipTo(from_kw).suppress() + \
from_kw.suppress() + table + rparen.suppress()
Wenn die WHERE-Klausel vorhanden ist, sieht die gleiche Anweisung möglicherweise so aus: SELECT ... FROM a, d, (SELECT * FROM z WHERE (c1 = 1) und (c2 = 3)), p Ich dachte daran, die Definition von "nested_table" zu ändern zu:
nested_table = lparen.suppress() + select_kw.suppress() + SkipTo(from_kw).suppress() + \
from_kw.suppress() + table + Optional(where_kw + SkipTo(rparen)) + rparen
Dies funktioniert jedoch nicht, da es nach "c = 1" in der rechten Klammer steht. Was ich wissen möchte, ist, wie man zu der rechten Klammer springt, die der linken Klammer vor "SELECT * FROM z ..." entspricht. Ich weiß nicht, wie man es mit PyParsing macht
Auch in einer anderen Hinsicht möchte ich einige Ratschläge geben, wie Tabellennamen aus komplexen verschachtelten SQLs am besten abgerufen werden können. Jede Hilfe wird sehr geschätzt.
Danke Abhijit