Python SQL sorgu dizesi biçimlendirmesi


102

Bir sql sorgu dizesini biçimlendirmenin en iyi yolunu bulmaya çalışıyorum. Uygulamamda hata ayıklarken, tüm sql sorgu dizelerini dosyalamak için oturum açmak istiyorum ve dizenin uygun şekilde biçimlendirilmiş olması önemlidir.

seçenek 1

def myquery():
    sql = "select field1, field2, field3, field4 from table where condition1=1 and condition2=2"
    con = mymodule.get_connection()
    ...
  • Bu, sql dizesini yazdırmak için iyidir.
  • Dizenin uzun olması ve 80 karakterlik standart genişliğe uymaması iyi bir çözüm değildir.

seçenek 2

def query():
    sql = """
        select field1, field2, field3, field4
        from table
        where condition1=1
        and condition2=2"""
    con = mymodule.get_connection()
    ...
  • Burada kod açıktır, ancak sql sorgu dizesini yazdırdığınızda tüm bu can sıkıcı beyaz boşlukları alırsınız.

    u '\ n alan1, alan2, alan3, alan4 \ n_ _ ___ tablosundan \ n _ ___ burada koşul1 = 1 \ n _ ___ _ ve koşul2 = 2'

Not: _Editör tarafından kırpıldığı için beyaz boşlukları alt çizgi ile değiştirdim

3. Seçenek

def query():
    sql = """select field1, field2, field3, field4
from table
where condition1=1
and condition2=2"""
    con = mymodule.get_connection()
    ...
  • Bu seçeneği sevmiyorum çünkü iyi tablo haline getirilmiş kodun açıklığını bozuyor.

4. seçenek

def query():
    sql = "select field1, field2, field3, field4 " \
          "from table " \
          "where condition1=1 " \
          "and condition2=2 "
    con = mymodule.get_connection()    
    ...
  • Bu seçeneği sevmiyorum çünkü her satırdaki fazladan yazım ve sorguyu düzenlemek de zor.

Benim için en iyi çözüm Seçenek 2 olurdu, ancak sql dizesini yazdırdığımda fazladan boşluklardan hoşlanmıyorum.

Başka seçenek biliyor musun?


Bu, Psycopg insanlarının sorgu dizelerinin bileşimine saf bir yaklaşım dediği şeydir, örneğin dizge birleştirme kullanarak - initd.org/psycopg/docs/… . Bunun yerine, SQL enjeksiyon saldırılarını önlemek ve Python nesnelerini SQL değişmezlerine otomatik olarak dönüştürmek için sorgu parametrelerini kullanın. stackoverflow.com/questions/3134691/…
Matthew Cornell

Bu soru aslında SQL sorgularına özgü değildir, ancak genellikle Python'da çok satırlı dizelerin biçimlendirilmesi için geçerlidir. SQL etiketi kaldırılmalıdır.
cstork

Yanıtlar:


136

Bu kadar eski bir ileti dizisine yazdığım için özür dilerim - ama aynı zamanda pitonik 'en iyi' tutkusunu paylaşan biri olarak çözümümüzü paylaşacağımı düşündüm.

Çözüm, Seçenek 2 ve Seçenek 4 arasında bir yerde nitelendirilebilen python'un Dize Değişmez Birleştirme ( http://docs.python.org/ ) kullanarak SQL ifadeleri oluşturmaktır.

Kod Örneği:

sql = ("SELECT field1, field2, field3, field4 "
       "FROM table "
       "WHERE condition1=1 "
       "AND condition2=2;")

İle de çalışır f-dizeleri :

fields = "field1, field2, field3, field4"
table = "table"
conditions = "condition1=1 AND condition2=2"

sql = (f"SELECT {fields} "
       f"FROM {table} "
       f"WHERE {conditions};")

Artıları:

  1. Pythonic 'iyi tablo haline getirilmiş' formatı korur, ancak gereksiz boşluk karakterleri eklemez (bu, günlük kaydını kirletir).
  2. Seçenek 4'teki ters eğik çizgi devam çirkinliğini önler, bu da ifade eklemeyi zorlaştırır (beyaz boşluk körlüğünden bahsetmiyorum bile).
  3. Ve dahası, VIM'deki ifadeyi genişletmek gerçekten çok basittir (imleci ekleme noktasına getirin ve yeni bir satır açmak için SHIFT-O tuşlarına basın ).

2
Bu baskı içinse, bence daha iyi bir alternatif, onu mutiline string olarak yazmak """ve çıktı textwrap.dedent()almadan önce kullanmaktır
slezica

Bu seçenekle oynadım, ancak aynı zamanda log çıktısını çok satırlı yaptı. Bir db konuşkan uygulamasını izlerken, bu çok büyük çıktılara neden oldu.
user590028

1
Bu eski bir ileti dizisi, ancak bu biçimi en iyi uygulama olarak kullanıyorum, ancak daha uzun sorgularla sıkıcı oluyor
Jabda

8
"sql query"SQL dizeleriyle (standart olarak tek tırnak kullanan) karışıklıktan kaçınmak için her zaman çift tırnak kullanmamız gerekmez mi?
tpvasconcelos

19

Açıkça, sorunsuz yazdırılacak şekilde SQL yazmak için birçok yol düşündünüz, ancak SQL'inizi sevmediğiniz şekillerde yazmak yerine hata ayıklama günlüğü için kullandığınız 'print' ifadesini değiştirmeye ne dersiniz? Yukarıdaki favori seçeneğinizi kullanarak, bunun gibi bir günlük kaydı işlevine ne dersiniz:

def debugLogSQL(sql):
     print ' '.join([line.strip() for line in sql.splitlines()]).strip()

sql = """
    select field1, field2, field3, field4
    from table"""
if debug:
    debugLogSQL(sql)

Bu aynı zamanda, satır istediğiniz uzunluktan daha uzunsa, günlüğe kaydedilen dizeyi birden çok satıra bölmek için ek mantık eklemeyi önemsiz hale getirir.


11

Karşılaştığım en temiz yol, sql stil kılavuzundan ilham aldı .

sql = """
    SELECT field1, field2, field3, field4
      FROM table
     WHERE condition1 = 1
       AND condition2 = 2;
"""

Esasen, bir cümleye başlayan anahtar kelimeler sağa hizalanmalı ve alan adları vb. Sola hizalanmalıdır. Bu çok düzgün görünüyor ve aynı zamanda hata ayıklaması daha kolay.


2
sql = ("select field1, field2, field3, field4 "
       "from table "
       "where condition1={} "
       "and condition2={}").format(1, 2)

Output: 'select field1, field2, field3, field4 from table 
         where condition1=1 and condition2=2'

koşulun değerinin bir dizge olması gerekiyorsa, şunu yapabilirsiniz:

sql = ("select field1, field2, field3, field4 "
       "from table "
       "where condition1='{0}' "
       "and condition2='{1}'").format('2016-10-12', '2017-10-12')

Output: "select field1, field2, field3, field4 from table where
         condition1='2016-10-12' and condition2='2017-10-12'"

5
Lütfen bunu asla yapma. Buna SQL enjeksiyonu deniyor ve gerçekten tehlikeli. Hemen hemen her Python veritabanı kitaplığı, parametreleri kullanmak için bir kolaylık sağlar. Kendinizi format()SQL dizeleri kullanırken yakalarsanız , bu büyük bir kod kokusu.
mattmc3

Kullanamayacağımızı sanmıyorum, kullanmadan önce parametreleri doğrulamalısınız ve neyi geçtiğinizi bilmelisiniz.
pangpang

Doğrulama, where condition1=:field1değerleri parametre olarak kullanmaktan ve sonra iletmekten çok daha fazla hataya açıktır . Eğer kullanıyorsanız .format(), SQL'inize bir ';DROP TABLE Usersgirmenin bir yolu olacaktır . Parametrelerin nasıl doğru kullanılacağını öğrenmek için PEP-249'a bakın. python.org/dev/peps/pep-0249/#paramstyle
mattmc3

1

inspect.cleandocYazdırılan SQL ifadenizi güzel bir şekilde biçimlendirmek için kullanabilirsiniz .

Bu, 2. seçeneğinizle çok iyi çalışıyor .

Not: print("-"*40)cleandoc kullanmıyorsanız, sadece gereksiz boş satırları göstermektir.

from inspect import cleandoc
def query():
    sql = """
        select field1, field2, field3, field4
        from table
        where condition1=1
        and condition2=2
    """

    print("-"*40)
    print(sql)
    print("-"*40)
    print(cleandoc(sql))
    print("-"*40)

query()

Çıktı:

----------------------------------------

        select field1, field2, field3, field4
        from table
        where condition1=1
        and condition2=2

----------------------------------------
select field1, field2, field3, field4
from table
where condition1=1
and condition2=2
----------------------------------------

Gönderen docs :

inspect.cleandoc (doc)

Kod bloklarıyla aynı hizaya gelecek şekilde girintilenmiş belge dizilerindeki girintiyi temizleyin.

Baştaki tüm boşluklar ilk satırdan kaldırılır. İkinci satırdan itibaren tekdüze bir şekilde kaldırılabilen herhangi bir öndeki boşluk kaldırılır. Baştaki ve sondaki boş çizgiler daha sonra kaldırılır. Ayrıca, tüm sekmeler boşluklara genişletilir.


0

Biçimlendirmeyi tamamen önlemek için , harika bir çözümün prosedürleri kullanmak olduğunu düşünüyorum .

Bir prosedürü aramak size şunu verir: , bu yordama eklemek istediğiniz sorgunun sonucunu . Aslında bir prosedür içinde birden çok sorguyu işleyebilirsiniz . Çağrı, sadece çağrılan son sorguyu döndürecektir .

MYSQL

DROP PROCEDURE IF EXISTS example;
 DELIMITER //
 CREATE PROCEDURE example()
   BEGIN
   SELECT 2+222+2222+222+222+2222+2222 AS this_is_a_really_long_string_test;
   END //
 DELIMITER;

#calling the procedure gives you the result of whatever query you want to put in this procedure. You can actually process multiple queries within a procedure. The call just returns the last query result
 call example;

Python

sql =('call example;')

-1

alan adlarını bir dizi "alanlar" içine koyabilir ve ardından:


sql = 'select %s from table where condition1=1 and condition2=2' % (
 ', '.join(fields))

koşullar listeniz büyürse, 've' .join (koşullar) kullanarak aynısını yapabilirsiniz
jcomeau_ictx

çözümünüzle, sorguyu düzenlemek Seçenek_4'e göre daha da zor olacak ve ayrıca okumak da zor olacaktır.
ssoler

@ssoler, bu bir şeyin nasıl yapıldığına bağlı. Programlarımda birkaç değişken bildiriyorum ve onun yerine dizgi dizilerini kullanıyorum, bu da yukarıdaki gibi yöntemleri en azından benim için çok kullanışlı ve sürdürülebilir kılıyor .
jcomeau_ictx

-1

2. seçeneğe bağlı SELECT * FROM tablekalmanızı öneririm (her zaman daha karmaşık sorgular için kullanıyorum ) ve güzel bir şekilde yazdırmak isterseniz her zaman ayrı bir modül kullanabilirsiniz .


-1

Bir veya iki satıra sığabilen kısa sorgular için, yukarıdaki en çok oylanan çözümde dizgi değişmez çözümünü kullanıyorum. Daha uzun sorgular için onları .sqldosyalara bölerim. Daha sonra dosyayı yüklemek ve komut dosyasını çalıştırmak için bir sarmalayıcı işlevi kullanıyorum, şöyle bir şey:

script_cache = {}
def execute_script(cursor,script,*args,**kwargs):
    if not script in script_cache:
        with open(script,'r') as s:
            script_cache[script] = s
    return cursor.execute(script_cache[script],*args,**kwargs)

Tabii ki bu genellikle bir sınıfta yaşıyor, bu yüzden genellikle cursoraçık bir şekilde geçmek zorunda kalmam . Ben de genel olarak kullanıyorum codecs.open()ama bu genel fikri karşılıyor. Ardından SQL betikleri, kendi sözdizimi vurgulamaları ile kendi dosyalarında tamamen bağımsızdır.


-2
sql = """\
select field1, field2, field3, field4
from table
where condition1=1
and condition2=2
"""

[açıklamaya yanıt olarak düzenle]
Bir yöntem içinde bir SQL dizesine sahip olmak , onu "tabüle etmeniz" gerektiği anlamına GELMEZ :

>>> class Foo:
...     def fubar(self):
...         sql = """\
... select *
... from frobozz
... where zorkmids > 10
... ;"""
...         print sql
...
>>> Foo().fubar()
select *
from frobozz
where zorkmids > 10
;
>>>

IMO, Option_2 ile aynı
ssoler

@ssoler: Seçenek_2'nizin tüm satırlarında önde gelen boşluklar vardır ; örneğinizin daha önce baştaki boşlukları atladığını unutmayın select. Cevabımın önde gelen boşlukları yok. Onların aynı olduğu fikrini oluşturmanıza ne sebep oldu?
John Machin

Sql dizginizi bir yöntemin içine koyarsanız, tüm satırları (Seçenek_2) tablo haline getirmeniz gerekecektir. Bunun olası bir çözümü Seçenek_3'dur.
ssoler

@ssoler: Üzgünüm, bu yorumu anlamıyorum. Lütfen güncellenmiş cevabıma bakın.
John Machin

Güncellenmiş cevabınız benim Option_3'ım değil mi? Bu seçeneği sevmiyorum çünkü iyi tablo haline getirilmiş kodun açıklığını bozuyor.
ssoler
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.