> ## Documentation Index
> Fetch the complete documentation index at: https://anaconda.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Run SQL query with pandas

export const Comments = ({children}) => {
  return <div class="my-4 px-5 py-4 overflow-hidden rounded-2xl flex gap-3 border border-zinc-500/20 bg-zinc-50/50 dark:border-zinc-500/30 dark:bg-zinc-500/10" data-callout-type="comments">
      <div class="w-4">
        <svg width="14" height="14" viewBox="0 0 640 640" fill="currentColor" xmlns="http://www.w3.org/2000/svg" class="w-5 h-5" aria-label="Comments">
            <path d="M320 112C434.9 112 528 205.1 528 320C528 434.9 434.9 528 320 528C205.1 528 112 434.9 112 320C112 205.1 205.1 112 320 112zM320 576C461.4 576 576 461.4 576 320C576 178.6 461.4 64 320 64C178.6 64 64 178.6 64 320C64 461.4 178.6 576 320 576zM280 400C266.7 400 256 410.7 256 424C256 437.3 266.7 448 280 448L360 448C373.3 448 384 437.3 384 424C384 410.7 373.3 400 360 400L352 400L352 312C352 298.7 341.3 288 328 288L280 288C266.7 288 256 298.7 256 312C256 325.3 266.7 336 280 336L304 336L304 400L280 400zM320 256C337.7 256 352 241.7 352 224C352 206.3 337.7 192 320 192C302.3 192 288 206.3 288 224C288 241.7 302.3 256 320 256z" />
        </svg>
      </div>
      <div class="text-sm prose min-w-0 w-full">
        {children}
      </div>
    </div>;
};

Pandas has utility functions that make it one line to create a table, store it in a database, and later run queries against the data. This page shows how to run a SQL query against a self-hosted database from a Metaflow flow, transform the results in a dataframe, and write them back to the database.

<Steps>
  <Step title="Add a table to the MySQL database">
    To run the full example locally, [install MySQL](https://dev.mysql.com/doc/mysql-installation-excerpt/5.7/en/) and set up a database called `test`. This example uses a Python function defined in the script containing the flow to create the table, but you can set up the table any way you prefer to interact with the database.
  </Step>

  <Step title="Run the flow">
    The flow shows how to:

    * Access data in a [pandas](https://pandas.pydata.org) dataframe by running a SQL query on a local database.
      * This example uses a [MySQL](https://www.mysql.com/) database, but you could also store data in [PostgreSQL](https://www.postgresql.org).
    * Make a transformation to the dataframe.
    * Save the result to a separate table in the database.

    ```py title="sql_query_local.py" expandable theme={null}
    from metaflow import FlowSpec, step, Parameter
    from sqlalchemy import create_engine
    import pandas as pd

    class LocalQueryFlow(FlowSpec):
        
        @step
        def start(self):        
            self.next(self.extract)
            
        @step
        def extract(self):
            QUERY = f"SELECT * FROM {table_name}"
            self.result = pd.read_sql(QUERY, con=conn)
            self.next(self.transform)
            
        @step
        def transform(self):
            f = lambda x: x["feat_1"] + x["feat_2"]
            self.result["feat_12"] = self.result.apply(f, 
                                                    axis=1)
            self.next(self.write)
            
        @step
        def write(self):
            self.result.to_sql(name=f"{table_name}_updated", 
                               con=conn, 
                               if_exists="replace")
            self.next(self.end)
            
        @step
        def end(self):
            conn.close()
            
    ### Local database configuration ###
    db_path = 'mysql://<USERNAME>:<PASSWORD>@localhost/data' 
    table_name = 'data' 
    engine = create_engine(db_path, echo=False)
    conn = engine.connect()

    def create_table(db_path, table, conn):
        # Create the dataset
        dataset = pd.DataFrame({"id": [1, 2], 
                                "feat_1": ["foo", "bar"],
                                "feat_2": ["fizz", "buzz"]})
        try: # Write the contents to the local database
            dataset.to_sql(table, con=conn)
        except ValueError:
            print(f"{table} at {db_path} doesn't exist.")
        
    if __name__ == "__main__":
        create_table(db_path, table_name, conn)
        LocalQueryFlow()
    ```

    <Comments>
      Replace \<USERNAME> with your MySQL username.<br />
      Replace \<PASSWORD> with your MySQL password.
    </Comments>

    ```bash theme={null}
    python sql_query_local.py run
    ```

    ```text theme={null}
         Workflow starting (run-id 610):
         [610/start/3177 (pid 73064)] Task is starting.
         [610/start/3177 (pid 73064)] Task finished successfully.
         [610/extract/3178 (pid 73069)] Task is starting.
         [610/extract/3178 (pid 73069)] Task finished successfully.
         [610/transform/3179 (pid 73073)] Task is starting.
         [610/transform/3179 (pid 73073)] Task finished successfully.
         [610/write/3180 (pid 73082)] Task is starting.
         [610/write/3180 (pid 73082)] Task finished successfully.
         [610/end/3181 (pid 73086)] Task is starting.
         [610/end/3181 (pid 73086)] Task finished successfully.
         Done!
    ```
  </Step>

  <Step title="Access artifacts outside of the flow">
    Run the following in any script or notebook to access the contents of the dataframe that was stored as a flow artifact with `self.result`:

    ```python theme={null}
    from metaflow import Flow
    run = Flow('LocalQueryFlow').latest_run
    run.data.result
    ```

    ```text theme={null}
       index  id feat_1 feat_2 feat_12
    0      0   1    foo   fizz  foofizz
    1      1   2    bar   buzz  barbuzz
    ```
  </Step>
</Steps>
