【问题标题】:Insert multiple rows into DB with Python list of Tuples使用 Python 元组列表将多行插入数据库
【发布时间】:2016-05-05 19:42:09
【问题描述】:

我有一个元组列表:

list_ = [(1,7,3000),(1,8,3500), (1,9,3900)]

我想为给定 ID(在本例中为 ID = 1)更新包含多个行/值的表

所以:

INSERT INTO table (ID, Speed, Power) VALUES (1,7,3000),(1,8,3500),(1,9,3900)

我的格式有问题 - 我把字符串变成了这样:

INSERT INTO ... VALUES ((1,7,3000),(1,8,3500),(1,9,3900))

但这当然行不通,因为元组周围有额外的括号。有什么想法可以构建一种“以 Python 方式执行此操作”的方法吗?

【问题讨论】:

    标签: python sql tuples


    【解决方案1】:

    好吧,你需要构造这条线:

    INSERT INTO ... VALUES (1,7,3000), (1,8,3500), (1,9,3900)
    

    试试那个:

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

    【讨论】:

    • 这种 SQL 注入安全吗?
    • @StackerDekker 不,不是。 Python 数据库驱动程序接受列表作为参数:cursor.execute("INSERT INTO ... VALUES (%s, %s)", [(1, 2), (3, 4), (5, 6)]);你不应该使用字符串格式化/插值来插入查询参数(并且数据库通常会多次警告你),因为它不是 SQL 注入安全的。
    • @decorator-factory 更正一次应该是cursor.executemany()。如下图:cursor.executemany("INSERT INTO ... VALUES (%s, %s)", [(1, 2), (3, 4), (5, 6)]);
    • 对不起,我的错。你是对的:docs.python.org/3/library/…
    【解决方案2】:

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

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

    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)
    

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

    这是一个使用字符串格式的 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)
    

    如果值被正确转义:

     cur.execute("""SELECT name, password FROM users WHERE name = ?""", ('name',))
    

    不会返回任何行,从而阻止攻击。

    【讨论】:

      【解决方案3】:

      您还可以尝试以下方法:

      mydb = mysql.connector.connect(
        host="localhost",
        user="myusername",
        password="mypassword",
        database="mydatabase"
       )
       mycursor = mydb.cursor()
       sql = "INSERT INTO customers (name, address) VALUES (%s, %s)"
       val = [
        ('Peter', 'Lowstreet 4'),
        ('Amy', 'Apple st 652'),
        ('Hannah', 'Mountain 21'),
        ('Michael', 'Valley 345'),
        ('Sandy', 'Ocean blvd 2'),
        ('Betty', 'Green Grass 1'),
        ('Richard', 'Sky st 331'),
        ('Susan', 'One way 98'),
        ('Vicky', 'Yellow Garden 2'),
        ('Ben', 'Park Lane 38'),
        ('William', 'Central st 954'),
        ('Chuck', 'Main Road 989'),
        ('Viola', 'Sideway 1633')
       ]
      
       mycursor.executemany(sql, val)
      
       mydb.commit()
      
       print(mycursor.rowcount, "record was inserted.")
      

      【讨论】:

        猜你喜欢
        • 2019-09-25
        • 2020-03-07
        • 1970-01-01
        • 1970-01-01
        • 2013-08-11
        • 1970-01-01
        • 1970-01-01
        • 2020-02-06
        • 1970-01-01
        相关资源
        最近更新 更多