{ "cells": [ { "cell_type": "markdown", "id": "27ae18ec", "metadata": {}, "source": [ "# The sqlite-utils tutorial\n", "\n", "[sqlite-utils](https://sqlite-utils.datasette.io/en/stable/python-api.html) is a Python library (and [command-line tool](https://sqlite-utils.datasette.io/en/stable/cli.html) for quickly creating and manipulating SQLite database files.\n", "\n", "This tutorial will show you how to use the Python library to manipulate data.\n", "\n", "## Installation\n", "\n", "To install the library, run:\n", "\n", " pip install sqlite-utils\n", "\n", "You can run this in a Jupyter notebook cell by executing:\n", "\n", " %pip install sqlite-utils\n", " \n", "Or use `pip install -U sqlite-utils` to ensure you have upgraded to the most recent version." ] }, { "cell_type": "code", "execution_count": 1, "id": "bddee0d2", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Requirement already satisfied: sqlite_utils in /usr/local/Cellar/jupyterlab/3.0.16_1/libexec/lib/python3.9/site-packages (3.14)\n", "Requirement already satisfied: click-default-group in /usr/local/lib/python3.9/site-packages (from sqlite_utils) (1.2.2)\n", "Requirement already satisfied: sqlite-fts4 in /usr/local/lib/python3.9/site-packages (from sqlite_utils) (1.0.1)\n", "Requirement already satisfied: click in /Users/simon/Library/Python/3.9/lib/python/site-packages (from sqlite_utils) (7.1.2)\n", "Requirement already satisfied: tabulate in /usr/local/lib/python3.9/site-packages (from sqlite_utils) (0.8.7)\n", "Requirement already satisfied: dateutils in /usr/local/Cellar/jupyterlab/3.0.16_1/libexec/lib/python3.9/site-packages (from sqlite_utils) (0.6.12)\n", "Requirement already satisfied: python-dateutil in /usr/local/Cellar/jupyterlab/3.0.16_1/libexec/lib/python3.9/site-packages (from dateutils->sqlite_utils) (2.8.1)\n", "Requirement already satisfied: pytz in /usr/local/Cellar/jupyterlab/3.0.16_1/libexec/lib/python3.9/site-packages (from dateutils->sqlite_utils) (2021.1)\n", "Requirement already satisfied: six>=1.5 in /usr/local/Cellar/jupyterlab/3.0.16_1/libexec/lib/python3.9/site-packages (from python-dateutil->dateutils->sqlite_utils) (1.16.0)\n", "\u001b[33mWARNING: You are using pip version 21.1.1; however, version 21.2.2 is available.\n", "You should consider upgrading via the '/usr/local/Cellar/jupyterlab/3.0.16_1/libexec/bin/python3.9 -m pip install --upgrade pip' command.\u001b[0m\n", "Note: you may need to restart the kernel to use updated packages.\n" ] } ], "source": [ "%pip install -U sqlite_utils" ] }, { "cell_type": "code", "execution_count": 2, "id": "050e85a8", "metadata": {}, "outputs": [], "source": [ "import sqlite_utils" ] }, { "cell_type": "markdown", "id": "348bcbfc", "metadata": {}, "source": [ "You can use the library with a database file on disk by running:\n", "\n", " db = sqlite_utils.Database(\"path/to/my/database.db\")\n", "\n", "In this tutorial we will use an in-memory database. This is a quick way to try out new things, though you should note that when you close the notebook the data store in the in-memory database will be lost." ] }, { "cell_type": "code", "execution_count": 3, "id": "4b2aee7e", "metadata": {}, "outputs": [ { "data": { "text/plain": [ ">" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "db = sqlite_utils.Database(memory=True)\n", "db" ] }, { "cell_type": "markdown", "id": "1598ab43", "metadata": {}, "source": [ "## Creating a table\n", "\n", "We are going to create a new table in our database called `creatures` by passing in a Python list of dictionaries.\n", "\n", "`db[name_of_table]` will access a database table object with that name.\n", "\n", "Inserting data into that table will create it if it does not already exist." ] }, { "cell_type": "code", "execution_count": 4, "id": "4a0ac420", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "db[\"creatures\"].insert_all([{\n", " \"name\": \"Cleo\",\n", " \"species\": \"dog\",\n", " \"age\": 6\n", "}, {\n", " \"name\": \"Lila\",\n", " \"species\": \"chicken\",\n", " \"age\": 0.8,\n", "}, {\n", " \"name\": \"Bants\",\n", " \"species\": \"chicken\",\n", " \"age\": 0.8,\n", "}])" ] }, { "cell_type": "markdown", "id": "049d110b", "metadata": {}, "source": [ "Let's grab a `table` reference to the new creatures table:" ] }, { "cell_type": "code", "execution_count": 5, "id": "8d84ad9c", "metadata": {}, "outputs": [], "source": [ "table = db[\"creatures\"]" ] }, { "cell_type": "markdown", "id": "ffe45750", "metadata": {}, "source": [ "`sqlite-utils` automatically creates a table schema that matches the keys and data types of the dictionaries that were passed to `.insert_all()`.\n", "\n", "We can see that schema using `table.schema`:" ] }, { "cell_type": "code", "execution_count": 6, "id": "136cee1e", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "CREATE TABLE [creatures] (\n", " [name] TEXT,\n", " [species] TEXT,\n", " [age] FLOAT\n", ")\n" ] } ], "source": [ "print(table.schema)" ] }, { "cell_type": "markdown", "id": "9e5c3ae9", "metadata": {}, "source": [ "## Accessing data\n", "\n", "The `table.rows` property lets us loop through the rows in the table, returning each one as a Python dictionary:" ] }, { "cell_type": "code", "execution_count": 7, "id": "f812914d", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "{'name': 'Cleo', 'species': 'dog', 'age': 6.0}\n", "{'name': 'Lila', 'species': 'chicken', 'age': 0.8}\n", "{'name': 'Bants', 'species': 'chicken', 'age': 0.8}\n" ] } ], "source": [ "for row in table.rows:\n", " print(row)" ] }, { "cell_type": "markdown", "id": "60bc6b2c", "metadata": {}, "source": [ "The `db.query(sql)` method can be used to execute SQL queries and return the results as dictionaries:" ] }, { "cell_type": "code", "execution_count": 8, "id": "eaadd85f", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[{'name': 'Cleo', 'species': 'dog', 'age': 6.0},\n", " {'name': 'Lila', 'species': 'chicken', 'age': 0.8},\n", " {'name': 'Bants', 'species': 'chicken', 'age': 0.8}]" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "list(db.query(\"select * from creatures\"))" ] }, { "cell_type": "markdown", "id": "6614467b", "metadata": {}, "source": [ "Or in a loop:" ] }, { "cell_type": "code", "execution_count": 9, "id": "88fdd52e", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Cleo is a dog\n", "Lila is a chicken\n", "Bants is a chicken\n" ] } ], "source": [ "for row in db.query(\"select name, species from creatures\"):\n", " print(f'{row[\"name\"]} is a {row[\"species\"]}')" ] }, { "cell_type": "markdown", "id": "b81c031c", "metadata": {}, "source": [ "### SQL parameters\n", "\n", "You can run a parameterized query using `?` as placeholders and passing a list of variables. The variables you pass will be correctly quoted, protecting your code from SQL injection vulnerabilities." ] }, { "cell_type": "code", "execution_count": 10, "id": "267035d9", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[{'name': 'Cleo', 'species': 'dog', 'age': 6.0}]" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "list(db.query(\"select * from creatures where age > ?\", [1.0]))" ] }, { "cell_type": "markdown", "id": "87cb301b", "metadata": {}, "source": [ "As an alternative to question marks we can use `:name` parameters and feed in the values using a dictionary:" ] }, { "cell_type": "code", "execution_count": 11, "id": "83be9a80", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[{'name': 'Lila', 'species': 'chicken', 'age': 0.8},\n", " {'name': 'Bants', 'species': 'chicken', 'age': 0.8}]" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "list(db.query(\"select * from creatures where species = :species\", {\"species\": \"chicken\"}))" ] }, { "cell_type": "markdown", "id": "5e5179cc", "metadata": {}, "source": [ "### Primary keys\n", "\n", "When we created this table we did not specify a primary key. SQLite automatically creates a primary key called `rowid` if no other primary key is defined.\n", "\n", "We can run `select rowid, * from creatures` to see this hidden primary key:" ] }, { "cell_type": "code", "execution_count": 12, "id": "c9d963df", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[{'rowid': 1, 'name': 'Cleo', 'species': 'dog', 'age': 6.0},\n", " {'rowid': 2, 'name': 'Lila', 'species': 'chicken', 'age': 0.8},\n", " {'rowid': 3, 'name': 'Bants', 'species': 'chicken', 'age': 0.8}]" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "list(db.query(\"select rowid, * from creatures\"))" ] }, { "cell_type": "markdown", "id": "0f87cdfb", "metadata": {}, "source": [ "We can also see that using `table.pks_and_rows_where()`:" ] }, { "cell_type": "code", "execution_count": 13, "id": "d365e405", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1 {'rowid': 1, 'name': 'Cleo', 'species': 'dog', 'age': 6.0}\n", "2 {'rowid': 2, 'name': 'Lila', 'species': 'chicken', 'age': 0.8}\n", "3 {'rowid': 3, 'name': 'Bants', 'species': 'chicken', 'age': 0.8}\n" ] } ], "source": [ "for pk, row in table.pks_and_rows_where():\n", " print(pk, row)" ] }, { "cell_type": "markdown", "id": "5b0e9b74", "metadata": {}, "source": [ "Let's recreate the table with our own primary key, which we will call `id`.\n", "\n", "`table.drop()` drops the table:" ] }, { "cell_type": "code", "execution_count": 14, "id": "568a0e29", "metadata": {}, "outputs": [], "source": [ "table.drop()" ] }, { "cell_type": "code", "execution_count": 15, "id": "13ebd3ab", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "
" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "table" ] }, { "cell_type": "markdown", "id": "522aa6d0", "metadata": {}, "source": [ "We can see a list of tables in the database using `db.tables`:" ] }, { "cell_type": "code", "execution_count": 16, "id": "f3e62678", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[]" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "db.tables" ] }, { "cell_type": "markdown", "id": "6b80d523", "metadata": {}, "source": [ "We'll create the table again, this time with an `id` column.\n", "\n", "We use `pk=\"id\"` to specify that the `id` column should be treated as the primary key for the table:" ] }, { "cell_type": "code", "execution_count": 17, "id": "c9ee8b9f", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "
" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "db[\"creatures\"].insert_all([{\n", " \"id\": 1,\n", " \"name\": \"Cleo\",\n", " \"species\": \"dog\",\n", " \"age\": 6\n", "}, {\n", " \"id\": 2,\n", " \"name\": \"Lila\",\n", " \"species\": \"chicken\",\n", " \"age\": 0.8,\n", "}, {\n", " \"id\": 3,\n", " \"name\": \"Bants\",\n", " \"species\": \"chicken\",\n", " \"age\": 0.8,\n", "}], pk=\"id\")" ] }, { "cell_type": "code", "execution_count": 18, "id": "523e01ab", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "CREATE TABLE [creatures] (\n", " [id] INTEGER PRIMARY KEY,\n", " [name] TEXT,\n", " [species] TEXT,\n", " [age] FLOAT\n", ")\n" ] } ], "source": [ "print(table.schema)" ] }, { "cell_type": "markdown", "id": "811bea70", "metadata": {}, "source": [ "## Inserting more records\n", "\n", "We can call `.insert_all()` again to insert more records. Let's add two more chickens." ] }, { "cell_type": "code", "execution_count": 19, "id": "716df161", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "
" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "table.insert_all([{\n", " \"id\": 4,\n", " \"name\": \"Azi\",\n", " \"species\": \"chicken\",\n", " \"age\": 0.8,\n", "}, {\n", " \"id\": 5,\n", " \"name\": \"Snowy\",\n", " \"species\": \"chicken\",\n", " \"age\": 0.9,\n", "}], pk=\"id\")" ] }, { "cell_type": "code", "execution_count": 20, "id": "4b1b2476", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[{'id': 1, 'name': 'Cleo', 'species': 'dog', 'age': 6.0},\n", " {'id': 2, 'name': 'Lila', 'species': 'chicken', 'age': 0.8},\n", " {'id': 3, 'name': 'Bants', 'species': 'chicken', 'age': 0.8},\n", " {'id': 4, 'name': 'Azi', 'species': 'chicken', 'age': 0.8},\n", " {'id': 5, 'name': 'Snowy', 'species': 'chicken', 'age': 0.9}]" ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ "list(table.rows)" ] }, { "cell_type": "markdown", "id": "2af4ae75", "metadata": {}, "source": [ "Since the `id` column is an integer primary key, we can insert a record without specifying an ID and one will be automatically added.\n", "\n", "Since we are only adding one record we will use `.insert()` instead of `.insert_all()`." ] }, { "cell_type": "code", "execution_count": 21, "id": "246c6dd5", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "
" ] }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" } ], "source": [ "table.insert({\"name\": \"Blue\", \"species\": \"chicken\", \"age\": 0.9})" ] }, { "cell_type": "markdown", "id": "d7c28e4d", "metadata": {}, "source": [ "We can use `table.last_pk` to see the ID of the record we just added." ] }, { "cell_type": "code", "execution_count": 22, "id": "de012e1e", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "6" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ "table.last_pk" ] }, { "cell_type": "markdown", "id": "c38edaf4", "metadata": {}, "source": [ "Here's the full list of rows again:" ] }, { "cell_type": "code", "execution_count": 23, "id": "7c27075e", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[{'id': 1, 'name': 'Cleo', 'species': 'dog', 'age': 6.0},\n", " {'id': 2, 'name': 'Lila', 'species': 'chicken', 'age': 0.8},\n", " {'id': 3, 'name': 'Bants', 'species': 'chicken', 'age': 0.8},\n", " {'id': 4, 'name': 'Azi', 'species': 'chicken', 'age': 0.8},\n", " {'id': 5, 'name': 'Snowy', 'species': 'chicken', 'age': 0.9},\n", " {'id': 6, 'name': 'Blue', 'species': 'chicken', 'age': 0.9}]" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ "list(table.rows)" ] }, { "cell_type": "markdown", "id": "64931bd0", "metadata": {}, "source": [ "If you try to add a new record with an existing ID, you will get an `IntegrityError`:" ] }, { "cell_type": "code", "execution_count": 24, "id": "36327794", "metadata": {}, "outputs": [ { "ename": "IntegrityError", "evalue": "UNIQUE constraint failed: creatures.id", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mIntegrityError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mtable\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0minsert\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m{\u001b[0m\u001b[0;34m\"id\"\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;36m6\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"name\"\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;34m\"Red\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"species\"\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;34m\"chicken\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"age\"\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;36m0.9\u001b[0m\u001b[0;34m}\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;32m/usr/local/Cellar/jupyterlab/3.0.16_1/libexec/lib/python3.9/site-packages/sqlite_utils/db.py\u001b[0m in \u001b[0;36minsert\u001b[0;34m(self, record, pk, foreign_keys, column_order, not_null, defaults, hash_id, alter, ignore, replace, extracts, conversions, columns)\u001b[0m\n\u001b[1;32m 2027\u001b[0m \u001b[0mcolumns\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mDEFAULT\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2028\u001b[0m ):\n\u001b[0;32m-> 2029\u001b[0;31m return self.insert_all(\n\u001b[0m\u001b[1;32m 2030\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0mrecord\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2031\u001b[0m \u001b[0mpk\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mpk\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;32m/usr/local/Cellar/jupyterlab/3.0.16_1/libexec/lib/python3.9/site-packages/sqlite_utils/db.py\u001b[0m in \u001b[0;36minsert_all\u001b[0;34m(self, records, pk, foreign_keys, column_order, not_null, defaults, batch_size, hash_id, alter, ignore, replace, truncate, extracts, conversions, columns, upsert)\u001b[0m\n\u001b[1;32m 2143\u001b[0m \u001b[0mfirst\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;32mFalse\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2144\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 2145\u001b[0;31m self.insert_chunk(\n\u001b[0m\u001b[1;32m 2146\u001b[0m \u001b[0malter\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2147\u001b[0m \u001b[0mextracts\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;32m/usr/local/Cellar/jupyterlab/3.0.16_1/libexec/lib/python3.9/site-packages/sqlite_utils/db.py\u001b[0m in \u001b[0;36minsert_chunk\u001b[0;34m(self, alter, extracts, chunk, all_columns, hash_id, upsert, pk, conversions, num_records_processed, replace, ignore)\u001b[0m\n\u001b[1;32m 1955\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mquery\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mparams\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mqueries_and_params\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1956\u001b[0m \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1957\u001b[0;31m \u001b[0mresult\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdb\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mexecute\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mquery\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mparams\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1958\u001b[0m \u001b[0;32mexcept\u001b[0m \u001b[0mOperationalError\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0me\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1959\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0malter\u001b[0m \u001b[0;32mand\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0;34m\" column\"\u001b[0m \u001b[0;32min\u001b[0m \u001b[0me\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;32m/usr/local/Cellar/jupyterlab/3.0.16_1/libexec/lib/python3.9/site-packages/sqlite_utils/db.py\u001b[0m in \u001b[0;36mexecute\u001b[0;34m(self, sql, parameters)\u001b[0m\n\u001b[1;32m 255\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_tracer\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0msql\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mparameters\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 256\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mparameters\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 257\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mconn\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mexecute\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0msql\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mparameters\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 258\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 259\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mconn\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mexecute\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0msql\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;31mIntegrityError\u001b[0m: UNIQUE constraint failed: creatures.id" ] } ], "source": [ "table.insert({\"id\": 6, \"name\": \"Red\", \"species\": \"chicken\", \"age\": 0.9})" ] }, { "cell_type": "markdown", "id": "2e00692f", "metadata": {}, "source": [ "You can use `replace=True` to replace the matching record with a new one:" ] }, { "cell_type": "code", "execution_count": 25, "id": "2be75589", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "
" ] }, "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "source": [ "table.insert({\"id\": 6, \"name\": \"Red\", \"species\": \"chicken\", \"age\": 0.9}, replace=True)" ] }, { "cell_type": "code", "execution_count": 26, "id": "83281675", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[{'id': 1, 'name': 'Cleo', 'species': 'dog', 'age': 6.0},\n", " {'id': 2, 'name': 'Lila', 'species': 'chicken', 'age': 0.8},\n", " {'id': 3, 'name': 'Bants', 'species': 'chicken', 'age': 0.8},\n", " {'id': 4, 'name': 'Azi', 'species': 'chicken', 'age': 0.8},\n", " {'id': 5, 'name': 'Snowy', 'species': 'chicken', 'age': 0.9},\n", " {'id': 6, 'name': 'Red', 'species': 'chicken', 'age': 0.9}]" ] }, "execution_count": 26, "metadata": {}, "output_type": "execute_result" } ], "source": [ "list(table.rows)" ] }, { "cell_type": "markdown", "id": "d7122b76", "metadata": {}, "source": [ "## Updating a record\n", "\n", "We will rename that row back to `Blue`, this time using the `table.update(pk, updates)` method:" ] }, { "cell_type": "code", "execution_count": 28, "id": "43df156d", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "
" ] }, "execution_count": 28, "metadata": {}, "output_type": "execute_result" } ], "source": [ "table.update(6, {\"name\": \"Blue\"})" ] }, { "cell_type": "code", "execution_count": 32, "id": "0b8f8422", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[{'id': 6, 'name': 'Blue', 'species': 'chicken', 'age': 0.9}]" ] }, "execution_count": 32, "metadata": {}, "output_type": "execute_result" } ], "source": [ "list(db.query(\"select * from creatures where id = ?\", [6]))" ] }, { "cell_type": "markdown", "id": "58142b86", "metadata": {}, "source": [ "## Extracting one of the columns into another table\n", "\n", "Our current table has a `species` column with a string in it - let's pull that out into a separate table.\n", "\n", "We can do that using the [table.extract() method](https://sqlite-utils.datasette.io/en/stable/python-api.html#extracting-columns-into-a-separate-table)." ] }, { "cell_type": "code", "execution_count": 34, "id": "6ab69111", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "
" ] }, "execution_count": 34, "metadata": {}, "output_type": "execute_result" } ], "source": [ "table.extract(\"species\")" ] }, { "cell_type": "markdown", "id": "dca327b2", "metadata": {}, "source": [ "We now have a new table called `species`, which we can see using the `db.tables` method:" ] }, { "cell_type": "code", "execution_count": 35, "id": "76e95b36", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[
,
]" ] }, "execution_count": 35, "metadata": {}, "output_type": "execute_result" } ], "source": [ "db.tables" ] }, { "cell_type": "markdown", "id": "5ea43bf5", "metadata": {}, "source": [ "Our creatures table has been modified - instead of a `species` column it now has `species_id` which is a foreign key to the new table:" ] }, { "cell_type": "code", "execution_count": 37, "id": "c0438bff", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "CREATE TABLE \"creatures\" (\n", " [id] INTEGER PRIMARY KEY,\n", " [name] TEXT,\n", " [species_id] INTEGER,\n", " [age] FLOAT,\n", " FOREIGN KEY([species_id]) REFERENCES [species]([id])\n", ")\n", "[{'id': 1, 'name': 'Cleo', 'species_id': 1, 'age': 6.0}, {'id': 2, 'name': 'Lila', 'species_id': 2, 'age': 0.8}, {'id': 3, 'name': 'Bants', 'species_id': 2, 'age': 0.8}, {'id': 4, 'name': 'Azi', 'species_id': 2, 'age': 0.8}, {'id': 5, 'name': 'Snowy', 'species_id': 2, 'age': 0.9}, {'id': 6, 'name': 'Blue', 'species_id': 2, 'age': 0.9}]\n" ] } ], "source": [ "print(db[\"creatures\"].schema)\n", "print(list(db[\"creatures\"].rows))" ] }, { "cell_type": "markdown", "id": "0452c201", "metadata": {}, "source": [ "The new `species` table has been created and populated too:" ] }, { "cell_type": "code", "execution_count": 39, "id": "5d38c3a8", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "CREATE TABLE [species] (\n", " [id] INTEGER PRIMARY KEY,\n", " [species] TEXT\n", ")\n", "[{'id': 1, 'species': 'dog'}, {'id': 2, 'species': 'chicken'}]\n" ] } ], "source": [ "print(db[\"species\"].schema)\n", "print(list(db[\"species\"].rows))" ] }, { "cell_type": "markdown", "id": "a0312d1e", "metadata": {}, "source": [ "We can use a join SQL query to combine data from these two tables:" ] }, { "cell_type": "code", "execution_count": 44, "id": "6734ed5d", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[{'id': 1, 'name': 'Cleo', 'age': 6.0, 'species_id': 1, 'species': 'dog'},\n", " {'id': 2, 'name': 'Lila', 'age': 0.8, 'species_id': 2, 'species': 'chicken'},\n", " {'id': 3, 'name': 'Bants', 'age': 0.8, 'species_id': 2, 'species': 'chicken'},\n", " {'id': 4, 'name': 'Azi', 'age': 0.8, 'species_id': 2, 'species': 'chicken'},\n", " {'id': 5, 'name': 'Snowy', 'age': 0.9, 'species_id': 2, 'species': 'chicken'},\n", " {'id': 6, 'name': 'Blue', 'age': 0.9, 'species_id': 2, 'species': 'chicken'}]" ] }, "execution_count": 44, "metadata": {}, "output_type": "execute_result" } ], "source": [ "list(db.query(\"\"\"\n", " select\n", " creatures.id,\n", " creatures.name,\n", " creatures.age,\n", " species.id as species_id,\n", " species.species\n", " from creatures\n", " join species on creatures.species_id = species.id\n", "\"\"\"))" ] }, { "cell_type": "code", "execution_count": null, "id": "5c4802ac", "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "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.9.6" } }, "nbformat": 4, "nbformat_minor": 5 }