## SQL Database

This notebook showcases an agent designed to interact with a `SQL` databases.

It is designed to answer more general questions about a database, as well as recover from errors.

Note that, as this agent is in active development, all answers might not be correct.

Additionally, it is not guaranteed that the agent won’t perform DML statements on your database given certain questions.

Be careful running it on sensitive data!|

This uses the example `Chinook` database.

To set it up follow [these instructions](https://database.guide/2-sample-databases-sqlite/) and place the .db file in a notebooks folder at the root of this repository.

```
%pip install --upgrade --quiet  langchain langchain-community langchain-experimental
```

```
WARNING: There was an error checking the latest version of pip.Note: you may need to restart the kernel to use updated packages.
```

## Initialize our database[​](#initialize-our-database "Direct link to Initialize our database")

```
from langchain_community.utilities.sql_database import SQLDatabasedb = SQLDatabase.from_uri("sqlite:///Chinook.db")
```

## Quickstart[​](#quickstart "Direct link to Quickstart")

Following the [SQL use case docs](https://python.langchain.com/docs/use_cases/sql/agents/), we can use the `create_sql_agent` helper.

```
from langchain_community.agent_toolkits import create_sql_agentfrom langchain_openai import ChatOpenAIllm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)agent_executor = create_sql_agent(llm, db=db, agent_type="openai-tools", verbose=True)
```

```
agent_executor.invoke(    "List the total sales per country. Which country's customers spent the most?")
```

```
> Entering new AgentExecutor chain...Invoking: `sql_db_list_tables` with `{}`Album, Artist, Customer, Employee, Genre, Invoice, InvoiceLine, MediaType, Playlist, PlaylistTrack, TrackInvoking: `sql_db_schema` with `Invoice,Customer`CREATE TABLE "Customer" (    "CustomerId" INTEGER NOT NULL,     "FirstName" NVARCHAR(40) NOT NULL,     "LastName" NVARCHAR(20) NOT NULL,     "Company" NVARCHAR(80),     "Address" NVARCHAR(70),     "City" NVARCHAR(40),     "State" NVARCHAR(40),     "Country" NVARCHAR(40),     "PostalCode" NVARCHAR(10),     "Phone" NVARCHAR(24),     "Fax" NVARCHAR(24),     "Email" NVARCHAR(60) NOT NULL,     "SupportRepId" INTEGER,     PRIMARY KEY ("CustomerId"),     FOREIGN KEY("SupportRepId") REFERENCES "Employee" ("EmployeeId"))/*3 rows from Customer table:CustomerId  FirstName   LastName    Company Address City    State   Country PostalCode  Phone   Fax Email   SupportRepId1   Luís    Gonçalves   Embraer - Empresa Brasileira de Aeronáutica S.A.    Av. Brigadeiro Faria Lima, 2170 São José dos Campos SP  Brazil  12227-000   +55 (12) 3923-5555  +55 (12) 3923-5566  luisg@embraer.com.br    32   Leonie  Köhler  None    Theodor-Heuss-Straße 34 Stuttgart   None    Germany 70174   +49 0711 2842222    None    leonekohler@surfeu.de   53   François    Tremblay    None    1498 rue Bélanger   Montréal    QC  Canada  H2G 1A7 +1 (514) 721-4711   None    ftremblay@gmail.com 3*/CREATE TABLE "Invoice" (    "InvoiceId" INTEGER NOT NULL,     "CustomerId" INTEGER NOT NULL,     "InvoiceDate" DATETIME NOT NULL,     "BillingAddress" NVARCHAR(70),     "BillingCity" NVARCHAR(40),     "BillingState" NVARCHAR(40),     "BillingCountry" NVARCHAR(40),     "BillingPostalCode" NVARCHAR(10),     "Total" NUMERIC(10, 2) NOT NULL,     PRIMARY KEY ("InvoiceId"),     FOREIGN KEY("CustomerId") REFERENCES "Customer" ("CustomerId"))/*3 rows from Invoice table:InvoiceId   CustomerId  InvoiceDate BillingAddress  BillingCity BillingState    BillingCountry  BillingPostalCode   Total1   2   2009-01-01 00:00:00 Theodor-Heuss-Straße 34 Stuttgart   None    Germany 70174   1.982   4   2009-01-02 00:00:00 Ullevålsveien 14    Oslo    None    Norway  0171    3.963   8   2009-01-03 00:00:00 Grétrystraat 63 Brussels    None    Belgium 1000    5.94*/Invoking: `sql_db_query` with `SELECT c.Country, SUM(i.Total) AS TotalSales FROM Invoice i JOIN Customer c ON i.CustomerId = c.CustomerId GROUP BY c.Country ORDER BY TotalSales DESC LIMIT 10;`responded: To list the total sales per country, I can query the "Invoice" and "Customer" tables. I will join these tables on the "CustomerId" column and group the results by the "BillingCountry" column. Then, I will calculate the sum of the "Total" column to get the total sales per country. Finally, I will order the results in descending order of the total sales.Here is the SQL query:```sqlSELECT c.Country, SUM(i.Total) AS TotalSalesFROM Invoice iJOIN Customer c ON i.CustomerId = c.CustomerIdGROUP BY c.CountryORDER BY TotalSales DESCLIMIT 10;```Now, I will execute this query to get the total sales per country.[('USA', 523.0600000000003), ('Canada', 303.9599999999999), ('France', 195.09999999999994), ('Brazil', 190.09999999999997), ('Germany', 156.48), ('United Kingdom', 112.85999999999999), ('Czech Republic', 90.24000000000001), ('Portugal', 77.23999999999998), ('India', 75.25999999999999), ('Chile', 46.62)]The total sales per country are as follows:1. USA: $523.062. Canada: $303.963. France: $195.104. Brazil: $190.105. Germany: $156.486. United Kingdom: $112.867. Czech Republic: $90.248. Portugal: $77.249. India: $75.2610. Chile: $46.62To answer the second question, the country whose customers spent the most is the USA, with a total sales of $523.06.> Finished chain.
```

```
{'input': "List the total sales per country. Which country's customers spent the most?", 'output': 'The total sales per country are as follows:\n\n1. USA: $523.06\n2. Canada: $303.96\n3. France: $195.10\n4. Brazil: $190.10\n5. Germany: $156.48\n6. United Kingdom: $112.86\n7. Czech Republic: $90.24\n8. Portugal: $77.24\n9. India: $75.26\n10. Chile: $46.62\n\nTo answer the second question, the country whose customers spent the most is the USA, with a total sales of $523.06.'}
```

We can look at what runs under the hood with the `create_sql_agent` helper above.

We can also highlight how the toolkit is used with the agent specifically.

```
from langchain_community.agent_toolkits import SQLDatabaseToolkitfrom langchain_openai import ChatOpenAItoolkit = SQLDatabaseToolkit(db=db, llm=ChatOpenAI(temperature=0))context = toolkit.get_context()tools = toolkit.get_tools()
```

### Use SQLDatabaseToolkit within an Agent[​](#use-sqldatabasetoolkit-within-an-agent "Direct link to Use SQLDatabaseToolkit within an Agent")

```
from langchain_community.agent_toolkits.sql.prompt import SQL_FUNCTIONS_SUFFIXfrom langchain_core.messages import AIMessage, SystemMessagefrom langchain_core.prompts.chat import (    ChatPromptTemplate,    HumanMessagePromptTemplate,    MessagesPlaceholder,)messages = [    HumanMessagePromptTemplate.from_template("{input}"),    AIMessage(content=SQL_FUNCTIONS_SUFFIX),    MessagesPlaceholder(variable_name="agent_scratchpad"),]prompt = ChatPromptTemplate.from_messages(messages)prompt = prompt.partial(**context)
```

```
from langchain.agents import create_openai_tools_agentfrom langchain.agents.agent import AgentExecutorllm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)agent = create_openai_tools_agent(llm, tools, prompt)agent_executor = AgentExecutor(    agent=agent,    tools=toolkit.get_tools(),    verbose=True,)
```

## Disclaimer ⚠️[​](#disclaimer "Direct link to Disclaimer ⚠️")

The query chain may generate insert/update/delete queries. When this is not expected, use a custom prompt or create a SQL users without write permissions.

The final user might overload your SQL database by asking a simple question such as “run the biggest query possible”. The generated query might look like:

```
SELECT * FROM "public"."users"    JOIN "public"."user_permissions" ON "public"."users".id = "public"."user_permissions".user_id    JOIN "public"."projects" ON "public"."users".id = "public"."projects".user_id    JOIN "public"."events" ON "public"."projects".id = "public"."events".project_id;
```

For a transactional SQL database, if one of the table above contains millions of rows, the query might cause trouble to other applications using the same database.

Most datawarehouse oriented databases support user-level quota, for limiting resource usage.

## Example: describing a table[​](#example-describing-a-table "Direct link to Example: describing a table")

```
agent_executor.invoke({"input": "Describe the schema of the playlist table"})
```

```
> Entering new AgentExecutor chain...Invoking: `sql_db_list_tables` with ``Album, Artist, Customer, Employee, Genre, Invoice, InvoiceLine, MediaType, Playlist, PlaylistTrack, TrackInvoking: `sql_db_schema` with `Playlist`CREATE TABLE "Playlist" (    "PlaylistId" INTEGER NOT NULL,     "Name" NVARCHAR(120),     PRIMARY KEY ("PlaylistId"))/*3 rows from Playlist table:PlaylistId  Name1   Music2   Movies3   TV Shows*/The schema of the "Playlist" table is as follows:- PlaylistId: INTEGER (Primary Key)- Name: NVARCHAR(120)Here are three sample rows from the "Playlist" table:| PlaylistId | Name      ||------------|-----------|| 1          | Music     || 2          | Movies    || 3          | TV Shows  |> Finished chain.
```

```
{'input': 'Describe the schema of the playlist table', 'output': 'The schema of the "Playlist" table is as follows:\n\n- PlaylistId: INTEGER (Primary Key)\n- Name: NVARCHAR(120)\n\nHere are three sample rows from the "Playlist" table:\n\n| PlaylistId | Name      |\n|------------|-----------|\n| 1          | Music     |\n| 2          | Movies    |\n| 3          | TV Shows  |'}
```

## Example: describing a table, recovering from an error[​](#example-describing-a-table-recovering-from-an-error "Direct link to Example: describing a table, recovering from an error")

In this example, the agent tries to search for a table, `playlists`, that doesn’t exist, but finds the next best result

```
agent_executor.invoke({"input": "Describe the playlists table"})
```

```
> Entering new AgentExecutor chain...Invoking: `sql_db_list_tables` with ``Album, Artist, Customer, Employee, Genre, Invoice, InvoiceLine, MediaType, Playlist, PlaylistTrack, TrackInvoking: `sql_db_schema` with `Playlist`CREATE TABLE "Playlist" (    "PlaylistId" INTEGER NOT NULL,     "Name" NVARCHAR(120),     PRIMARY KEY ("PlaylistId"))/*3 rows from Playlist table:PlaylistId  Name1   Music2   Movies3   TV Shows*/The "Playlists" table has the following schema:- PlaylistId: INTEGER (Primary Key)- Name: NVARCHAR(120)Here are three sample rows from the "Playlists" table:| PlaylistId | Name      ||------------|-----------|| 1          | Music     || 2          | Movies    || 3          | TV Shows  |> Finished chain.
```

```
{'input': 'Describe the playlists table', 'output': 'The "Playlists" table has the following schema:\n\n- PlaylistId: INTEGER (Primary Key)\n- Name: NVARCHAR(120)\n\nHere are three sample rows from the "Playlists" table:\n\n| PlaylistId | Name      |\n|------------|-----------|\n| 1          | Music     |\n| 2          | Movies    |\n| 3          | TV Shows  |'}
```

## Example: running queries[​](#example-running-queries "Direct link to Example: running queries")

```
agent_executor.invoke(    {        "input": "List the total sales per country. Which country's customers spent the most?"    })
```

```
> Entering new AgentExecutor chain...Invoking: `sql_db_list_tables` with ``Album, Artist, Customer, Employee, Genre, Invoice, InvoiceLine, MediaType, Playlist, PlaylistTrack, TrackInvoking: `sql_db_schema` with `Invoice, Customer`CREATE TABLE "Customer" (    "CustomerId" INTEGER NOT NULL,     "FirstName" NVARCHAR(40) NOT NULL,     "LastName" NVARCHAR(20) NOT NULL,     "Company" NVARCHAR(80),     "Address" NVARCHAR(70),     "City" NVARCHAR(40),     "State" NVARCHAR(40),     "Country" NVARCHAR(40),     "PostalCode" NVARCHAR(10),     "Phone" NVARCHAR(24),     "Fax" NVARCHAR(24),     "Email" NVARCHAR(60) NOT NULL,     "SupportRepId" INTEGER,     PRIMARY KEY ("CustomerId"),     FOREIGN KEY("SupportRepId") REFERENCES "Employee" ("EmployeeId"))/*3 rows from Customer table:CustomerId  FirstName   LastName    Company Address City    State   Country PostalCode  Phone   Fax Email   SupportRepId1   Luís    Gonçalves   Embraer - Empresa Brasileira de Aeronáutica S.A.    Av. Brigadeiro Faria Lima, 2170 São José dos Campos SP  Brazil  12227-000   +55 (12) 3923-5555  +55 (12) 3923-5566  luisg@embraer.com.br    32   Leonie  Köhler  None    Theodor-Heuss-Straße 34 Stuttgart   None    Germany 70174   +49 0711 2842222    None    leonekohler@surfeu.de   53   François    Tremblay    None    1498 rue Bélanger   Montréal    QC  Canada  H2G 1A7 +1 (514) 721-4711   None    ftremblay@gmail.com 3*/CREATE TABLE "Invoice" (    "InvoiceId" INTEGER NOT NULL,     "CustomerId" INTEGER NOT NULL,     "InvoiceDate" DATETIME NOT NULL,     "BillingAddress" NVARCHAR(70),     "BillingCity" NVARCHAR(40),     "BillingState" NVARCHAR(40),     "BillingCountry" NVARCHAR(40),     "BillingPostalCode" NVARCHAR(10),     "Total" NUMERIC(10, 2) NOT NULL,     PRIMARY KEY ("InvoiceId"),     FOREIGN KEY("CustomerId") REFERENCES "Customer" ("CustomerId"))/*3 rows from Invoice table:InvoiceId   CustomerId  InvoiceDate BillingAddress  BillingCity BillingState    BillingCountry  BillingPostalCode   Total1   2   2009-01-01 00:00:00 Theodor-Heuss-Straße 34 Stuttgart   None    Germany 70174   1.982   4   2009-01-02 00:00:00 Ullevålsveien 14    Oslo    None    Norway  0171    3.963   8   2009-01-03 00:00:00 Grétrystraat 63 Brussels    None    Belgium 1000    5.94*/Invoking: `sql_db_query` with `SELECT c.Country, SUM(i.Total) AS TotalSales FROM Customer c JOIN Invoice i ON c.CustomerId = i.CustomerId GROUP BY c.Country ORDER BY TotalSales DESC LIMIT 1;`responded: Based on the schema of the `Customer` and `Invoice` tables, we can join these two tables on the `CustomerId` column to get the total sales per customer. Then, we can group the results by the `Country` column to get the total sales per country. Finally, we can sort the results in descending order of total sales and select the country with the highest total sales.Here is the SQL query to achieve this:```sqlSELECT c.Country, SUM(i.Total) AS TotalSalesFROM Customer cJOIN Invoice i ON c.CustomerId = i.CustomerIdGROUP BY c.CountryORDER BY TotalSales DESCLIMIT 1;```Let me execute this query to find out which country's customers spent the most.[('USA', 523.0600000000003)]The country whose customers spent the most is the USA, with a total sales amount of $523.06.> Finished chain.
```

```
{'input': "List the total sales per country. Which country's customers spent the most?", 'output': 'The country whose customers spent the most is the USA, with a total sales amount of $523.06.'}
```

## Recovering from an error[​](#recovering-from-an-error "Direct link to Recovering from an error")

In this example, the agent is able to recover from an error after initially trying to access an attribute (`Track.ArtistId`) which doesn’t exist.

```
agent_executor.invoke({"input": "Who are the top 3 best selling artists?"})
```

```
> Entering new AgentExecutor chain...Invoking: `sql_db_list_tables` with ``Album, Artist, Customer, Employee, Genre, Invoice, InvoiceLine, MediaType, Playlist, PlaylistTrack, TrackInvoking: `sql_db_schema` with `InvoiceLine`CREATE TABLE "InvoiceLine" (    "InvoiceLineId" INTEGER NOT NULL,     "InvoiceId" INTEGER NOT NULL,     "TrackId" INTEGER NOT NULL,     "UnitPrice" NUMERIC(10, 2) NOT NULL,     "Quantity" INTEGER NOT NULL,     PRIMARY KEY ("InvoiceLineId"),     FOREIGN KEY("TrackId") REFERENCES "Track" ("TrackId"),     FOREIGN KEY("InvoiceId") REFERENCES "Invoice" ("InvoiceId"))/*3 rows from InvoiceLine table:InvoiceLineId   InvoiceId   TrackId UnitPrice   Quantity1   1   2   0.99    12   1   4   0.99    13   2   6   0.99    1*/Invoking: `sql_db_schema` with `Artist`CREATE TABLE "Artist" (    "ArtistId" INTEGER NOT NULL,     "Name" NVARCHAR(120),     PRIMARY KEY ("ArtistId"))/*3 rows from Artist table:ArtistId    Name1   AC/DC2   Accept3   Aerosmith*/Invoking: `sql_db_query` with `SELECT Artist.Name, SUM(InvoiceLine.Quantity) AS TotalQuantity FROM Artist JOIN Album ON Artist.ArtistId = Album.ArtistId JOIN Track ON Album.AlbumId = Track.AlbumId JOIN InvoiceLine ON Track.TrackId = InvoiceLine.TrackId GROUP BY Artist.Name ORDER BY TotalQuantity DESC LIMIT 3`[('Iron Maiden', 140), ('U2', 107), ('Metallica', 91)]The top 3 best selling artists are:1. Iron Maiden - 140 total quantity sold2. U2 - 107 total quantity sold3. Metallica - 91 total quantity sold> Finished chain.
```

```
{'input': 'Who are the top 3 best selling artists?', 'output': 'The top 3 best selling artists are:\n\n1. Iron Maiden - 140 total quantity sold\n2. U2 - 107 total quantity sold\n3. Metallica - 91 total quantity sold'}
```