bir python MySQLDB IN cümlesinde kullanılmak üzere bir listeyi parçalamak


86

Bir listeyi bir dizeye nasıl eşleyeceğimi biliyorum:

foostring = ",".join( map(str, list_of_ids) )

Ve bu dizeyi bir IN yan tümcesine almak için aşağıdakileri kullanabileceğimi biliyorum:

cursor.execute("DELETE FROM foo.bar WHERE baz IN ('%s')" % (foostring))

İhtiyacım olan şey, MySQLDB kullanarak aynı şeyi SAFELY (SQL enjeksiyonundan kaçınarak) yapmaktır. Yukarıdaki örnekte, foostring yürütülecek bir argüman olarak iletilmediğinden, savunmasızdır. Ayrıca mysql kütüphanesinin dışına alıntı yapmalı ve kaçmalıyım.

( İlgili bir SO sorusu var , ancak burada listelenen yanıtlar MySQLDB için çalışmıyor veya SQL enjeksiyonuna karşı savunmasız.)




@mluebke Sorguda birden çok liste geçirme hakkında bir fikriniz var mı?
Dipen Dedania

Yanıtlar:


161

list_of_idsDoğrudan kullanın :

format_strings = ','.join(['%s'] * len(list_of_ids))
cursor.execute("DELETE FROM foo.bar WHERE baz IN (%s)" % format_strings,
                tuple(list_of_ids))

Bu şekilde, kendinizden alıntı yapmak zorunda kalmazsınız ve her türlü sql enjeksiyonundan kaçınırsınız.

Data ( list_of_ids) 'nın bir parametre olarak (sorgu metninde değil) doğrudan mysql sürücüsüne gittiğine ve dolayısıyla enjeksiyon yapılmayacağına dikkat edin. Dizide istediğiniz herhangi bir karakteri bırakabilirsiniz, karakterleri çıkarmaya veya alıntı yapmaya gerek yoktur.


2
@heikogerlach:% s'den alıntı yapmıyorum ... İlk satır "% s,% s,% s" dizesi oluşturuyor ... aynı uzunlukta list_of_ids.
nosklo

Argh, haklısın. Daha dikkatli bakmalıyım. Bir şekilde karıştırdım. Yine de güzel çözüm.

Bu da sqlite olarak çalışacak mı? Sadece denedim ve sözdizimi hatalarını gösteriyor gibi görünüyor.
Sohaib

@Sohaib sqlite içinde , ilk satırı olarak değiştirirseniz işe yarayacağı için yedek karakter ?değildir . %sformat_strings = ','.join('?' * len(list_of_ids))
nosklo

1
@kdas sizin durumunuzda, % format_stringsparçanın %ssorgunuzdaki diğer yer tutucuları değiştirmesini istemezsiniz , sadece IN (%s)yer tutucuyu - Bunu başarmanın yolu %, değiştirmek istediğiniz haricindeki tüm karakterleri ikiye query = ("select distinct cln from vcf_commits where branch like %%s and repository like %%s and filename in (%s) and author not like %%s" % format_strings,); cursor.execute(query, (branch, repository) + tuple(fname_list) + (invalid_author,))
katlamaktır

6

Bu soru oldukça eski olsa da, istediğimi başka birinin araması durumunda yanıt bırakmanın daha iyi olacağını düşündüm.

Kabul edilen yanıt, çok fazla parametremiz olduğunda veya adlandırılmış parametreleri kullanmak istediğimizde karmaşıklaşıyor

Bazı denemelerden sonra

ids = [5, 3, ...]  # list of ids
cursor.execute('''
SELECT 
...
WHERE
  id IN %(ids)s
  AND created_at > %(start_dt)s
''', {
  'ids': tuple(ids), 'start_dt': '2019-10-31 00:00:00'
})

İle test edildi python2.7,pymysql==0.7.11


4
Bu, python 3 ve mysql-connector-python 8.0.21 ile çalışmaz. "Python tuple MySQL türüne dönüştürülemez" hatası döndürülür.
2020

-2

Eğer kullanırsanız Django 2.0 or 2.1ve Python 3.6bu doğru yoludur:

from django.db import connection
RESULT_COLS = ['col1', 'col2', 'col3']
RESULT_COLS_STR = ', '.join(['a.'+'`'+i+'`' for i in RESULT_COLS])
QUERY_INDEX = RESULT_COLS[0]

TABLE_NAME = 'test'
search_value = ['ab', 'cd', 'ef']  # <-- a list
query = (
    f'SELECT DISTINCT {RESULT_COLS_STR} FROM {TABLE_NAME} a '
    f'WHERE a.`{RESULT_COLS[0]}` IN %s '
    f'ORDER BY a.`{RESULT_COLS[0]}`;'
)  # <- 'SELECT DISTINCT a.`col1`, a.`col2`, a.`col3` FROM test a WHERE a.`col1` IN %s ORDER BY a.`col1`;'
with connection.cursor() as cursor:
    cursor.execute(query, params=[search_value])  # params is a list with a list as its element

ref: https://stackoverflow.com/a/23891759/2803344 https://docs.djangoproject.com/en/2.1/topics/db/sql/#passing-parameters-into-raw


Yanlış bir şey varsa, lütfen beni düzeltin!
Belter

-2

Bu soru oldukça eski olsa da. Birine yardımcı olabilecekse çözümümü paylaşıyorum.

list_to_check = ['A', 'B'] cursor.execute("DELETE FROM foo.bar WHERE baz IN ({})".format(str(list_to_check)[1:-1])

İle test edildi Python=3.6


1
Sağlanan list_to_checkSQL çıkışlı olmadığı için bu çözümün SQL enjeksiyon saldırılarına karşı savunmasız olduğundan korkuyorum . Bu nedenle, değerleri parametre olarak geçirmek executedaha uygundur. Bu çözümü çok dikkatli kullanın (diğer bir deyişle, giriş kimlikleri uygulamanızın dışından parametre olarak alınmaz), çünkü birisi bunu sisteminize saldırmak ve veritabanınıza erişmek için kullanabilir.
2020

-3

Liste anlamayı kullanan başka bir basit çözüm:

# creating a new list of strings and convert to tuple
sql_list = tuple([ key.encode("UTF-8") for key in list_of_ids ])

# replace "{}" with "('id1','id2',...'idlast')"
cursor.execute("DELETE FROM foo.bar WHERE baz IN {}".format(sql_list))

-4
list_of_ids = [ 1, 2, 3]
query = "select * from table where x in %s" % str(tuple(list_of_ids))
print query

Bu, sorgu dizesini tamamlamak için bağımsız değişkenleri iletmeniz gereken yöntemle ilgilenmek istemiyorsanız ve yalnızca çağırmak istiyorsanız, bazı kullanım durumları için işe yarayabilir cursror.execute(query).

Başka bir yol da şunlar olabilir:

"select * from table where x in (%s)" % ', '.join(str(id) for id in list_of_ids)

-7

Çok basit: Sadece aşağıdaki dizilimi kullanın

Rules_id = ["9", "10"]

sql1 = "(" + "," .join (harita (str, kurallar_id)) + ") 'da NEREDE id (" + "," .join (harita (str, kurallar_id)) + ")

"," .join (harita (str, kurallar_kimliği))


Nerede sql alıntı yapıyor ve bu bağ değişkenleri yerine değişmez bir değer kullanmıyor mu?
2017, 08:54

Gerek yok, sadece iyi çalışıyor. Tuple oluşumunu doğrudan ilk kaşlı ayraçlarla ("9", "10") dize olarak dönüştürüldüğünden test edebilirsiniz. Hangi sql oluşumunu ayarlar. Yani,
sql'yi adjastable

1
ve eğer bir rules_idiçeriyorsa "); DROP TABLES Bobby --?
2017,

Zaten "bir liste
çökertiliyor

veya şunu kullanın: sql1 = "SELECT * FROM attance_rules_staff NEREDE id (" + "," .join (harita (str, kurallar_id)) + ")"
Mizanur Rahman
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.