How to excecute multiple SQL queries to pandas dataframes in parallel

pandas, parallel-processing, python, sql

Solution

Use N of connections in N threads. Then join theads and procces results.

# imports
import ceODBC
import numpy as np
import pandas as pd
import pandas.io.sql as psql
from ConfigParser import ConfigParser  
import os
import glob
import threading
enter code here


# db connection string
cnxn_string = 'DRIVER={SQL Server Native Client 11.0}; SERVER=<servername>; DATABASE=<dname>; Trusted_Connection=Yes'

# directories (also should be moved to config)
dataDir = os.getcwd() + '\\data\\'
sqlDir = os.getcwd() + '\\sql\\'

#variable to store results
responses={}
responses_lock=threading.Lock()

maxconnections = 8
pool_sema = BoundedSemaphore(value=maxconnections)


def task(fname):

    with open(fname, 'r') as f: sql = f.read()

    # Connect to db, run SQL, assign result into dataframe, close connection. 
    # to limit connections on DB used semaphore
    pool_sema.acquire()
    cnxn = ceODBC.connect(cnxn_string)
    cursor = cnxn.cursor()
    # execute the queries and close the connection. Parallelize?
    df = psql.frame_query(sql, cnxn)
    # close connection
    cnxn.close()
    pool_sema.release()

    # to ensure that only one thread can modify global variable
    responses_lock.acquire()
    responses[fname] = df
    responses_lock.release()


pool = []

#find sql files and spawn theads
for fname im glob.glob( os.path.join(sqlDir,'*sql')):
    #create new thread with task
    thread = threading.Thread(target=task,args=(fname,))
    thread.daemon = True
    # store thread in pool 
    pool.append(thread)
    #thread started
    thread.start()

#wait for all threads tasks done
for thread in pool:
    thread.join()

# results of each execution stored in responses dict

Each file executes in separate thread. Result stored in one variable.

Equivalent for function with `with` statement:

def task(fname):

    with open(fname, 'r') as f: sql = f.read()

    # Connect to db, run SQL, assign result into dataframe, close connection. 
    # to limit connections on DB used semaphore
    with pool_sema:
        cnxn = ceODBC.connect(cnxn_string)
        cursor = cnxn.cursor()
        # execute the queries and close the connection. Parallelize?
        df = psql.frame_query(sql, cnxn)
        # close connection
        cnxn.close()


    # to ensure that only one thread can modify global variable
    with responses_lock:
        responses[fname] = df

`multiprocessing.Pool` is easy for distributing heavy tasks, but has more IO operations in it self.

Problem

Hi all Python Pandas gurus. I'm looking for a way to run some SQL in parallel with Python, returning several Pandas dataframes. I have code similar to below that serially runs 4 SQL queries against a MS SQL server database. Two of the queries have much longer execution time vs. IO (network) time to get the results, so I'm thinking parallelizing would make the code run ~2x faster. Is there an easy way to execute the queries in parallel? Ideally, I would like to be able to read all the *.sql files in a sub dir of a project, then fire off the queries to run in parallel and return the four dataframes in a easy to use format (list?) for further operations (indexing, joining, aggregating). Thanks in advance, Randall ``` # imports import ceODBC import numpy as np import pandas as pd import pandas.io.sql as psql from ConfigParser import ConfigParser import os import glob # db connection string cnxn = 'DRIVER={SQL Server Native Client 11.0}; SERVER=<servername>; DATABASE=<dname>; Trusted_Connection=Yes' # directories (also should be moved to config) dataDir = os.getcwd() + '\\data\\' sqlDir = os.getcwd() + '\\sql\\' # read sql from external .sql files. Possible to read all *.sql files in a sql dir into a list (or other structure...)? with open(sqlDir + 'q1.sql', 'r') as f: q1sql = f.read() with open(sqlDir + 'q2.sql', 'r') as f: q2sql = f.read() with open(sqlDir + 'q3.sql', 'r') as f: q3sql = f.read() with open(sqlDir + 'q4.sql', 'r') as f: q4sql = f.read() # Connect to db, run SQL, assign result into dataframe, close connection. cnxn = ceODBC.connect(cnxn) cursor = cnxn.cursor() # execute the queries and close the connection. Parallelize? df1 = psql.frame_query(q1sql, cnxn) df2 = psql.frame_query(q2sql, cnxn) df3 = psql.frame_query(q3sql, cnxn) df4 = psql.frame_query(q4sql, cnxn) # close connection cnxn.close() ```

Original source