Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/sql-server/26.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 查询大型SQL Server表时,pymssql/pyodbc性能(cursor.execute)非常慢_Python_Sql Server_Performance_Pyodbc_Pymssql - Fatal编程技术网

Python 查询大型SQL Server表时,pymssql/pyodbc性能(cursor.execute)非常慢

Python 查询大型SQL Server表时,pymssql/pyodbc性能(cursor.execute)非常慢,python,sql-server,performance,pyodbc,pymssql,Python,Sql Server,Performance,Pyodbc,Pymssql,我们在SQL Server上有一个大约5亿条记录的大视图。由于它无法放入内存,我考虑使用fetchmany进行分块处理,如下所示: with pymssql.connect(host, user, pass, db) as conn: query = f"SELECT * FROM view_name;" with conn.cursor() as cursor, futures.ThreadPoolExecutor(3) as executor: curso

我们在SQL Server上有一个大约5亿条记录的大视图。由于它无法放入内存,我考虑使用fetchmany进行分块处理,如下所示:

with pymssql.connect(host, user, pass, db) as conn:

    query = f"SELECT * FROM view_name;"

    with conn.cursor() as cursor, futures.ThreadPoolExecutor(3) as executor:
        cursor.execute(query)
        chunk_size = 5000
        data = cursor.fetchmany(chunk_size)

        while data:
            future_rs = executor.submit(process_chunk, data)
            data = cursor.fetchmany(chunk_size)
然而,看起来像cursor.execute实际上在调用fetchmany之前尝试获取所有行,因为它非常慢

我对文档的理解是cursor.execute应该只准备查询,而不是具体化全部结果

您将如何在可管理的时间内处理如此大的表/视图


PS:我也试过pyodbc,也是同样的问题。如预期的那样,将查询更改为从视图中选择top 100*很快。

好的,经过一段时间的调试,我有了一个解决方案

问题的一部分是潜在的观点,这是非常缓慢的。我错误地判断了这一点,因为像DBeaver这样的数据库客户机很可能会很快返回结果,因为它在后台对查询应用分页?。不管怎样,我试图用cursor.fetchmany做的是,我用数据库功能做的

SQLServer12和更高版本使用OFFSET和FetchNext具有非常好的分页功能。因此,我的解决方案如下所示:

offset = 0
offset_increment = 200000

def get_chunk(cursor, offset):
    query = f"""
            SELECT * FROM table ORDER BY some_col 
            OFFSET {offset} ROWS FETCH NEXT {offset_incriment} ROWS ONLY;
            """
    return cursor.execute(query).fetchall()

with futures.ThreadPoolExecutor(6) as executor:
    chunk = get_chunk(query, offset)

    while chunk:
        executor.submit(process_chunk, chunk)
        offset += offset_increment
        chunk = get_chunk(query, offset)
因此,这里的实施是为了:

使用带有OFFSET和FETCH NEXT的SQL Server分页功能,只获取有限的行数。 使用多个线程并行处理块。您还可以将SQL查询执行部分并行化以使其更快。这需要更多的工作,因为你需要知道什么时候停止。 这是我的解决方案背后的基本思想。上面的代码只是一个例子,实际上,我不得不在我的项目中根据资源使用情况(主要是内存)进行更多的调优。您还可以使用ProcessPoolExecutor执行多处理而不是线程化。想法是一样的,代码需要一些更改,因为多处理只需要可拾取的对象


因此,在块中同时使用分页和处理结果,您可以非常轻松地处理大型表/视图:

您为什么要尝试向应用程序返回5亿行?那是不可用的。如果这是一项工作,那么我预计需要很长时间才能完成5亿行。但是execute方法执行它所声明的,它执行您传递给它的查询。是的,返回100行所需的时间将少于500000000。。。执行意味着执行,就像你认为的那样。你似乎想在这里稍微改变一下单词的定义…看看关于分页的一些想法。这是一个批处理过程,而不是一个应用程序。我需要处理一下这些数据,然后转移到不同的地方。我假设execute是懒惰的,因为有fetchall和fetchmeny方法可用。如果完整的结果已经可用,我认为调用fetchall没有任何意义。它似乎只是将结果转换为列表。我不能为pymssql说话,但pyodbc肯定不会获取.execute上的所有行。使用默认事务隔离READ_COMMITTED的快速Wireshark测试显示-连接到SQL Server:8960字节;从百万行执行SELECT*:82_310字节;fetchmany5_000:314_810字节;Fetchmany 995_000:62_564_304字节这会忽略前200000条记录吗?初始偏移量是否应设置为零?