[PYTHON] Format SQL avec sqlparse

Je cherchais un outil pour formater SQL localement et j'ai trouvé un outil appelé sqlparse alors je l'ai essayé.

https://github.com/andialbrecht/sqlparse

Installation

sqlparse est créé par Python et peut être installé à l'aide de pip.

$ pip install sqlparse

Utilisation sur le Web

sqlparse est utilisé dans un service en ligne appelé SQLFormat, qui vous permet de transmettre et de formater SQL via votre navigateur ou API.

SQLFormat - Online SQL Formatter

Utilisé sur la ligne de commande

Une commande appelée sqlformat qui devient disponible lorsque vous installez sqlparse Si vous entrez SQL en tant que fichier ou entrée standard, le SQL formaté sera généré.

-rSaut de ligne / retrait,-k upperVous pouvez mettre en majuscule les mots-clés avec. Pour d'autres options-hVous pouvez le vérifier en option.

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

Utiliser comme bibliothèque

sqlparse peut également être utilisé comme bibliothèque.

format

sqlparse.format vous permet de formater comme la commande sqlformat.

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

Vous pouvez obtenir le résultat de l'analyse avec sqlparse.parse. sqlparse.parse renvoie un tuple d'instructions représentant des instructions SQL.

>>> parsed = sqlparse.parse(sql)
>>> parsed
(<Statement 'select...' at 0x1077c6160>,)

sqlparse.parse peut également contenir plusieurs instructions SQL dans son entrée comme suit: Dans ce cas, le taple a plusieurs éléments.

>>> sqlparse.parse("select 1; select 2;")
(<Statement 'select...' at 0x1077c6958>,
 <Statement 'select...' at 0x1077c6848>)

Vous pouvez obtenir une liste de jetons avec Statement.tokens.

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

Personnaliser la méthode de formatage

Il existe différents styles de formatage SQL. Par exemple, les noms de colonne dans la clause SELECT peuvent être répertoriés avec une virgule à la fin de la ligne, comme vous le ferez dans la sortie de sqlformat, ou avec une virgule au début de la ligne.

Vous pouvez modifier la méthode de format dans une certaine mesure avec les options de la méthode de format, mais certaines options telles que la position des virgules ne peuvent pas être gérées.

Modifions le filtre que sqlparse contrôle la méthode de formatage afin que les noms de colonne dans la clause SELECT soient disposés avec la virgule au début.

Tout d'abord, l'implémentation est la suivante pour comprendre le flux de traitement de sqlparse.format.

__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()Créer un analyseur avec, stack.run(sql, encoding)Exécute l'analyse SQL donnée dans.



 Vous pouvez ajouter des filtres au FilterStack pour contrôler la façon dont il est formaté. Dans sqlparse.format, `` formatter.validate_options '' et `` formatter.build_filter_stack '' sont définis en fonction de la valeur de l'option.


#### **`stack.postprocess.append(filters.SerializerUnicode())Est un filtre pour convertir une liste de jetons en chaîne.`**

Par souci de simplicité, considérons un exemple qui ne prend pas d'options. Commençons par afficher l'entrée telle quelle.

>>> 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.L'ajout d'un ReindentFilter entraînera l'indentation des résultats.



#### **`Si vous utilisez un filtre qui traite SQL, tel que ReindentFilter, empilez.enable_grouping()Le regroupement de jetons doit être activé dans.`**
```enable_grouping()Le regroupement de jetons doit être activé dans.


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

Pour modifier le comportement afin que la virgule soit au début, créez une classe qui remplace la _process_identifierlist '' de `` ReindentFilter```.

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) 

reindentfilterAu lieu demyreindentfilterVous pouvez voir qu'il est formaté de sorte que la virgule vienne en premier.

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

Même dans la sous-requête, les noms de colonne sont correctement organisés en fonction de la profondeur de retrait, comme indiqué ci-dessous.

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

reindentfilterD'autres méthodes et filtres vous permettent également de contrôler le retrait de la clause where et la casse des mots-clés.

Recommended Posts

Format SQL avec sqlparse
Formater json avec Vim (avec python)
Format de chaîne avec l'opérateur Python%
Utilisation de la base de données SQL d'Azure avec SQL Alchemy
Analyser les données au format CSV à l'aide de SQL
Opération et conversion de format vectoriel avec arcpy
Fusionner les données au format JSON avec Ansible
[Python] Formater quand to_csv avec des pandas