Use Python's built-in sqlite3 module to create a portable database file, store records, query data, and export results. Start with this working example, then explore the separate commands below.
import sqlite3
my_conn = sqlite3.connect(':memory:')
my_conn.execute('CREATE TABLE student (id INTEGER PRIMARY KEY, name TEXT)')
my_conn.execute('INSERT INTO student (name) VALUES (?)', ('John Deo',))
print(my_conn.execute('SELECT id, name FROM student').fetchall())
my_conn.close()
Output:
[(1, 'John Deo')]

Run the SQLite examples step by step, create the student table, practice safe inserts and updates, inspect database tables, and export CSV data in Google Colab.
View on GitHubSQLite is a lightweight, serverless relational database. It works well for desktop applications, prototypes, local reports, and small-to-medium datasets. Python includes sqlite3 in its standard library, so there is no separate database server to install.
The example above runs entirely in memory. For a persistent database, continue with the next section. The sqlite3 module is included with standard Python builds.
SQLite creates the database file when it does not already exist. Use a dedicated practice database rather than a production file.
import sqlite3
my_conn = sqlite3.connect('plus2net_sqlite_practice.db')
my_conn.execute('PRAGMA foreign_keys = ON')
print('Database connection ready')
Expected output:
Database connection ready
Use an absolute path if needed; the relative filename above writes to the current working directory. See SQLite connection details and SQLite with Google Drive in Colab.
Use CREATE TABLE IF NOT EXISTS to avoid an error when the table already exists. INTEGER PRIMARY KEY supplies automatically generated numeric IDs when omitted.
my_conn.execute('''CREATE TABLE IF NOT EXISTS student (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
class TEXT,
mark INTEGER CHECK (mark BETWEEN 0 AND 100),
gender TEXT
)''')
my_conn.commit()
print('Student table ready')
Expected output:
Student table ready
Use placeholders and executemany() for multiple rows. The example includes recognizable student records from this tutorial; the linked full dataset contains more rows.
students = [
(1, 'John Deo', 'Four', 75, 'female'),
(2, 'Max Ruin', 'Three', 85, 'male'),
(3, 'Arnold', 'Three', 55, 'male'),
(4, 'Krish Star', 'Four', 60, 'female'),
(34, 'Gain Toe', 'Seven', 69, 'male'),
(35, 'Rows Noump', 'Six', 88, 'female')
]
my_conn.executemany('''INSERT OR IGNORE INTO student
(id, name, class, mark, gender)
VALUES (?, ?, ?, ?, ?)''', students)
my_conn.commit()
print('Sample records available:', my_conn.execute('SELECT COUNT(*) FROM student').fetchone()[0])
Expected output (example):
Sample records available: 6 (on a new practice database)
INSERT OR IGNORE skips an existing record with the same primary key. If you have previously added more records, the count may be higher.
Full student dataset (35 rows) The examples here use six rows so results stay easy to verify.
fetchall() returns a list of tuples. ORDER BY keeps the demonstration output predictable.
rows = my_conn.execute('SELECT id, name, class, mark, gender FROM student ORDER BY id').fetchall()
for row in rows:
print(row)
Expected output:
(1, 'John Deo', 'Four', 75, 'female')
(2, 'Max Ruin', 'Three', 85, 'male')
(3, 'Arnold', 'Three', 55, 'male')
(4, 'Krish Star', 'Four', 60, 'female')
(34, 'Gain Toe', 'Seven', 69, 'male')
(35, 'Rows Noump', 'Six', 88, 'female')
Pass search values separately instead of building SQL by concatenating user input.
minimum_mark = 75
rows = my_conn.execute('''SELECT name, mark FROM student
WHERE mark >= ? ORDER BY mark DESC, name''', (minimum_mark,)).fetchall()
for row in rows:
print(row)
Expected output:
('Rows Noump', 88)
('Max Ruin', 85)
('John Deo', 75)
fetchone() retrieves the first record, or returns None if there is no match.
row = my_conn.execute(
'SELECT name, mark FROM student WHERE id = ?', (2,)
).fetchone()
print(row)
Expected output:
('Max Ruin', 85)
PRAGMA table_info() reports the column position, name, declared type, NOT NULL flag, default value, and primary-key flag.
for row in my_conn.execute('PRAGMA table_info(student)'):
print(row)
Expected output:
(0, 'id', 'INTEGER', 0, None, 1)
(1, 'name', 'TEXT', 1, None, 0)
(2, 'class', 'TEXT', 0, None, 0)
(3, 'mark', 'INTEGER', 0, None, 0)
(4, 'gender', 'TEXT', 0, None, 0)
Each PRAGMA result contains the column name at index 1.
columns = [row[1] for row in my_conn.execute('PRAGMA table_info(student)')]
print(columns)
Expected output:
['id', 'name', 'class', 'mark', 'gender']
COUNT(*) counts the records in a table. Add WHERE when only a subset should be counted.
total = my_conn.execute('SELECT COUNT(*) FROM student').fetchone()[0]
print('Total student records:', total)
Expected output (example):
Total student records: 6 (on a new practice database)
GROUP BY summarizes records across categories.
rows = my_conn.execute('''SELECT class, COUNT(*) AS total FROM student
GROUP BY class ORDER BY class''').fetchall()
for row in rows:
print(row)
Expected output:
('Four', 2)
('Seven', 1)
('Six', 1)
('Three', 2)
Use cursor.lastrowid after INSERT. The name is chosen for the dedicated practice database, not for a live student table.
cursor = my_conn.execute(
'INSERT INTO student (name, class, mark, gender) VALUES (?, ?, ?, ?)',
('New Student', 'Four', 45, 'female')
)
my_conn.commit()
print('Inserted row ID:', cursor.lastrowid)
Expected output (example):
Inserted row ID: 36 (with the initial sample records)
The generated ID can differ if you have run the INSERT example before.
Update only matching rows with WHERE; rowcount indicates how many records matched.
cursor = my_conn.execute(
'UPDATE student SET mark = ? WHERE name = ?',
(90, 'Max Ruin')
)
my_conn.commit()
print('Rows updated:', cursor.rowcount)
Expected output:
Rows updated: 1
DELETE without WHERE removes every row. This example deletes only the practice row inserted earlier.
cursor = my_conn.execute(
'DELETE FROM student WHERE name = ?', ('New Student',)
)
my_conn.commit()
print('Rows deleted:', cursor.rowcount)
Expected output:
Rows deleted: 1
SQLite changes() reports rows changed by the most recent INSERT, UPDATE, or DELETE statement on this connection.
changed = my_conn.execute('SELECT changes()').fetchone()[0]
print('Rows affected by previous statement:', changed)
Expected output:
Rows affected by previous statement: 1
A CHECK constraint rejects invalid marks. The context manager commits valid transactions and rolls back after an exception.
try:
with my_conn:
my_conn.execute(
'INSERT INTO student (name, mark) VALUES (?, ?)',
('Invalid Mark', 150)
)
except sqlite3.IntegrityError as error:
print('Rejected invalid mark:', type(error).__name__)
Expected output:
Rejected invalid mark: IntegrityError
sqlite_master describes database objects. This query shows user-created tables.
rows = my_conn.execute('''SELECT name FROM sqlite_master
WHERE type = 'table' AND name NOT LIKE 'sqlite_%'
ORDER BY name''').fetchall()
for row in rows:
print(row[0])
Expected output:
student
Table identifiers cannot be SQL parameters; properly quote database identifiers when composing a statement from table names.
table_names = [row[0] for row in my_conn.execute(
"SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'"
)]
for table_name in table_names:
quoted_name = '"' + table_name.replace('"', '""') + '"'
total = my_conn.execute('SELECT COUNT(*) FROM ' + quoted_name).fetchone()[0]
print(table_name, ':', total)
Expected output:
student : 6 (after the insert/update/delete sequence)
Set row_factory to sqlite3.Row when you need access by column name, such as row["name"].
my_conn.row_factory = sqlite3.Row
row = my_conn.execute('SELECT id, name FROM student WHERE id = ?', (1,)).fetchone()
print(dict(row))
my_conn.row_factory = None
Expected output:
{'id': 1, 'name': 'John Deo'}
SAVEPOINT allows testing a change and undoing it without changing the permanent practice data.
my_conn.execute('SAVEPOINT practice_demo')
my_conn.execute('UPDATE student SET mark = 99 WHERE id = 1')
print('Temporary mark:', my_conn.execute(
'SELECT mark FROM student WHERE id = 1').fetchone()[0])
my_conn.execute('ROLLBACK TO SAVEPOINT practice_demo')
my_conn.execute('RELEASE SAVEPOINT practice_demo')
print('Restored mark:', my_conn.execute(
'SELECT mark FROM student WHERE id = 1').fetchone()[0])
Expected output:
Temporary mark: 99
Restored mark: 75
SQLite stores common date values as ISO-format text and provides functions such as date() for calculations.
today = my_conn.execute("SELECT date('now')").fetchone()[0]
next_week = my_conn.execute("SELECT date('now', '+7 days')").fetchone()[0]
print('Today:', today)
print('After seven days:', next_week)
Expected output (example):
Today: YYYY-MM-DD
After seven days: YYYY-MM-DD (seven days later)
For further use, see date queries, date table SQL dump and BLOB binary data.
SQLAlchemy is optional. It is useful when one application works with several SQL databases.
pip install sqlalchemy
Use text() for SQL statements and a context manager for the connection. This reads the practice database created above.
from sqlalchemy import create_engine, text
engine = create_engine('sqlite:///plus2net_sqlite_practice.db')
with engine.connect() as connection:
rows = connection.execute(
text('SELECT name, mark FROM student WHERE mark >= :minimum'),
{'minimum': 80}
).fetchall()
for row in rows:
print(row)
Expected output:
('Max Ruin', 90)
('Rows Noump', 88)
Catch SQLAlchemyError where an application needs to report errors without exposing connection credentials.
from sqlalchemy.exc import SQLAlchemyError
try:
with engine.connect() as connection:
result = connection.execute(text('SELECT COUNT(*) FROM student'))
print('Total:', result.scalar_one())
except SQLAlchemyError as error:
print('Database operation failed:', type(error).__name__)
Expected output (example):
Total: 6 (on a new practice database)
More examples: SQLAlchemy insert and SQLAlchemy delete.
The built-in csv module produces a spreadsheet-friendly CSV without extra packages. For .xlsx files, follow the related Excel tutorial.
import csv
rows = my_conn.execute('SELECT id, name, class, mark, gender FROM student ORDER BY id')
with open('student_export.csv', 'w', newline='', encoding='utf-8') as file:
writer = csv.writer(file)
writer.writerow(['id', 'name', 'class', 'mark', 'gender'])
writer.writerows(rows)
print('Created student_export.csv')
Expected output:
Created student_export.csv
For Excel workbook output and two-way data transfer, see SQLite and Excel with Pandas and SQLite and Google Sheets.
Close the connection after finishing the database tasks.
my_conn.close()
print('Database connection closed')
Expected output:
Database connection closed
A SQLite database is stored in a file. Only delete a database when you are sure it is no longer needed. This safely demonstrates deletion on a separate temporary example file.
from pathlib import Path
example_path = Path('sqlite_delete_example.db')
example_connection = sqlite3.connect(str(example_path))
example_connection.close()
example_path.unlink()
print('Example database file removed')
Expected output:
Example database file removed
Use these shorter tutorials to learn individual SQL commands before combining them in a complete program.
| Topic | Learn |
|---|---|
| Connection | Creating and opening a database |
| BETWEEN | Select values within a range |
| CASE | Conditional SQL expressions |
| CHECK | Validating inserted values |
| INSERT | Adding one or many records |
| IIF | Conditional SQL expressions |
| UPDATE | Changing existing records |
| DELETE | Removing rows safely |
| DISTINCT | Selecting unique values |
| SELECT | Reading rows |
| LIMIT | Limiting results |
| ORDER BY | Sorting rows |
| LIKE | Pattern matching |
| Date validation | Using CHECK for dates |
| Date queries | Filtering dates |
| BLOB | Binary files and images |
| Tkinter SQLite connector | Build a database GUI |
| Excel integration | Read and write spreadsheets |
| Google Sheets integration | Transfer data |
| SQLAlchemy INSERT | Insert through SQLAlchemy |
| SQLAlchemy DELETE | Delete through SQLAlchemy |
SQLite with Google Drive in Colab Download sample database and notebooks (.zip)
SQLite is also useful inside graphical tools. Explore Tkinter database applications and the Python webpage auditor.
sqlite3.OperationalError: no such tablesqlite3.IntegrityErrorsqlite3.ProgrammingError: Cannot operate on a closed databasedatabase is lockedcommit() after INSERT, UPDATE or DELETE, or use with my_conn: for transaction handling.no such module named sqlalchemypip install sqlalchemy in the Python environment running the script.No. The sqlite3 Python module is included with standard Python installations. A separate database server is unnecessary.
A file-based SQLite connection stores data in the specified file. The :memory: option creates a temporary database that disappears when its connection closes.
Use bound placeholders (?) for values. Do not concatenate values into SQL queries. Quote validated table and column identifiers if dynamic identifiers are unavoidable.
Write operations can be part of a transaction. commit() makes changes persistent, while rollback() undoes uncommitted changes.
Yes. Use Pandas read_sql_query() to create a DataFrame, and export with to_excel(). You can also use Python's built-in csv module, as shown above.
SQLite is useful for small services and moderate concurrency. For high write concurrency or distributed server workloads, consider a server-based database.
Author & Instructor at plus2net
I write and maintain practical tutorials on Python, PHP, SQL, JavaScript, HTML, jQuery, and web development at plus2net. The tutorials focus on clear explanations, working examples, and code that readers can test and adapt while learning.