使用Python的SQL多插入

使用Python的SQL多插入,python,mysql,mysql-python,Python,Mysql,Mysql Python,更新 根据下面Nathan的建议,在向execute()传递一个行列表后,代码会进一步执行,但仍然会卡在execute函数上。错误消息如下: query = query % db.literal(args) TypeError: not all arguments converted during string formatting 所以它仍然不起作用。有人知道为什么现在出现类型错误吗? 结束更新 我有一个很大的.xls格式的邮件列表。我使用python和xlrd将xls文件中的名称和

更新
根据下面Nathan的建议,在向execute()传递一个行列表后,代码会进一步执行,但仍然会卡在execute函数上。错误消息如下:

    query = query % db.literal(args)
TypeError: not all arguments converted during string formatting
所以它仍然不起作用。有人知道为什么现在出现类型错误吗?
结束更新

我有一个很大的.xls格式的邮件列表。我使用python和xlrd将xls文件中的名称和电子邮件检索到两个列表中。现在我想把每个名字和电子邮件放入一个mysql数据库。我正在使用MySQLdb来完成这个部分。显然,我不想对每个列表项都执行insert语句。
以下是我目前掌握的信息。

from xlrd import open_workbook, cellname
import MySQLdb

dbname = 'h4h'
host = 'localhost'
pwd = 'P@ssw0rd'
user = 'root'

book = open_workbook('h4hlist.xls')
sheet = book.sheet_by_index(0)
mailing_list = {}
name_list = []
email_list = []

for row in range(sheet.nrows):
    """name is in the 0th col. email is the 4th col."""
    name = sheet.cell(row, 0).value  
    email =  sheet.cell(row, 4).value
    if name and email:
        mailing_list[name] = email

for n, e in sorted(mailing_list.iteritems()):
    name_list.append(n)
    email_list.append(e)

db = MySQLdb.connect(host=host, user=user, db=dbname, passwd=pwd)
cursor = db.cursor()
cursor.execute("""INSERT INTO mailing_list (name,email) VALUES (%s,%s)""",
              (name_list, email_list))
游标执行时的问题。这是错误:
\u mysql\u exceptions.OperationalError:(1241,'操作数应包含1列)
我最初尝试将查询放入一个var中,但它只是弹出一条消息,告诉我们如何传递一个元组以执行()

我做错了什么?这可能吗?

这个列表是巨大的,我绝对负担不起将插入放入循环中。我看了一下如何使用loaddatainfle,但我真的不知道如何格式化文件或查询,当我不得不阅读MySQL文档时,我的眼睛会流血。我知道我可能会使用一些在线xls到mysql转换器,但这对我来说也是一个学习练习有没有更好的方法?

您需要给出一个行列表。您不需要将姓名和电子邮件拆分为单独的列表,只需创建一个包含这两个值的列表即可

rows = []

for row in range(sheet.nrows):
    """name is in the 0th col. email is the 4th col."""
    name = sheet.cell(row, 0).value  
    email =  sheet.cell(row, 4).value
    rows.append((name, email))

db = MySQLdb.connect(host=host, user=user, db=dbname, passwd=pwd)
cursor = db.cursor()
cursor.executemany("""INSERT INTO mailing_list (name,email) VALUES (%s,%s)""", rows)

更新:正如@JonClements所提到的,它应该是
executemany()
而不是
executemany()

来修复
类型错误:不是在字符串格式化过程中转换的所有参数
-您需要使用
游标.executemany(…)
方法,因为它接受一个元组的iterable(多行),而
cursor.executemany(…)
要求参数为单行值


执行命令后,您需要使用
db.commit()

确保事务已提交,以使更改在数据库中处于活动状态。如果您对代码的高性能感兴趣,这个答案可能会更好

excutemay
方法相比,下面的
executemany
执行速度要快得多:

INSERT INTO mailing_list (name,email) VALUES ('Jim','jim@yahoo.com'),('Lucy','Lucy@gmail.com')
您可以很容易地修改@Nathan Villaescusa的答案并获得新代码

cursor.execute("""INSERT INTO mailing_list (name,email) VALUES (%s)""".format(",".join(str(i) for i in rows))
以下是我自己的测试结果:

excutemany:10000 runs takes 220 seconds

execute:10000 runs takes 12 seconds.

速度差大约是15倍。

采用@PengjuZhao的思想,只需为要传递的所有值添加一个占位符就可以了。与@PengjuZhao的答案不同的是,这些值作为第二个参数传递给execute()函数,该函数应该是注入攻击安全的,因为它仅在运行时进行求值(与“.format()”相反)

cursor.execute(““”插入邮件列表(名称、电子邮件)值(%s)”,“,”。join(str(i)表示行中的i))

只有当这不能正常工作时,才尝试下面的方法

####

@PengjuZhao的回答表明,executemany()要么有很高的Python开销,要么在不需要的地方使用多个execute()语句,否则executemany()不会比单个execute()语句慢太多

这里的函数将NathanVillaescusa和@PengjuZhao的答案放在一个execute()方法中

该解决方案生成了要添加到sql语句中的动态占位符数。它是一个手动生成的execute()语句,具有多个占位符“%s”,其性能可能优于executemany()语句

例如,在2列处插入100行:

  • execute():200次“%s”(=依赖于行数)
  • executemany():仅2次“%s”(=独立于行数)
此解决方案有可能具有@PengjuZhao的快速回答,而不会冒注入攻击的风险。

from xlrd import open_workbook, cellname
import MySQLdb

dbname = 'h4h'
host = 'localhost'
pwd = 'P@ssw0rd'
user = 'root'

book = open_workbook('h4hlist.xls')
sheet = book.sheet_by_index(0)
mailing_list = {}
name_list = []
email_list = []

for row in range(sheet.nrows):
    """name is in the 0th col. email is the 4th col."""
    name = sheet.cell(row, 0).value  
    email =  sheet.cell(row, 4).value
    if name and email:
        mailing_list[name] = email

for n, e in sorted(mailing_list.iteritems()):
    name_list.append(n)
    email_list.append(e)

db = MySQLdb.connect(host=host, user=user, db=dbname, passwd=pwd)
cursor = db.cursor()
cursor.execute("""INSERT INTO mailing_list (name,email) VALUES (%s,%s)""",
              (name_list, email_list))
  • 准备函数的参数:
  • 您将在一维numpy数组
    arr_name
    arr_email
    中存储您的值,然后逐行转换为串联值列表。或者,您可以使用@NathanVillaescusa的方法

    from itertools import chain
    
    listAllValues = list(chain([
    arr_name.reshape(-1,1), arr_email.reshape(-1,1)
    ]))
    
    column_names = 'name, email'
    
    table_name = 'mailing_list'
    
  • 获取带有占位符的sql查询:
  • numRows=int((len(listAllValues))/numColumns)
    只是避免传递行数。如果在listAllValues的两列中插入6个值,那么显然,这将使6/2=3行

    def getSqlInsertMultipleRowsInSqlTable(table_name, column_names, listAllValues):
        numColumns = len(column_names.split(","))
        numRows = int((len(listAllValues))/numColumns)
        
        placeholdersPerRow = "("+', '.join(['%s'] * numColumns)+")"
        placeholders = ', '.join([placeholdersPerRow] * numRows)
        
        sqlInsertMultipleRowsInSqlTable = "insert into `{table}` ({columns}) values {values};".format(table=table_name, columns=column_names, values=placeholders)
    
        return sqlInsertMultipleRowsInSqlTable
    
    strSqlQuery = getSqlInsertMultipleRowsInSqlTable(table_name, column_names, listAllValues)
    
  • 执行strSqlQuery
  • 最后一步:

    db = MySQLdb.connect(host=host, user=user, db=dbname, passwd=pwd)
    cursor = db.cursor()
    cursor.execute(strSqlQuery, listAllValues)
    
    希望此解决方案不会像@PengjuZhao的回答那样存在注入攻击的风险,因为它只使用占位符而不是值填充sql语句。此时,值仅在
    listAllValues
    中单独传递,其中
    strSqlQuery
    只有占位符而不是值:

    cursor.execute(strSqlQuery, listAllValues)
    

    执行()语句在两个单独的参数中获取带有占位符%s的sql语句和值列表,正如@NathanVillaescusa的回答中所做的那样。我仍然不确定这是否避免了注入攻击。我的理解是,只有将值直接放入sql语句,如果我错了,请评论。

    谢谢你修复了原来的问题,如果答案总是这么快,我可能会开始变得懒惰。现在我遇到了一个类型错误,我确定这不是什么所以不能解决的问题。@JonClements和execute很多脚本都没有错误,但我的数据库仍然是空的。@shakabra然后就是ma确保你的事务已提交-
    db.commit()
    @JonClements你太棒了。如果你写了一个答案,我会把它排除在外。什么是format executemany?这是一个列表列表吗?比如:[[1,2],[3,4],[5,6]?不要使用
    %
    操作符来构建查询!跟随Nath
    from xlrd import open_workbook, cellname
    import MySQLdb
    
    dbname = 'h4h'
    host = 'localhost'
    pwd = 'P@ssw0rd'
    user = 'root'
    
    book = open_workbook('h4hlist.xls')
    sheet = book.sheet_by_index(0)
    mailing_list = {}
    name_list = []
    email_list = []
    
    for row in range(sheet.nrows):
        """name is in the 0th col. email is the 4th col."""
        name = sheet.cell(row, 0).value  
        email =  sheet.cell(row, 4).value
        if name and email:
            mailing_list[name] = email
    
    for n, e in sorted(mailing_list.iteritems()):
        name_list.append(n)
        email_list.append(e)
    
    db = MySQLdb.connect(host=host, user=user, db=dbname, passwd=pwd)
    cursor = db.cursor()
    cursor.execute("""INSERT INTO mailing_list (name,email) VALUES (%s,%s)""",
                  (name_list, email_list))