[PYTHON] SQL-Format mit sqlparse

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

Installation

sqlparse wird von Python erstellt und kann mit pip installiert werden.

$ pip install sqlparse

Verwendung im Web

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

Wird in der Befehlszeile verwendet

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.

-rZeilenumbruch / Einzug,-k upperSie können Schlüsselwörter mit groß schreiben. Für andere Optionen-hSie 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";

Als Bibliothek verwenden

sqlparse kann auch als Bibliothek verwendet werden.

Format

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";

Perth

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";

Formatierungsmethode anpassen

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) 

reindentfilterAnstattmyreindentfilterSie 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;

reindentfilterMit 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

SQL-Format mit sqlparse
Formatieren Sie json mit Vim (mit Python)
String-Format mit Python% -Operator
Verwenden der SQL-Datenbank von Azure mit SQL Alchemy
Analysieren Sie Daten im CSV-Format mit SQL
Vektorformatoperation & Konvertierung mit arcpy
Führen Sie Daten im JSON-Format mit Ansible zusammen
[Python] Format, wenn to_csv mit Pandas