Pemformatan string kueri SQL Python

101

Saya mencoba menemukan cara terbaik untuk memformat string kueri sql. Ketika saya men-debug aplikasi saya, saya ingin masuk ke file semua string kueri sql, dan penting bahwa string tersebut diformat dengan benar.

Pilihan 1

def myquery():
    sql = "select field1, field2, field3, field4 from table where condition1=1 and condition2=2"
    con = mymodule.get_connection()
    ...
  • Ini bagus untuk mencetak string sql.
  • Ini bukan solusi yang baik jika stringnya panjang dan tidak sesuai dengan lebar standar 80 karakter.

pilihan 2

def query():
    sql = """
        select field1, field2, field3, field4
        from table
        where condition1=1
        and condition2=2"""
    con = mymodule.get_connection()
    ...
  • Di sini kodenya jelas tetapi ketika Anda mencetak string kueri sql Anda mendapatkan semua ruang putih yang mengganggu ini.

    u '\ npilih bidang1, bidang2, bidang3, bidang4 \ n_ _ ___ dari tabel \ n _ ___ di mana condition1 = 1 \ n _ ___ _and condition2 = 2'

Catatan: Saya telah mengganti spasi putih dengan garis bawah _, karena mereka dipangkas oleh editor

Pilihan 3

def query():
    sql = """select field1, field2, field3, field4
from table
where condition1=1
and condition2=2"""
    con = mymodule.get_connection()
    ...
  • Saya tidak suka opsi ini karena merusak kejelasan kode yang ditabulasikan dengan baik.

Pilihan 4

def query():
    sql = "select field1, field2, field3, field4 " \
          "from table " \
          "where condition1=1 " \
          "and condition2=2 "
    con = mymodule.get_connection()    
    ...
  • Saya tidak suka opsi ini karena semua pengetikan tambahan di setiap baris dan sulit juga untuk mengedit kueri.

Bagi saya solusi terbaik adalah Opsi 2 tetapi saya tidak suka spasi ekstra ketika saya mencetak string sql.

Apakah Anda mengetahui opsi lain?

ssoler
sumber
Inilah yang oleh orang Psycopg disebut pendekatan naif untuk komposisi string kueri, misalnya menggunakan penggabungan string - initd.org/psycopg/docs/… . Alih-alih, gunakan parameter kueri untuk menghindari serangan injeksi SQL dan untuk secara otomatis mengonversi objek Python ke dan dari literal SQL. stackoverflow.com/questions/3134691/…
Matthew Cornell
Pertanyaan ini sebenarnya tidak khusus untuk kueri SQL, tetapi berlaku secara umum untuk memformat string multi-baris dengan Python. Tag SQL harus dihapus.
gabus

Jawaban:

135

Maaf telah memposting ke utas lama seperti itu - tetapi sebagai seseorang yang juga memiliki hasrat untuk pythonic 'terbaik', saya pikir saya akan membagikan solusi kami.

Solusinya adalah membangun pernyataan SQL menggunakan String Literal Concatenation python ( http://docs.python.org/ ), yang dapat dikualifikasikan di suatu tempat antara Opsi 2 dan Opsi 4

Contoh Kode:

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

Berfungsi juga dengan f-string :

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

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

Kelebihan:

  1. Ini mempertahankan format pythonic 'well tabulated', tetapi tidak menambahkan karakter spasi asing (yang mencemari logging).
  2. Ini menghindari keburukan kelanjutan garis miring terbalik dari Opsi 4, yang membuatnya sulit untuk menambahkan pernyataan (belum lagi kebutaan ruang putih).
  3. Dan selanjutnya, sangat mudah untuk memperluas pernyataan di VIM (cukup posisikan kursor ke titik penyisipan, dan tekan SHIFT-O untuk membuka baris baru).
pengguna590028
sumber
2
Jika ini untuk mencetak, saya pikir alternatif yang lebih baik adalah menulisnya sebagai mutiline string dengan """dan digunakan textwrap.dedent()sebelum mengeluarkan
slezica
Saya bermain-main dengan opsi itu, tetapi itu juga membuat keluaran log menjadi multiline. Saat melacak aplikasi db chatty, ini menyebabkan keluaran yang banyak.
pengguna590028
1
Ini adalah utas lama, tetapi saya telah menggunakan format ini sebagai praktik terbaik, namun menjadi membosankan dengan pertanyaan yang lebih lama
Jabda
8
Bukankah kita harus selalu menggunakan tanda kutip ganda "sql query"untuk menghindari mengotak-atik string SQL (yang menggunakan tanda kutip tunggal sebagai standar)?
tpvasconcelos
19

Anda jelas telah mempertimbangkan banyak cara untuk menulis SQL sedemikian rupa sehingga tercetak dengan baik, tetapi bagaimana dengan mengubah pernyataan 'print' yang Anda gunakan untuk debug logging, daripada menulis SQL Anda dengan cara yang tidak Anda sukai? Dengan menggunakan opsi favorit Anda di atas, bagaimana dengan fungsi logging seperti ini:

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)

Ini juga akan membuatnya mudah untuk menambahkan logika tambahan untuk membagi string yang dicatat menjadi beberapa baris jika garis lebih panjang dari panjang yang Anda inginkan.

cdlk.dll
sumber
11

Cara terbersih yang saya temukan terinspirasi oleh panduan gaya sql .

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

Pada dasarnya, kata kunci yang memulai klausa harus rata kanan dan nama bidang dll, harus rata kiri. Ini terlihat sangat rapi dan lebih mudah untuk di-debug juga.

aandis
sumber
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'

jika nilai kondisi harus berupa string, Anda dapat melakukan seperti ini:

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'"
pangpang
sumber
5
Tolong jangan pernah melakukan ini. Ini disebut injeksi SQL dan ini sangat berbahaya. Hampir setiap pustaka database Python menyediakan fasilitas untuk menggunakan parameter. Jika Anda menemukan diri Anda menggunakan format()dengan string SQL, itu adalah bau kode utama.
mattmc3
Saya tidak berpikir kami tidak dapat menggunakannya, Anda harus memvalidasi parameter sebelum menggunakannya, dan Anda harus tahu apa yang Anda lulus.
pangpang
Memvalidasi jauh lebih rentan terhadap kesalahan daripada hanya menggunakan where condition1=:field1dan kemudian meneruskan nilai sebagai parameter. Jika Anda menggunakan .format(), akan ada cara untuk memasukkan a ';DROP TABLE Userske SQL Anda. Lihat PEP-249 tentang cara menggunakan parameter dengan benar. python.org/dev/peps/pep-0249/#paramstyle
mattmc3
1

Anda dapat menggunakan inspect.cleandocuntuk memformat pernyataan SQL yang dicetak dengan baik.

Ini bekerja sangat baik dengan opsi 2 Anda .

Catatan: print("-"*40)ini hanya untuk mendemonstrasikan baris kosong superflous jika Anda tidak menggunakan cleandoc.

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

Keluaran:

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

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

Dari dokumen :

inspect.cleandoc (doc)

Bersihkan indentasi dari docstrings yang diindentasi agar sejajar dengan blok kode.

Semua spasi di depan dihapus dari baris pertama. Setiap spasi di depan yang dapat dihapus secara seragam dari baris kedua dan seterusnya akan dihapus. Garis-garis kosong di awal dan akhir kemudian dihapus. Selain itu, semua tab diperluas menjadi spasi.

Mike Scotty
sumber
0

Untuk menghindari pemformatan sepenuhnya , saya pikir solusi yang bagus adalah dengan menggunakan prosedur .

Memanggil prosedur memberi Anda hasil dari kueri apa pun yang ingin Anda masukkan ke dalam prosedur ini. Anda sebenarnya dapat memproses beberapa kueri dalam sebuah prosedur. Panggilan tersebut hanya akan mengembalikan kueri terakhir yang dipanggil.

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;')
Paroofkey
sumber
-1

Anda dapat memasukkan nama bidang ke dalam "bidang" array, lalu:


sql = 'select %s from table where condition1=1 and condition2=2' % (
 ', '.join(fields))
jcomeau_ictx
sumber
jika daftar ketentuan Anda bertambah, Anda dapat melakukan hal yang sama, menggunakan 'dan' .join (ketentuan)
jcomeau_ictx
dengan solusi Anda, kueri akan lebih sulit untuk diedit daripada dengan Option_4, dan akan sulit juga untuk dibaca.
ssoler
@ssoler, itu tergantung bagaimana seseorang melakukan sesuatu. Saya mendeklarasikan beberapa variabel dalam program saya, dan menggunakan array string sebagai gantinya, yang membuat metode seperti di atas sangat berguna dan dapat dipelihara, setidaknya oleh saya.
jcomeau_ictx
-1

Saya akan menyarankan untuk tetap menggunakan opsi 2 (saya selalu menggunakannya untuk pertanyaan yang lebih kompleks dari SELECT * FROM table) dan jika Anda ingin mencetaknya dengan cara yang baik, Anda selalu dapat menggunakan modul terpisah .

Michal Chruszcz
sumber
-1

Untuk pertanyaan singkat yang dapat dimuat pada satu atau dua baris, saya menggunakan solusi literal string dalam solusi pilihan teratas di atas. Untuk pertanyaan yang lebih lama, saya memecahnya menjadi .sqlfile. Saya kemudian menggunakan fungsi pembungkus untuk memuat file dan menjalankan skrip, seperti:

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)

Tentu saja ini sering terjadi di dalam kelas jadi saya biasanya tidak harus lulus cursorsecara eksplisit. Saya juga biasanya menggunakan codecs.open(), tetapi ini menyampaikan gambaran umum. Kemudian skrip SQL sepenuhnya mandiri dalam file mereka sendiri dengan penyorotan sintaksnya sendiri.

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

[edit in responese to comment]
Memiliki string SQL di dalam metode TIDAK berarti Anda harus "mentabulasi" -nya:

>>> class Foo:
...     def fubar(self):
...         sql = """\
... select *
... from frobozz
... where zorkmids > 10
... ;"""
...         print sql
...
>>> Foo().fubar()
select *
from frobozz
where zorkmids > 10
;
>>>
John Machin
sumber
IMO ini sama dengan Option_2
ssoler
@ssoler: Opsi Anda_2 memiliki spasi di depan pada semua baris; perhatikan bahwa contoh Anda menghilangkan spasi sebelumnya select. Jawaban saya tidak memiliki spasi. Apa yang membuat Anda beranggapan bahwa mereka sama?
John Machin
Jika Anda meletakkan string sql Anda di dalam sebuah metode, Anda harus membuat tabel semua baris (Option_2). Salah satu solusi yang mungkin untuk ini adalah Option_3.
ssoler
@ssoler: Maaf, saya tidak mengerti komentar itu. Silakan lihat jawaban saya yang diperbarui.
John Machin
Jawaban terbaru Anda adalah Option_3 saya, bukan? Saya tidak suka opsi ini karena merusak kejelasan kode yang ditabulasi dengan baik.
ssoler