pymssql executemany 插入值非常慢

Posted

技术标签:

【中文标题】pymssql executemany 插入值非常慢【英文标题】:pymssql executemany insert value very slow 【发布时间】:2019-09-29 13:17:05 【问题描述】:

python-2.7.15、pymssql-2.1.4、SQL_Server-2018、Windows 10 专业版、MS-Office-2016

import time
import csv
import pymssql

db_settings = 
    "host" : "127.0.0.1",
    "port" : "1433",
    "user" : "sa",
    "password" : "********",
    "database" : "testdb",
    "charset" : "utf8"
               
conn = pymssql.connect(**db_settings)
cursor = conn.cursor()
ff = csv.reader(open('base.csv', 'r'))
sql = """
    BEGIN
        INSERT INTO Base([name], [year], [update], [status], 
    [timeline], [language], [pic]) VALUES (%s, %s, %s, %s, %s, %s, %s)
    END
    """
now=time.strftime("%M:%S")
t = []
for i in ff:
    i = i[1:]
    if "year" in i:
        pass
    else:
        t.append((i[0], i[1], i[3], i[4], i[6], i[5], i[8]))
cursor.executemany(sql, t)
conn.commit()

end=time.strftime("%M:%S")

print(now+","+end)

“base.csv”文件大小为 21.7 MB 和 30374 行。当我执行上述代码时,需要 929 秒才能完成。这意味着只有 32.7 行/秒,太慢了。谁能帮我找出原因?非常感谢。 :-)

【问题讨论】:

你能在 for 循环检查年份之后插入另一个打印的时间吗?我怀疑不是插入需要这么长时间,而是字符串比较...... 我尝试删除代码中的for循环,耗时928秒,for循环只想删除base.csv中的第一行 嗯... csv 是机密文件还是您可以在此处共享该数据?我想自己尝试使用代码做一些事情,但是在没有原始数据的情况下这样做可能会产生非常不同的结果。 您可以在进入for i in ff: 循环之前跳过带有next(ff, None) 的标题行。这样你就不必检查每一行。你真的要插入本地数据库实例(127.0.0.1)吗?当我为 100,000 行 x 4 列执行此操作时,我使用 pymssql 获得大约 695 行/秒(使用 pyodbc、“SQL Server 的 ODBC 驱动程序 17”和 fast_executemany=True 获得大约 5,000 行/秒)。 ( cc: @sekky ) - 我刚刚再次尝试使用您的 30,374 行测试数据。我使用 pymssql 获得了 2,336 行/秒,使用 pyodbc+fast_executemany 获得了 7,594 行/秒。很明显,您的环境中发生了一些您没有向我们展示的其他事情。 【参考方案1】:

我像这样将pymssql中execute_many的时间从30分钟减少到30秒。

在 sql 中,您可以一次创建包含多行的插入语句。如下图所示

INSERT (col_name1, col_name2) 
INTO table_name 
VALUES 
(row1_val1, row1_val2), 
(row2_val1, row2_val2) ... 
(row1000_val1, row1000_val2)

我实现了插入函数,它获取数据块并修改查询以通过一次执行插入多个值。

def insert(query, data, chunk=999):
    conn = get_connection()
    cursor = conn.cursor()
    query = query.lower()
    insert_q, values_q = query.split('values') # get part with the query and the parameters
    insert_q += 'values' # add values to make sql query correct after split
    for chunk_data in chunks(data, chunk):
        # chunk_data contains list of row parameters
        flat_list = [item for sublist in chunk_data for item in sublist] # we make it flat to use execute later instead execute_many
        chunk_query = insert_q + ','.join([values_q] * len(chunk_data)) # creating the query with multiple values insert
        cursor.execute(chunk_query, tuple(flat_list)
        conn.commit()

chunks可以这样实现(感谢本论坛的精彩回复)

def chunks(lst, n):
    for i in range(0, len(lst), n):
        yield lst[i:i + n]

示例用法

insert('INSERT (user_id, name, surname) INTO users VALUES (%s, %s, %s)',
       [(1, 'Jack', 'Kcaj'), (2, 'Andrew', 'Golara')]

【讨论】:

以上是关于pymssql executemany 插入值非常慢的主要内容,如果未能解决你的问题,请参考以下文章

如何在列表pymssql中循环插入值?

Python3 | sqlite3:executemany() 不插入任何内容

python中pymssql如何向sql server插入数据 插入的值可以用变量替换

在 PostgreSQL 中使用 executemany() 在另一个表中插入外键

Python SQLite 正则表达式 ExecuteMany

如何使用pymssql python插入但忽略相同的日期标签