parametre olarak sql sorgusunda python listesi


125

Bir piton listem var, diyelim ki

l = [1,5,8]

Listenin tüm öğelerinin verilerini almak için bir sql sorgusu yazmak istiyorum, diyelim ki

select name from students where id = |IN THE LIST l|

Bunu nasıl başarırım?

Yanıtlar:


113

Şimdiye kadarki cevaplar, değerleri düz bir SQL dizgisine dönüştürüyordu. Bu tamsayılar için kesinlikle sorun değil, ancak bunu dizeler için yapmak istersek, kaçış sorununu alırız.

Her ikisi için de işe yarayacak parametreli sorgu kullanan bir varyant:

placeholder= '?' # For SQLite. See DBAPI paramstyle.
placeholders= ', '.join(placeholder for unused in l)
query= 'SELECT name FROM students WHERE id IN (%s)' % placeholders
cursor.execute(query, l)

11
','.join(placeholder * len(l))hala okunabilir iken biraz daha kısa olurdu
ThiefMaster

7
@Thiefmaster: Evet, ([placeholder]*len(l))yer tutucu birden çok karakter olabileceğinden genel durumda olması gerekir.
bobince

1
@bobince benim durumumda a = 'john' ve b = 'snow' değişkenini atadım ve bunu got = ['a, b'] adıyla bir tuple içinde sakladım ve aynı sorguyu yaptım. Bunu yaparak erroy yani Tür Hatası aldım: dizge biçimlendirme sırasında tüm argümanlar dönüştürülmedi. Bunu nasıl çözebilirim
TechJhola

2
Neden bu çalışmayı dbapi'ye bırakmaktansa "el ile" katılmak istiyorsunuz? anahtar, bir liste yerine bir demet kullanmaktır ...
Alessandro Dentella

5
Bu cevaba dayanarak, Python 3.6 için tek satırlık bir çözüm var cursor.execute(f'SELECT name FROM students WHERE id IN ({','.join('?' for _ in l)})', l). @bobince, çözümünüzde kullanmanın ?SQL enjeksiyonundan kaçınmak açısından güvenli bir yol olduğunu da belirtmelisiniz . Burada savunmasız pek çok cevap var, temelde python'da dizeleri birleştiren cevaplar.
toto_tico

60

En kolay yol listeyi tupleilk sıraya çevirmektir

t = tuple(l)
query = "select name from studens where id IN {}".format(t)

1
Bu aslında doğru cevap. Neden göz ardı edildiğinden emin değilim. Önce l listesini ayarlayın, ardından tuple'ı kullanın, ardından tuple'ı sorguya aktarın. aferin.
MEdwin

11
@Renstrm tarafından belirtildiği gibi, eğer sadece bir öğe içeriyorsam bu işe yaramaz, sonunda id IN (1,) elde edersiniz. Bu bir sözdizimi hatasıdır.
2019

1
@Boris, girişinizin her zaman bir liste olduğunu garanti edemezseniz, sorgunuza argümanı iletmeden önce bu tek satırlık stackoverflow.com/questions/38821586/… benzeri bir şey yapabilirsiniz
Amir Imani

10
Bu, burada kabul edilenin dışındaki çoğu yanıt gibi, SQL enjeksiyonuna karşı savunmasızdır ve kullanılmamalıdır.
Bacon Bits

2
@BaconBits Adil uyarısı, ancak SQL enjeksiyonunun sorun olmadığı bazı uygulamalar için (örneğin, tek kullanıcı olduğum bir veritabanının analizi) bu işe yarayacaktır.
Irene

26

Karmaşıklaştırmayın, Bunun çözümü basit.

l = [1,5,8]

l = tuple(l)

params = {'l': l}

cursor.execute('SELECT * FROM table where id in %(l)s',params)

görüntü açıklamasını buraya girin

Umarım bu yardımcı olmuştur !!!


10
Bu, lyalnızca bir öğe içeriyorsa işe yaramaz , sonunda elde edersiniz id IN (1,). Bu bir sözdizimi hatasıdır.
renstrm

3
Eğer sadece 1 eleman içeriyorsa, bunun bir demet olduğundan ve ÇALIŞACAĞINDAN emin olun. Bence bu çözüm, kabul edilenden daha net.
Alessandro Dentella

2
Ancak, tuple boşsa yine de çalışmayacaktır, bu nedenle sorgu yürütmeden önce ek kontrol yapılmalıdır.
Никита Конин

2
Bu benim için çalışmıyor sqlite3. Bunu hangi kütüphaneye karşı test ettiniz?
Nick Chammas

1
İyi bir çözüm ancak @renstrm ve Никита-Конин, tuple tek bir öğeye sahip olduğunda veya hiç öğe içermediğinde ek kontroller gerektirme konusunda haklıdır.
Anish Sana

23

İstediğiniz SQL

select name from studens where id in (1, 5, 8)

Bunu python'dan inşa etmek istiyorsanız,

l = [1, 5, 8]
sql_query = 'select name from studens where id in (' + ','.join(map(str, l)) + ')'

Harita işlevi kullanarak virgülle yapıştırılmış olabilir dizeleri bir liste halinde listeyi değiştirecek str.join yöntemi.

Alternatif:

l = [1, 5, 8]
sql_query = 'select name from studens where id in (' + ','.join((str(n) for n in l)) + ')'

harita işlevine jeneratör ifadelerini tercih ederseniz .

GÜNCELLEME: S. Lott yorumlarda Python SQLite bağlamalarının dizileri desteklemediğinden bahsediyor. Bu durumda isteyebilirsin

select name from studens where id = 1 or id = 5 or id = 8

Oluşturan

sql_query = 'select name from studens where ' + ' or '.join(('id = ' + str(n) for n in l))

2
:-( Python SQLite bağlama dizileri desteklemiyor.
S.Lott

Ah? Fark etmedim Sonra yine, bir SQLite bağlama çözümüne gittiğimizi fark etmedim.
Blair Conrad

Maalesef, SQLite önermiyorum veya ima etmiyorum - sadece listeler için bağlamaların orada çalışmaması üzücü.
S.Lott

11

String.Join virgülle ayrılmış liste değerlerini ve kullanım biçimi operatörü sorgu dizisi oluşturulur.

myquery = "select name from studens where id in (%s)" % ",".join(map(str,mylist))

(Teşekkürler blair-conrad )


2
Bu yaklaşım veritabanı parametresi ikamesini kullanmadığından, sizi SQL enjeksiyon saldırılarına maruz bırakır. %Burada kullanılan varlık biçimlendirme sadece düz Python dizedir.
Nick Chammas

8

Bobince'nin cevabını beğendim:

placeholder= '?' # For SQLite. See DBAPI paramstyle.
placeholders= ', '.join(placeholder for unused in l)
query= 'SELECT name FROM students WHERE id IN (%s)' % placeholders
cursor.execute(query, l)

Ama şunu fark ettim:

placeholders= ', '.join(placeholder for unused in l)

Şununla değiştirilebilir:

placeholders= ', '.join(placeholder*len(l))

Daha az zeki ve daha az genel olsa da bunu daha dolaysız buluyorum. Burada lbir uzunluğa sahip olmak gerekir (yani bir __len__yöntemi tanımlayan bir nesneye atıfta bulunun ), ki bu bir problem olmamalıdır. Ancak yer tutucunun da tek bir karakter olması gerekir. Çok karakterli bir yer tutucuyu desteklemek için:

placeholders= ', '.join([placeholder]*len(l))

2

@Umounted cevabı için çözüm, çünkü bu tek öğeli bir demetle bozuldu, çünkü (1,) geçerli SQL değil:

>>> random_ids = [1234,123,54,56,57,58,78,91]
>>> cursor.execute("create table test (id)")
>>> for item in random_ids:
    cursor.execute("insert into test values (%d)" % item)
>>> sublist = [56,57,58]
>>> cursor.execute("select id from test where id in %s" % str(tuple(sublist)).replace(',)',')'))
>>> a = cursor.fetchall()
>>> a
[(56,), (57,), (58,)]

Sql dizesi için diğer çözüm:

cursor.execute("select id from test where id in (%s)" % ('"'+'", "'.join(l)+'"'))

5
Bu, sql enjeksiyonu nedeniyle daha iyi bir çözüm değildir.
Anurag

2
placeholders= ', '.join("'{"+str(i)+"}'" for i in range(len(l)))
query="select name from students where id (%s)"%placeholders
query=query.format(*l)
cursor.execute(query)

Bu probleminizi çözmeli.


2

PostgreSQL'i Psycopg2 kitaplığı ile kullanıyorsanız, tuple uyarlamasının sizin için tüm kaçış ve dize enterpolasyonunu yapmasına izin verebilirsiniz , örneğin:

ids = [1,2,3]
cur.execute(
  "SELECT * FROM foo WHERE id IN %s",
  [tuple(ids)])

yani, INparametreyi bir tuple. eğer bir listise = ANYdizi sözdizimini kullanabilirsiniz :

cur.execute(
  "SELECT * FROM foo WHERE id = ANY (%s)",
  [list(ids)])

bunların her ikisinin de aynı sorgu planına dönüşeceğini unutmayın, bu nedenle hangisi daha kolaysa onu kullanmanız gerekir. Örneğin, listeniz bir demet halinde gelirse, ilkini, bir listede saklanıyorlarsa ikincisini kullanın.


1

Örneğin, sql sorgusunu istiyorsanız:

select name from studens where id in (1, 5, 8)

Ne dersin:

my_list = [1, 5, 8]
cur.execute("select name from studens where id in %s" % repr(my_list).replace('[','(').replace(']',')') )

1

daha basit bir çözüm:

lst = [1,2,3,a,b,c]

query = f"""SELECT * FROM table WHERE IN {str(lst)[1:-1}"""

1
l = [1] # or [1,2,3]

query = "SELECT * FROM table WHERE id IN :l"
params = {'l' : tuple(l)}
cursor.execute(query, params)

:varNotasyonu daha basit görünüyor. (Python 3.7)


0

Bu, parametre değiştirmeyi kullanır ve tek değer listesi durumuyla ilgilenir:

l = [1,5,8]

get_operator = lambda x: '=' if len(x) == 1 else 'IN'
get_value = lambda x: int(x[0]) if len(x) == 1 else x

query = 'SELECT * FROM table where id ' + get_operator(l) + ' %s'

cursor.execute(query, (get_value(l),))
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.