{ "cells": [ { "cell_type": "markdown", "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "source": [ "# SQL Database\n", "\n", "::: {.callout-note}\n", "The `SQLDatabase` adapter utility is a wrapper around a database connection.\n", "\n", "For talking to SQL databases, it uses the [SQLAlchemy] Core API .\n", ":::\n", "\n", "\n", "This notebook shows how to use the utility to access an SQLite database.\n", "It uses the example [Chinook Database], and demonstrates those features:\n", "\n", "- Query using SQL\n", "- Query using SQLAlchemy selectable\n", "- Fetch modes `cursor`, `all`, and `one`\n", "- Bind query parameters\n", "\n", "[Chinook Database]: https://github.com/lerocha/chinook-database\n", "[SQLAlchemy]: https://www.sqlalchemy.org/\n", "\n", "\n", "You can use the `Tool` or `@tool` decorator to create a tool from this utility.\n", "\n", "\n", "::: {.callout-caution}\n", "If creating a tool from the SQLDatbase utility and combining it with an LLM or exposing it to an end user\n", "remember to follow good security practices.\n", "\n", "See security information: https://python.langchain.com/docs/security\n", ":::" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true, "jupyter": { "outputs_hidden": true } }, "outputs": [], "source": [ "!wget 'https://github.com/lerocha/chinook-database/releases/download/v1.4.2/Chinook_Sqlite.sql'" ] }, { "cell_type": "code", "execution_count": 22, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1|AC/DC\r\n", "2|Accept\r\n", "3|Aerosmith\r\n", "4|Alanis Morissette\r\n", "5|Alice In Chains\r\n", "6|Antônio Carlos Jobim\r\n", "7|Apocalyptica\r\n", "8|Audioslave\r\n", "9|BackBeat\r\n", "10|Billy Cobham\r\n", "11|Black Label Society\r\n", "12|Black Sabbath\r\n" ] } ], "source": [ "!sqlite3 -bail -cmd '.read Chinook_Sqlite.sql' -cmd 'SELECT * FROM Artist LIMIT 12;' -cmd '.quit'" ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "!sqlite3 -bail -cmd '.read Chinook_Sqlite.sql' -cmd '.save Chinook.db' -cmd '.quit'" ] }, { "cell_type": "markdown", "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "source": [ "## Initialize Database" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "from pprint import pprint\n", "\n", "import sqlalchemy as sa\n", "from langchain_community.utilities import SQLDatabase\n", "\n", "db = SQLDatabase.from_uri(\"sqlite:///Chinook.db\")" ] }, { "cell_type": "markdown", "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "source": [ "## Query as cursor\n", "\n", "The fetch mode `cursor` returns results as SQLAlchemy's\n", "`CursorResult` instance." ] }, { "cell_type": "code", "execution_count": 30, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "[{'ArtistId': 1, 'Name': 'AC/DC'},\n", " {'ArtistId': 2, 'Name': 'Accept'},\n", " {'ArtistId': 3, 'Name': 'Aerosmith'},\n", " {'ArtistId': 4, 'Name': 'Alanis Morissette'},\n", " {'ArtistId': 5, 'Name': 'Alice In Chains'},\n", " {'ArtistId': 6, 'Name': 'Antônio Carlos Jobim'},\n", " {'ArtistId': 7, 'Name': 'Apocalyptica'},\n", " {'ArtistId': 8, 'Name': 'Audioslave'},\n", " {'ArtistId': 9, 'Name': 'BackBeat'},\n", " {'ArtistId': 10, 'Name': 'Billy Cobham'},\n", " {'ArtistId': 11, 'Name': 'Black Label Society'},\n", " {'ArtistId': 12, 'Name': 'Black Sabbath'}]\n" ] } ], "source": [ "result = db.run(\"SELECT * FROM Artist LIMIT 12;\", fetch=\"cursor\")\n", "print(type(result))\n", "pprint(list(result.mappings()))" ] }, { "cell_type": "markdown", "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "source": [ "## Query as string payload\n", "\n", "The fetch modes `all` and `one` return results in string format." ] }, { "cell_type": "code", "execution_count": 29, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "[(1, 'AC/DC'), (2, 'Accept'), (3, 'Aerosmith'), (4, 'Alanis Morissette'), (5, 'Alice In Chains'), (6, 'Antônio Carlos Jobim'), (7, 'Apocalyptica'), (8, 'Audioslave'), (9, 'BackBeat'), (10, 'Billy Cobham'), (11, 'Black Label Society'), (12, 'Black Sabbath')]\n" ] } ], "source": [ "result = db.run(\"SELECT * FROM Artist LIMIT 12;\", fetch=\"all\")\n", "print(type(result))\n", "print(result)" ] }, { "cell_type": "code", "execution_count": 31, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "[(1, 'AC/DC')]\n" ] } ], "source": [ "result = db.run(\"SELECT * FROM Artist LIMIT 12;\", fetch=\"one\")\n", "print(type(result))\n", "print(result)" ] }, { "cell_type": "markdown", "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "source": [ "## Query with parameters\n", "\n", "In order to bind query parameters, use the optional `parameters` argument." ] }, { "cell_type": "code", "execution_count": 41, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[{'ArtistId': 35, 'Name': 'Pedro Luís & A Parede'},\n", " {'ArtistId': 115, 'Name': 'Page & Plant'},\n", " {'ArtistId': 116, 'Name': 'Passengers'},\n", " {'ArtistId': 117, 'Name': \"Paul D'Ianno\"},\n", " {'ArtistId': 118, 'Name': 'Pearl Jam'},\n", " {'ArtistId': 119, 'Name': 'Peter Tosh'},\n", " {'ArtistId': 120, 'Name': 'Pink Floyd'},\n", " {'ArtistId': 121, 'Name': 'Planet Hemp'},\n", " {'ArtistId': 186, 'Name': 'Pedro Luís E A Parede'},\n", " {'ArtistId': 256, 'Name': 'Philharmonia Orchestra & Sir Neville Marriner'},\n", " {'ArtistId': 275, 'Name': 'Philip Glass Ensemble'}]\n" ] } ], "source": [ "result = db.run(\n", " \"SELECT * FROM Artist WHERE Name LIKE :search;\",\n", " parameters={\"search\": \"p%\"},\n", " fetch=\"cursor\",\n", ")\n", "pprint(list(result.mappings()))" ] }, { "cell_type": "markdown", "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "source": [ "## Query with SQLAlchemy selectable\n", "\n", "Other than plain-text SQL statements, the adapter also accepts SQLAlchemy selectables." ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[{'ArtistId': 35, 'Name': 'Pedro Luís & A Parede'},\n", " {'ArtistId': 115, 'Name': 'Page & Plant'},\n", " {'ArtistId': 116, 'Name': 'Passengers'},\n", " {'ArtistId': 117, 'Name': \"Paul D'Ianno\"},\n", " {'ArtistId': 118, 'Name': 'Pearl Jam'},\n", " {'ArtistId': 119, 'Name': 'Peter Tosh'},\n", " {'ArtistId': 120, 'Name': 'Pink Floyd'},\n", " {'ArtistId': 121, 'Name': 'Planet Hemp'},\n", " {'ArtistId': 186, 'Name': 'Pedro Luís E A Parede'},\n", " {'ArtistId': 256, 'Name': 'Philharmonia Orchestra & Sir Neville Marriner'},\n", " {'ArtistId': 275, 'Name': 'Philip Glass Ensemble'}]\n" ] } ], "source": [ "# In order to build a selectable on SA's Core API, you need a table definition.\n", "metadata = sa.MetaData()\n", "artist = sa.Table(\n", " \"Artist\",\n", " metadata,\n", " sa.Column(\"ArtistId\", sa.INTEGER, primary_key=True),\n", " sa.Column(\"Name\", sa.TEXT),\n", ")\n", "\n", "# Build a selectable with the same semantics of the recent query.\n", "query = sa.select(artist).where(artist.c.Name.like(\"p%\"))\n", "result = db.run(query, fetch=\"cursor\")\n", "pprint(list(result.mappings()))" ] }, { "cell_type": "markdown", "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "source": [ "## Query with execution options\n", "\n", "It is possible to augment the statement invocation with custom execution options.\n", "For example, when applying a schema name translation, subsequent statements will\n", "fail, because they try to hit a non-existing table." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "query = sa.select(artist).where(artist.c.Name.like(\"p%\"))\n", "db.run(query, fetch=\"cursor\", execution_options={\"schema_translate_map\": {None: \"bar\"}})" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.10.12" } }, "nbformat": 4, "nbformat_minor": 4 }