Ich suchte nach einem Tool zum lokalen Formatieren von SQL und fand ein Tool namens sqlparse, also habe ich es ausprobiert.
https://github.com/andialbrecht/sqlparse
sqlparse wird von Python erstellt und kann mit pip installiert werden.
$ pip install sqlparse
sqlparse wird in einem Onlinedienst namens SQLFormat verwendet, mit dem Sie SQL über Ihren Browser oder Ihre API übergeben und formatieren können.
SQLFormat - Online SQL Formatter
Ein Befehl namens sqlformat, der verfügbar wird, wenn Sie sqlparse installieren Wenn Sie SQL als Datei oder Standardeingabe eingeben, wird das formatierte SQL ausgegeben.
-r
Zeilenumbruch / Einzug,-k upper
Sie können Schlüsselwörter mit groß schreiben. Für andere Optionen-h
Sie können es als Option überprüfen.
$ SQL='select t1.c1 A, t2.c2 B from t1 join t2 on t1.id = t2.id where t1.c1 = "HOGE";'
$ echo "$SQL" | sqlformat -r -k upper -
SELECT t1.c1 A,
t2.c2 B
FROM t1
JOIN t2 ON t1.id = t2.id
WHERE t1.c1 = "HOGE";
sqlparse kann auch als Bibliothek verwendet werden.
Mit sqlparse.format können Sie wie mit dem Befehl sqlformat formatieren.
>>> import sqlparse
>>> sql = 'select t1.c1 A, t2.c2 B from t1 join t2 on t1.id = t2.id where t1.c1 = "HOGE";'
>>> print sqlparse.format(sql, reindent=True, keyword_case='upper')
SELECT t1.c1 A,
t2.c2 B
FROM t1
JOIN t2 ON t1.id = t2.id
WHERE t1.c1 = "HOGE";
Sie können das Analyseergebnis mit sqlparse.parse abrufen. sqlparse.parse gibt ein Tupel von Anweisungen zurück, die SQL-Anweisungen darstellen.
>>> parsed = sqlparse.parse(sql)
>>> parsed
(<Statement 'select...' at 0x1077c6160>,)
Die Eingabe von sqlparse.parse kann auch mehrere SQL-Anweisungen wie folgt enthalten: In diesem Fall hat der Taple mehrere Elemente.
>>> sqlparse.parse("select 1; select 2;")
(<Statement 'select...' at 0x1077c6958>,
<Statement 'select...' at 0x1077c6848>)
Sie können eine Liste von Token mit Statement.tokens erhalten.
>>> stmt = parsed[0]
>>> for t in stmt.tokens:
... print type(t), t
...
<class 'sqlparse.sql.Token'> select
<class 'sqlparse.sql.Token'>
<class 'sqlparse.sql.IdentifierList'> t1.c1 A, t2.c2 B
<class 'sqlparse.sql.Token'>
<class 'sqlparse.sql.Token'> from
<class 'sqlparse.sql.Token'>
<class 'sqlparse.sql.Identifier'> t1
<class 'sqlparse.sql.Token'>
<class 'sqlparse.sql.Token'> join
<class 'sqlparse.sql.Token'>
<class 'sqlparse.sql.Identifier'> t2
<class 'sqlparse.sql.Token'>
<class 'sqlparse.sql.Token'> on
<class 'sqlparse.sql.Token'>
<class 'sqlparse.sql.Comparison'> t1.id = t2.id
<class 'sqlparse.sql.Token'>
<class 'sqlparse.sql.Where'> where t1.c1 = "HOGE";
Es gibt viele Arten der SQL-Formatierung. Beispielsweise können Spaltennamen in der SELECT-Klausel am Ende der Zeile mit einem Komma aufgeführt werden, wie Sie in der Ausgabe von sqlformat erhalten, oder sie können am Anfang der Zeile mit einem Komma aufgeführt werden.
Sie können die Formatmethode in gewissem Umfang mit den Optionen der Formatmethode ändern, es gibt jedoch einige Optionen, z. B. die Position von Kommas, die nicht verarbeitet werden können.
Lassen Sie uns den Filter ändern, mit dem sqlparse die Formatierungsmethode steuert, sodass die Spaltennamen in der SELECT-Klausel mit dem Komma am Anfang angeordnet werden.
Erstens ist die Implementierung wie folgt, um den Verarbeitungsablauf von sqlparse.format zu verstehen.
__init__.py
def format(sql, **options):
"""Format *sql* according to *options*.
Available options are documented in :ref:`formatting`.
In addition to the formatting options this function accepts the
keyword "encoding" which determines the encoding of the statement.
:returns: The formatted SQL statement as string.
"""
encoding = options.pop('encoding', None)
stack = engine.FilterStack()
options = formatter.validate_options(options)
stack = formatter.build_filter_stack(stack, options)
stack.postprocess.append(filters.SerializerUnicode())
return ''.join(stack.run(sql, encoding))
stack = engine.FilterStack()Erstellen Sie einen Parser mit, stack.run(sql, encoding)Führt die in angegebene SQL-Analyse aus.
Sie können dem FilterStack Filter hinzufügen, um zu steuern, wie er formatiert wird. In sqlparse.format werden `` `formatter.validate_options``` und` `` formatter.build_filter_stack``` basierend auf dem Wert der Option festgelegt.
#### **`stack.postprocess.append(filters.SerializerUnicode())Ist ein Filter zum Konvertieren einer Liste von Token in eine Zeichenfolge.`**
Betrachten wir der Einfachheit halber ein Beispiel, das keine Optionen enthält. Beginnen wir mit der Ausgabe der Eingabe wie sie ist.
>>> from sqlparse import engine
>>> stack = engine.FilterStack()
>>> stack.postprocess.append(filters.SerializerUnicode())
>>> print stack.run(sql).next()
select t1.c1 A, t2.c2 B from t1 join t2 on t1.id = t2.id where t1.c1 = "HOGE";
sqlparse.filters.Durch Hinzufügen eines ReindentFilter werden die Ergebnisse eingerückt.
#### **`Wenn Sie einen Filter verwenden, der SQL verarbeitet, z. B. ReindentFilter, stapeln Sie.enable_grouping()Die Token-Gruppierung muss in aktiviert sein.`**
```enable_grouping()Die Token-Gruppierung muss in aktiviert sein.
```pycon
>>> from sqlparse.filters import ReindentFilter
>>> stack = engine.FilterStack()
>>> stack.enable_grouping()
>>> stack.stmtprocess.append(ReindentFilter())
>>> stack.postprocess.append(filters.SerializerUnicode())
>>> print stack.run(sql).next()
select t1.c1 A,
t2.c2 B
from t1
join t2 on t1.id = t2.id
where t1.c1 = "HOGE";
Um das Verhalten so zu ändern, dass das Komma am Anfang steht, erstellen Sie eine Klasse, die die _process_identifierlist``` des
ReindentFilter``` überschreibt.
from sqlparse.sql import Function
class MyReindentFilter(ReindentFilter):
def _process_identifierlist(self, tlist):
identifiers = list(tlist.get_identifiers())
if len(identifiers) > 1 and not tlist.within(Function):
first = identifiers[0]
self.indent += 1
tlist.insert_before(first, self.nl())
self.offset -= 1
tlist.insert_after(first, self.nl())
for token in identifiers[1:len(identifiers)-1]:
prev = tlist.token_prev(tlist.token_index(token), False)
if prev and prev.is_whitespace():
prev.value = ''
tlist.insert_after(token, self.nl())
last = identifiers[-1]
prev = tlist.token_prev(tlist.token_index(last), False)
if prev and prev.is_whitespace():
prev.value = ''
self.offset += 1
self.indent -= 1
self._process_default(tlist)
reindentfilter
Anstattmyreindentfilter
Sie können sehen, dass es so formatiert ist, dass das Komma an erster Stelle steht.
>>> stack = engine.FilterStack()
>>> stack.enable_grouping()
>>> stack.stmtprocess.append(MyReindentFilter())
>>> stack.postprocess.append(filters.SerializerUnicode())
>>> print stack.run(sql).next()
select
t1.c1 A
,t2.c2 B
from t1
join t2 on t1.id = t2.id
where t1.c1 = "HOGE"
and t2.c2 = 1;
Selbst in der Unterabfrage sind die Spaltennamen gemäß der Einzugstiefe wie unten gezeigt korrekt angeordnet.
>>> print stack.run('select a, b, c FROM (select a, b, c FROM t1) t2;').next()
select
a
,b
,c
FROM
(select
a
,b
,c
FROM t1) t2;
reindentfilter
Mit anderen Methoden und Filtern können Sie auch steuern, wie die where-Klausel eingerückt wird und wie Schlüsselwörter verwendet werden.
Recommended Posts