รายการ python ในแบบสอบถาม sql เป็นพารามิเตอร์


125

ฉันมีรายการหลามพูด l

l = [1,5,8]

ฉันต้องการเขียนแบบสอบถาม sql เพื่อรับข้อมูลสำหรับองค์ประกอบทั้งหมดของรายการพูด

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

ฉันจะทำสิ่งนี้ให้สำเร็จได้อย่างไร?

คำตอบ:


113

คำตอบที่ผ่านมาได้ทำการเทมเพลตค่าเป็นสตริง SQL ธรรมดา เป็นเรื่องที่ดีสำหรับจำนวนเต็ม แต่ถ้าเราต้องการทำสำหรับสตริงเราจะได้รับปัญหาการหลีกเลี่ยง

นี่คือตัวแปรที่ใช้แบบสอบถามที่กำหนดพารามิเตอร์ซึ่งจะใช้ได้กับทั้งสองอย่าง:

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))จะสั้นลงเล็กน้อยในขณะที่ยังอ่านได้ imho
ThiefMaster

7
@Thiefmaster: ใช่จะต้องเป็น([placeholder]*len(l))กรณีทั่วไปเนื่องจากตัวยึดอาจมีหลายอักขระ
bobince

1
@bobince ในกรณีของฉันฉันได้กำหนดตัวแปร a = 'john' และ b = 'snow' และเก็บไว้ในทูเปิลที่มีชื่อ got = ['a, b'] และทำการสืบค้นเดียวกัน การทำเช่นนี้ฉันได้รับ erroy เช่น Type Error: ไม่ใช่อาร์กิวเมนต์ทั้งหมดที่ถูกแปลงระหว่างการจัดรูปแบบสตริง ฉันควรจะแก้มันอย่างไร
TechJhola

2
ทำไมคุณถึงต้องการเข้าร่วม "ด้วยมือ" แทนที่จะฝากงานนี้ไว้ที่ dbapi? กุญแจสำคัญคือการใช้ทูเพิลมากกว่ารายการ ...
Alessandro Dentella

5
ขึ้นอยู่กับ answerm นี่เป็นทางออกที่บรรทัดเดียวสำหรับPython cursor.execute(f'SELECT name FROM students WHERE id IN ({','.join('?' for _ in l)})', l)3.6 @bobince คุณควรตั้งข้อสังเกตในโซลูชันของคุณว่าการใช้?เป็นวิธีที่ปลอดภัยในการหลีกเลี่ยงการฉีด SQL มีคำตอบมากมายที่นี่ซึ่งมีช่องโหว่โดยพื้นฐานแล้วคำตอบใด ๆ ที่เชื่อมต่อสตริงใน python
toto_tico

60

วิธีที่ง่ายที่สุดคือเปลี่ยนรายการเป็นtupleอันดับแรก

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

1
นี่เป็นคำตอบที่ถูกต้อง ฉันไม่แน่ใจว่าเหตุใดจึงถูกเพิกเฉย ขั้นแรกให้คุณตั้งค่ารายการ l จากนั้นใช้ทูเปิลจากนั้นส่งทูเพิลลงในแบบสอบถาม ทำได้ดี.
MEdwin

11
ตามที่ระบุไว้โดย @renstrm สิ่งนี้จะใช้ไม่ได้หาก l มีเพียงองค์ประกอบเดียวคุณจะได้รับ id IN (1,) ซึ่งเป็นข้อผิดพลาดทางไวยากรณ์
ดับเบิ้ลดาวน์

1
@Boris หากคุณไม่สามารถรับประกันได้ว่าข้อมูลที่คุณป้อนจะเป็นรายการเสมอคุณสามารถทำสิ่งที่คล้ายกับซับนี้stackoverflow.com/questions/38821586/…ก่อนที่จะส่งอาร์กิวเมนต์ไปยังคำถามของคุณ
Amir Imani

10
เช่นเดียวกับคำตอบส่วนใหญ่ที่นี่นอกเหนือจากคำตอบที่ยอมรับมีความเสี่ยงต่อการแทรก SQL และไม่ควรใช้
Bacon Bits

2
@BaconBits คำเตือนที่เป็นธรรม แต่สำหรับบางแอปพลิเคชันที่การแทรก SQL ไม่ใช่ปัญหา (ตัวอย่างเช่นการวิเคราะห์ฐานข้อมูลที่ฉันเป็นผู้ใช้เพียงคนเดียว) สิ่งนี้จะทำ
irene

26

อย่าซับซ้อนวิธีแก้ปัญหานี้ง่ายมาก

l = [1,5,8]

l = tuple(l)

params = {'l': l}

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

ใส่คำอธิบายภาพที่นี่

หวังว่านี่จะช่วยได้ !!!


10
นี้จะไม่ทำงานถ้ามีเพียงองค์ประกอบหนึ่งคุณจะจบลงด้วยl id IN (1,)ซึ่งเป็นข้อผิดพลาดทางไวยากรณ์
renstrm

3
หาก l มีเพียง 1 องค์ประกอบให้แน่ใจว่าเป็นทูเพิลและจะใช้งานได้ วิธีนี้ชัดเจนในความคิดของฉันมากกว่าวิธีที่ยอมรับ
Alessandro Dentella

2
แต่จะยังใช้ไม่ได้หากทูเปิลว่างเปล่าดังนั้นต้องมีการตรวจสอบเพิ่มเติมก่อนที่จะดำเนินการสืบค้น
НикитаКонин

2
sqlite3นี้ไม่ได้ทำงานให้ฉันด้วย คุณทดสอบสิ่งนี้กับห้องสมุดใด
Nick Chammas

1
ทางออกที่ดี แต่ @renstrm และНикита-Конинนั้นถูกต้องเกี่ยวกับการต้องตรวจสอบเพิ่มเติมเมื่อทูเปิลมีองค์ประกอบเดียวหรือไม่มีองค์ประกอบ
Anish Sana

23

SQL ที่คุณต้องการคือ

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

หากคุณต้องการสร้างสิ่งนี้จาก python คุณสามารถใช้

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

แผนที่ฟังก์ชั่นจะเปลี่ยนรายชื่อลงในรายการของสตริงที่สามารถติดกาวเข้าด้วยกันโดยใช้เครื่องหมายจุลภาคที่str.joinวิธี

อีกวิธีหนึ่งคือ:

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

หากคุณต้องการนิพจน์ตัวสร้างกับฟังก์ชันแผนที่

UPDATE: S. Lottกล่าวในความคิดเห็นว่าการเชื่อมโยง Python SQLite ไม่รองรับลำดับ ในกรณีนี้คุณอาจต้องการ

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

ที่สร้างขึ้นโดย

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

2
:-( การผูก Python SQLite ไม่รองรับลำดับ
ล็อตต์

โอ้? ฉันไม่ได้ตระหนัก จากนั้นอีกครั้งฉันไม่รู้ว่าเรากำลังใช้โซลูชันการรวม SQLite
Blair Conrad

ขออภัยไม่ได้แนะนำหรือกล่าวเป็นนัยถึง SQLite - น่าเศร้าที่การเชื่อมโยงสำหรับรายการไม่ทำงานที่นั่น
ล็อตต์

11

สตริงเข้าร่วมค่ารายการที่คั่นด้วยเครื่องหมายจุลภาคและใช้ตัวดำเนินการรูปแบบเพื่อสร้างสตริงแบบสอบถาม

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

(ขอบคุณแบลร์คอนราด )


2
เนื่องจากวิธีนี้ไม่ได้ใช้การแทนที่พารามิเตอร์ฐานข้อมูลจึงทำให้คุณได้รับการโจมตีด้วยการฉีด SQL %ถูกนำมาใช้ที่นี่เป็นเพียงธรรมดาสตริงหลามการจัดรูปแบบ
นิคแชมมาส

8

ฉันชอบคำตอบของ Bobince:

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)

แต่ฉันสังเกตเห็นสิ่งนี้:

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

สามารถแทนที่ด้วย:

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

ฉันพบว่าสิ่งนี้ตรงกว่าถ้าฉลาดน้อยกว่าและไม่ค่อยมีคนทั่วไป ต่อไปlนี้จำเป็นต้องมีความยาว (เช่นอ้างถึงวัตถุที่กำหนด__len__วิธีการ) ซึ่งไม่น่าจะเป็นปัญหา แต่ตัวยึดจะต้องเป็นอักขระเดี่ยวด้วย เพื่อรองรับการใช้ตัวยึดแบบหลายอักขระ:

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

2

วิธีแก้ปัญหาสำหรับคำตอบ @umounted เพราะมันแตกด้วยทูเปิลองค์ประกอบเดียวเนื่องจาก (1) ไม่ใช่ SQL ที่ถูกต้อง

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

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

5
นี่ไม่ใช่ทางออกที่ดีกว่าเนื่องจากการฉีด sql
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)

วิธีนี้จะช่วยแก้ปัญหาของคุณได้


2

หากคุณใช้ PostgreSQL กับไลบรารี Psycopg2 คุณสามารถปล่อยให้การปรับตัวแบบทูเพิลทำการแก้ไขค่า Escape และการแก้ไขสตริงให้คุณได้เช่น:

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

กล่าวคือตรวจสอบให้แน่ใจว่าคุณกำลังส่งINพารามิเตอร์เป็นไฟล์tuple. ถ้าเป็นlistคุณสามารถใช้= ANYไวยากรณ์อาร์เรย์ :

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

โปรดทราบว่าทั้งสองอย่างนี้จะกลายเป็นแผนการสืบค้นเดียวกันดังนั้นคุณควรใช้สิ่งที่ง่ายกว่า เช่นถ้ารายการของคุณเป็นทูเพิลให้ใช้แบบเดิมหากเก็บไว้ในรายการให้ใช้รายการหลัง


1

ตัวอย่างเช่นหากคุณต้องการเคียวรี sql:

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

สิ่งที่เกี่ยวกับ:

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


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

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

:varโน้ตดูเหมือนง่าย (Python 3.7)


0

สิ่งนี้ใช้การทดแทนพารามิเตอร์และดูแลกรณีรายการค่าเดียว:

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),))
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.