使用Python元组列表在DB中插入多行

mk8*_*efz 4 python sql tuples

我有一个元组列表:

list_ = [(1,7,3000),(1,8,3500), (1,9,3900)]
Run Code Online (Sandbox Code Playgroud)

我想更新一个给定ID的多行/值的表(在这种情况下ID = 1)

所以:

INSERT INTO table (ID, Speed, Power) VALUES (1,7,3000),(1,8,3500),(1,9,3900)
Run Code Online (Sandbox Code Playgroud)

我在格式方面遇到了麻烦 - 我把字符串搞得像这样:

INSERT INTO ... VALUES ((1,7,3000),(1,8,3500),(1,9,3900))
Run Code Online (Sandbox Code Playgroud)

但是当然这不起作用,因为元组周围有额外的括号.有什么想法构建一种方法来"诡异地"吗?

sna*_*erb 9

在 Python 中处理此问题的惯用方法是使用正在使用的数据库驱动程序提供的游标的executemany方法。

例如,对于sqlite使用标准库中的sqlite3模块

conn = sqlite3.connect('/path/to/file.db')
cursor = conn.cursor()
sql = """INSERT INTO mytable (ID, Speed, Power) VALUES (?, ?, ?)"""
values = [(1,7,3000),(1,8,3500),(1,9,3900)]
cursor.executemany(stmt, values)
Run Code Online (Sandbox Code Playgroud)

VALUES子句中使用的占位符因特定驱动程序而异。正确的值可以在驱动程序的文档中找到,或者通过查找驱动程序模块的paramstyle属性来找到。

使用此方法而不是字符串插值/格式化或 f 字符串可确保正确引用值,从而防止 SQL 注入和其他错误:

>>> conn = sqlite3.connect(':memory:')
>>> cur = conn.cursor()
>>> date = '2020-11-23'

>>> # Correctly quoted input is returned as the selected value
>>> cur.execute("""SELECT ? AS today""", (date,)) # <- execute requires a tuple as values
<sqlite3.Cursor object at 0x7f1fa205e1f0>
>>> cur.fetchone()
('2020-11-23',)

>>> # Unquoted input is evaluated as an expression!
>>> cur.execute(f"""SELECT {date} AS today""")
<sqlite3.Cursor object at 0x7f1fa205e1f0>
>>> cur.fetchone()
(1986,)
Run Code Online (Sandbox Code Playgroud)

以下是使用字符串格式的 SQL 注入示例。由于值“name”没有转义,因此当程序员只想返回一个时,查询会返回表中的所有用户名和密码。

NAMES = [('Alice', 'apple'),  ('Bob', 'banana'),  ('Carol', 'cherry')]

conn = sqlite3.connect(':memory:')
cur = conn.cursor()
cur.execute("""CREATE TABLE users (name text, password text)""")
cur.executemany("""INSERT INTO users (name, password) VALUES (?, ?)""", NAMES)
conn.commit()
cur.execute("""SELECT name, password FROM users WHERE name = {}""".format('name'))
for row in cur.fetchall():
    print(row)
Run Code Online (Sandbox Code Playgroud)

如果值被正确转义:

 cur.execute("""SELECT name, password FROM users WHERE name = ?""", ('name',))
Run Code Online (Sandbox Code Playgroud)

不会返回任何行,从而挫败了攻击。


Via*_*kyi 7

好吧,你需要构建一行:

INSERT INTO ... VALUES (1,7,3000), (1,8,3500), (1,9,3900)
Run Code Online (Sandbox Code Playgroud)

试试那个:

rows = [(1,7,3000), (1,8,3500), (1,9,3900)]
values = ', '.join(map(str, rows))
sql = "INSERT INTO ... VALUES {}".format(values)
Run Code Online (Sandbox Code Playgroud)

  • @StackerDekker 不,不是。Python 数据库驱动程序接受列表作为参数: `cursor.execute("INSERT INTO ... VALUES (%s, %s)", [(1, 2), (3, 4), (5, 6)])` ; 你不应该使用字符串格式化/插值来插入查询参数(数据库通常会多次警告你),因为它不是 SQL 注入安全的。 (3认同)
  • @decorator-factory 一项修正应该是“cursor.executemany()”。如下所示: `cursor.executemany("INSERT INTO ... VALUES (%s, %s)", [(1, 2), (3, 4), (5, 6)]);` (3认同)
  • SQL注入安全吗? (2认同)