最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

python - How can I speed up dynamic SQL fetching in a stored procedure (SQL Server) with SQLAlchemy in a Flask app? - Stack Over

programmeradmin1浏览0评论

I'm developing a Flask application that uses SQLAlchemy (with the pyodbc driver) to execute a stored procedure on the SQL Server (MS SQL Server). The stored procedure dynamically constructs and executes a SQL query based on a provided table name and a CSV string of company names. Here’s the stored procedure:

CREATE PROCEDURE spGetDetailsByCompanyNames
    @table_name VARCHAR(255),
    @names NVARCHAR(MAX)
AS
BEGIN
    DECLARE @SQL AS NVARCHAR(MAX);
    SET @SQL = 'SELECT * FROM [Master].[' + @table_name + '] WHERE [Franchisee Name] IN (' + @names + ');';
    EXEC sp_executesql @SQL;
END;

In my Flask application, I call this procedure using SQLAlchemy’s text() with bound parameters. For example, my function is:

def get_details_by_name(table_name, company_names, dates, db):
    """
    Execute a stored procedure to retrieve details by company names from a specified table.

    Args:
        table_name (str): The name of the table to query.
        company_names (list): List of company names to filter the results.
        Dates (list): List of columns to parse as dates.
        Db: Database engine connection object.

    Returns:
        Pd.DataFrame or None: DataFrame containing the query results, or None if an error occurs.
    """
    try:
        query = text("EXEC spGetDetailsByCompanyNames @table_name=:table_name, @names=:names")
        query = query.bindparams(table_name=table_name, names=company_names)
        
        with db.connect() as conn:
            return pd.read_sql(query, conn, parse_dates=dates)
    except (sqlalchemy.exc.DatabaseError, TypeError) as e:
        logging.error(f"Query execution failed: {e}")
        return None

I prepare the company names using a helper function that converts a list into a CSV string (e.g., "'CompanyA','CompanyB','CompanyC'").

I use Flask-Executor to run multiple calls concurrently (one for each view).

futures = {
                "StoreDetails": executor.submit(fetch_store_details),
                "ProjectDetails": executor.submit(fetch_project_details),
                "ContractDetails": executor.submit(fetch_contract_details),
                "Context": executor.submit(fetch_context),
                #"SalesData": executor.submit(fetch_sales_data),
            }

            # Collect results after all tasks complete
            results = {key: future.result() for key, future in futures.items()}

This is how everything is initialized:

db = SQLAlchemy()

executor = Executor()

def create_app():
    '''
    Create and return a Flask application instance with the necessary configurations loaded from environment variables. 
    The application includes a secret key and a SQL Server connection string built using the configuration values for hostname, 
    database, username, and password. SQLAlchemy is initialized with the app, and the routes are imported. 
    '''
    # Initialize Flask app
    app = Flask(__name__)

    # Load configuration from environment variables
    app.config.from_object(Config)
    
    app.secret_key = app.config["SECRET_KEY"]

    params = urllib.parse.quote_plus(
        "Driver={ODBC Driver 17 for SQL Server};"
        f"Server=tcp:{app.config.get('HOSTNAME')},1433;"
        f"Database={app.config.get('DATABASE')};"
        f"Uid={app.config.get('USERNAME')};"
        f"Pwd={app.config.get('PASSWORD')};"
        "Encrypt=yes;"
        "TrustServerCertificate=yes;"
        "Connection Timeout=240;"
    )

    connection_string = f"mssql+pyodbc:///?odbc_connect={params}"
    app.config['SQLALCHEMY_DATABASE_URI'] = connection_string
    app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
    
    db.init_app(app)
    executor.init_app(app)  # Initialize Flask-Executor

What I'm Seeing: Concurrency works (all calls are fired simultaneously), but the overall speed of fetching the data from the database is slower than I'd like, especially given that the dynamic SQL is used for each table. Sometimes, it is as quick as 1-2 seconds. It often takes 6 seconds, and in some really bad cases, it takes 20+ seconds. (I think this could be due to coworkers running large SQL queries, but it logically shouldn't be the case.)

Question: Does anyone have suggestions for how I can speed up the database fetching? My manager has instructed me not to use caches. I have tried doing simple SELECT * statements instead of the stored procedure and filtering using Pandas, but the times are still roughly the same. This app is deployed on Azure web apps. The speed on the web app mirrors the speed I see when I locally deploy and test. If anyone knows of a way to speed up the process of grabbing the views from the stored procedure and/or making the times less volatile, it would be greatly appreciated. Thank You!

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论