SQL Language¶
SQL (Structured Query Language) is a language used to interact with databases. We use SQL in the following courses at CodeWizardsHQ:
Middle School | High School |
---|---|
Intro to Databases | APIs and Databases |
Mastering APIs | Professional Web App Development |
Mastering Databases | Capstone 2 |
Capstone 3 | Mastering MVC Framework |
Object Relational Mapping | |
Capstone 3 |
In this section of our documentation, you'll find references to most of the core SQL language features that we use in our CodeWizardsHQ courses. All of our courses interact with databases using Python + SQL, so we'll show the core SQL syntax first and then give a working example in Python.
You'll also find many Further reading sections, which pull from these excellent SQL/Python resources:
What Is A Relational Database?¶
In a relational database, you structure your data in tables made up of rows and columns, kind of like an Excel spreadsheet. You can combine data from multiple tables using JOIN
s or just pull data from a single table.
For example, consider the two tables below:
users table
user_id | username | password |
---|---|---|
1 | djs | mypa$$word |
2 | django | w0ff |
3 | alecg | c0de |
teachers table
teacher_id | user_id | username | is_admin |
---|---|---|---|
1 | 1 | djs | 1 |
2 | 3 | alecg | 0 |
We could get the username and password of all users that are teachers and admins like this:
SELECT
users.username, users.password
FROM
users
JOIN
teachers
USING
(user_id)
WHERE
teachers.is_admin = 1;
┌──────────┬────────────┐
│ username │ password │
├──────────┼────────────┤
│ djs │ mypa$$word │
└──────────┴────────────┘
There are many different relational database implementations (MySQL, Postgres, etc.) but we use SQLite at CodeWizardsHQ because it is easy to work with and supports most of the common SQL features.
Why Do We Need SQL?¶
When working with a database, you need a way to talk to the database and get data into/out of it. SQL is the language we use to do this in a SQLite database. SQL allows you to express relationships in a database in a structured way.
Why Do We Use Python And SQL Together?¶
Although you can use raw SQL commands to talk to a SQL database, we use Python at CodeWizardsHQ because often you'll interact with databases this way in the real world. Think about apps you've used that store data about you between visits. That's using a database and a programming language (like Python) to interact with the database!
The SQL portions of a Python DB query will be a Python str
. Consider this INSERT
statement in raw SQL:
INSERT INTO users (username, password) VALUES ("djs", "mypa$$word");
To run that from Python, we would do this:
import sqlite3
con = sqlite3.connect("users-database.db")
sql = con.cursor()
query = """
INSERT INTO users (username, password) VALUES ("djs", "mypa$$word");
"""
sql.execute(query)
con.commit()
The important thing to remember is that the query
is just a str
that you pass to sql.execute()
. If you make a change to a table in the database (as we did above) then you use the con.commit()
method to save the change (although in some CWHQ courses you merely view the results without changing the database). The semi-colon (;
) isn't required when using a query from Python, but we'll keep it for consistency between the raw SQL examples.
Bounded Parameters¶
When accepting user input in a Python program that modifies a SQL database, you'll use ?
as placeholders for any user-entered data and then pass the data to sql.execute()
as a list
like this:
import sqlite3
con = sqlite3.connect("users-database.db")
sql = con.cursor()
username = input("Enter your username: ")
password = input("Enter your password: ")
# Use `?` for any user-entered data
query = """
INSERT INTO users (username, password) VALUES (?, ?);
"""
# The `username` and `password` are bound to the `?` in the `query`
sql.execute(query, [username, password])
con.commit()
Further Reading
Aggregate Functions¶
Just like programming languages such as Python and JavaScript, SQL has functions to perform common tasks on result set called Aggregate Functions. The example below shows the different Aggregate Functions we use in CWHQ courses.
Raw SQL
SELECT * FROM products;
┌────────────┬──────────────────────────┬───────────────┬──────────────────┐
│ product_id │ product_name │ product_price │ product_category │
├────────────┼──────────────────────────┼───────────────┼──────────────────┤
│ 1 │ Dell XPS 17 │ 1599.99 │ Computers │
│ 2 │ Blue Snowball Microphone │ 99.5 │ Microphones │
│ 3 │ System76 Thelio B1 │ 1255.55 │ Computers │
│ 4 │ Logitech M1 │ 34.99 │ Accessories │
│ 5 │ Seagate S1 SSD │ 88.75 │ Accessories │
│ 6 │ MacBook Pro 16 │ 2100.5 │ Computers │
│ 7 │ Rode Z28 │ 275.99 │ Microphones │
│ 8 │ Lenovo ThinkPad │ 950.75 │ Computers │
└────────────┴──────────────────────────┴───────────────┴──────────────────┘
-- Get the average price of all products
SELECT AVG(product_price) AS average_product_price FROM products;
┌───────────────────────┐
│ average_product_price │
├───────────────────────┤
│ 800.7525 │
└───────────────────────┘
-- Count the number of products
SELECT COUNT(*) AS total_products FROM products;
┌────────────────┐
│ total_products │
├────────────────┤
│ 8 │
└────────────────┘
-- Get the most expensive product
SELECT product_name || " $" || MAX(product_price) AS most_expensive_product
FROM products;
┌────────────────────────┐
│ most_expensive_product │
├────────────────────────┤
│ MacBook Pro 16 $2100.5 │
└────────────────────────┘
-- Get the least expensive product
SELECT product_name || " $" || MIN(product_price) AS least_expensive_product
FROM products;
┌─────────────────────────┐
│ least_expensive_product │
├─────────────────────────┤
│ Logitech M1 $34.99 │
└─────────────────────────┘
-- Get the total cost of all products combined
SELECT SUM(product_price) AS total_cost_all_products FROM products;
┌─────────────────────────┐
│ total_cost_all_products │
├─────────────────────────┤
│ 6406.02 │
└─────────────────────────┘
Python + SQL
import sqlite3
con = sqlite3.connect("products-database.db")
sql = con.cursor()
def execute_query_and_display_rows(query):
result = sql.execute(query)
rows = result.fetchall()
for row in rows:
print(row)
query = """
SELECT * FROM products;
"""
print("All products:")
execute_query_and_display_rows(query)
query = """
SELECT AVG(product_price) AS average_product_price FROM products;
"""
print("\nAverage cost of all products:")
execute_query_and_display_rows(query)
query = """
SELECT COUNT(*) AS total_products FROM products;
"""
print("\nTotal number of products:")
execute_query_and_display_rows(query)
query = """
SELECT product_name || " $" || MAX(product_price) AS most_expensive_product
FROM products;
"""
print("\nMost expensive product:")
execute_query_and_display_rows(query)
query = """
SELECT product_name || " $" || MIN(product_price) AS least_expensive_product
FROM products;
"""
print("\nLeast expensive product:")
execute_query_and_display_rows(query)
query = """
SELECT SUM(product_price) AS total_cost_all_products FROM products;
"""
print("\nTotal cost of all products:")
execute_query_and_display_rows(query)
Output
All products:
(1, 'Dell XPS 17', 1599.99, 'Computers')
(2, 'Blue Snowball Microphone', 99.5, 'Microphones')
(3, 'System76 Thelio B1', 1255.55, 'Computers')
(4, 'Logitech M1', 34.99, 'Accessories')
(5, 'Seagate S1 SSD', 88.75, 'Accessories')
(6, 'MacBook Pro 16', 2100.5, 'Computers')
(7, 'Rode Z28', 275.99, 'Microphones')
(8, 'Lenovo ThinkPad', 950.75, 'Computers')
Average cost of all products:
(800.7524999999999,)
Total number of products:
(8,)
Most expensive product:
('MacBook Pro 16 $2100.5',)
Least expensive product:
('Logitech M1 $34.99',)
Total cost of all products:
(6406.0199999999995,)
Further Reading
Arithmetic Operators¶
When writing queries, you may find that the table does not have a column for a certain value but has columns which could be used to calculate it. SQL supports arithmetic operations in selections and WHERE
clauses. In a selection, this will add a column to the result which calculates the expression for each result.
Raw SQL
SELECT name, price, price * 0.07 FROM products;
┌───────────┬──────────┬─────────────────┐
│ name │ price │ price * 0.07 │
├───────────┼──────────┼─────────────────┤
│ cookies │ 10.00 │ 10.70 │
│ ice cream │ 5.00 │ 5.35 │
│ donuts │ 7.00 │ 7.49 │
└───────────┴──────────┴─────────────────┘
You can alias the calculated column using AS
for a clearer output.
Raw SQL
SELECT name, price, price * 0.07 AS sales_tax FROM products;
┌───────────┬──────────┬─────────────────┐
│ name │ price │ sales_tax │
├───────────┼──────────┼─────────────────┤
│ cookies │ 10.00 │ 0.70 │
│ ice cream │ 5.00 │ 0.35 │
│ donuts │ 7.00 │ 0.49 │
└───────────┴──────────┴─────────────────┘
The operations are also supported in WHERE
clauses.
-- select all rows where tax is greater than 10 dollars
SELECT * FROM products WHERE price * 0.07 > 10.00
Subqueries¶
A subquery, also known as a nested query or inner query, is a query that is embedded within another SQL query. Subqueries allow you to retrieve data from one or more tables and use that result within another query. They are an essential feature of SQL for performing complex queries and data manipulation.
There are two primary types of subqueries:
-
Scalar Subquery: A scalar subquery is a subquery that returns a single value. This type of subquery is typically used within a
SELECT
orWHERE
clause to compare a single value with the result of the subquery.The subquery-- Find the average salary of employees SELECT AVG(salary) FROM employees; ┌────────────────┐ | AVG(salary) │ ├────────────────┤ │ 85000 | └────────────────┘ -- Use the above query in a subquery SELECT name, salary FROM employees WHERE salary > (SELECT AVG(salary) FROM employees); ┌──────────┬─────────────┐ │ name │ salary │ ├──────────┼─────────────┤ │ Alice │ 100000 │ │ Bob │ 90000 │ │ Charlie │ 85001 │ └──────────┴─────────────┘
SELECT AVG(salary) FROM employees
is a single, or scalar, value. When used in theWHERE
clause, the full query finds all employees whose salary is greater than the average salary. -
Table Subquery: A table subquery, also known as a derived table or inline view, returns a result set (a table) that can be used in a
FROM
clause or joined with other tables in the main query.-- Count the number of courses by the programming language they use SELECT language, COUNT(*) as num_courses FROM programming_courses GROUP BY language; ┌──────────┬─────────────┐ │ language │ num_courses │ ├──────────┼─────────────┤ │ Python │ 10 │ │ HTML/CSS │ 6 │ │ SQL │ 3 │ └──────────┴─────────────┘ -- Using the above query in a subquery, we can use the MAX() function on the count column SELECT language, MAX(num_courses) FROM (SELECT language, COUNT(*) as num_courses FROM programming_courses GROUP BY language ); ┌──────────┬──────────────────┐ │ language │ MAX(num_courses) │ ├──────────┼──────────────────┤ │ Python │ 10 │ └──────────┴──────────────────┘
Further Reading
ALTER TABLE¶
After creating a table, you may need to add or rename a column. The ALTER TABLE
command allows you to do this.
Raw SQL
SELECT * FROM users;
┌─────────┬──────────┬────────────┐
│ user_id │ username │ password │
├─────────┼──────────┼────────────┤
│ 1 │ djs │ mypa$$word │
│ 2 │ django │ w0ff │
│ 3 │ alecg │ c0de │
└─────────┴──────────┴────────────┘
-- Renaming a column
ALTER TABLE users RENAME username TO teacher_name;
SELECT * FROM users;
┌─────────┬──────────────┬────────────┐
│ user_id │ teacher_name │ password │
├─────────┼──────────────┼────────────┤
│ 1 │ djs │ mypa$$word │
│ 2 │ django │ w0ff │
│ 3 │ alecg │ c0de │
└─────────┴──────────────┴────────────┘
-- Adding a new column with a default value for each row
ALTER TABLE users ADD is_admin INTEGER DEFAULT 0;
SELECT * FROM users;
┌─────────┬──────────────┬────────────┬──────────┐
│ user_id │ teacher_name │ password │ is_admin │
├─────────┼──────────────┼────────────┼──────────┤
│ 1 │ djs │ mypa$$word │ 0 │
│ 2 │ django │ w0ff │ 0 │
│ 3 │ alecg │ c0de │ 0 │
└─────────┴──────────────┴────────────┴──────────┘
Python + SQL
import sqlite3
con = sqlite3.connect("users-database.db")
sql = con.cursor()
def display_all_users_and_column_names():
# Don't worry about this, it just shows us the column names
query = """
SELECT name FROM PRAGMA_TABLE_INFO('users');
"""
result = sql.execute(query)
print("\nColumn names:")
print(*result.fetchall())
query = """
SELECT * FROM users;
"""
result = sql.execute(query)
rows = result.fetchall()
print("\nRows:")
print(rows)
display_all_users_and_column_names()
query = """
ALTER TABLE users RENAME username TO teacher_name;
"""
sql.execute(query)
display_all_users_and_column_names()
query = """
ALTER TABLE users ADD is_admin INTEGER DEFAULT 0;
"""
sql.execute(query)
display_all_users_and_column_names()
# Make sure to commit the changes to the DB
con.commit()
Output
Column names:
('user_id',) ('username',) ('password',)
Rows:
[(1, 'djs', 'mypa$$word'), (2, 'django', 'w0ff'), (3, 'alecg', 'c0de')]
Column names:
('user_id',) ('teacher_name',) ('password',)
Rows:
[(1, 'djs', 'mypa$$word'), (2, 'django', 'w0ff'), (3, 'alecg', 'c0de')]
Column names:
('user_id',) ('teacher_name',) ('password',) ('is_admin',)
Rows:
[(1, 'djs', 'mypa$$word', 0), (2, 'django', 'w0ff', 0), (3, 'alecg', 'c0de', 0)]
Further Reading
AS¶
The AS
clause is used to alias a column or table name. We only use it at CWHQ for column aliases though, so that's all that is covered here.
Aliasing Column Names¶
When aliasing column names, the AS
clause comes in handy when you are using the concatenation operator (||
) to combine multiple columns or when using Aggregate Functions to perform some calculation on a group of rows.
Raw SQL
SELECT * FROM products;
┌────────────┬──────────────────────────┬───────────────┬──────────────────┐
│ product_id │ product_name │ product_price │ product_category │
├────────────┼──────────────────────────┼───────────────┼──────────────────┤
│ 1 │ Dell XPS 17 │ 1599.99 │ Computers │
│ 2 │ Blue Snowball Microphone │ 99.5 │ Microphones │
│ 3 │ System76 Thelio B1 │ 1255.55 │ Computers │
│ 4 │ Logitech M1 │ 34.99 │ Accessories │
│ 5 │ Seagate S1 SSD │ 88.75 │ Accessories │
│ 6 │ MacBook Pro 16 │ 2100.5 │ Computers │
│ 7 │ Rode Z28 │ 275.99 │ Microphones │
│ 8 │ Lenovo ThinkPad │ 950.75 │ Computers │
└────────────┴──────────────────────────┴───────────────┴──────────────────┘
/*
* Build a result set that combines `product_name` and `product_price`
* into a single column
*/
SELECT product_name || " : $" || product_price AS product_description
FROM products;
┌───────────────────────────────────┐
│ product_description │
├───────────────────────────────────┤
│ Dell XPS 17 : $1599.99 │
│ Blue Snowball Microphone : $99.5 │
│ System76 Thelio B1 : $1255.55 │
│ Logitech M1 : $34.99 │
│ Seagate S1 SSD : $88.75 │
│ MacBook Pro 16 : $2100.5 │
│ Rode Z28 : $275.99 │
│ Lenovo ThinkPad : $950.75 │
└───────────────────────────────────┘
-- Get the total cost of all the computers in the `products` table
SELECT SUM(product_price) AS total_price_computers
FROM products
WHERE product_category = "Computers";
┌───────────────────────┐
│ total_price_computers │
├───────────────────────┤
│ 5906.79 │
└───────────────────────┘
Python + SQL
import sqlite3
con = sqlite3.connect("products-database.db")
sql = con.cursor()
def execute_query_and_display_rows(query):
result = sql.execute(query)
rows = result.fetchall()
for row in rows:
print(row)
query = """
SELECT * FROM products;
"""
print("All products:")
execute_query_and_display_rows(query)
query = """
SELECT product_name || " : $" || product_price AS product_description
FROM products;
"""
print("\nFormatted product descriptions:")
execute_query_and_display_rows(query)
query = """
SELECT SUM(product_price) AS total_price_computers
FROM products
WHERE product_category = "Computers";
"""
print("\nThe total price of all computers in the `products` table:")
execute_query_and_display_rows(query)
Output
All products:
(1, 'Dell XPS 17', 1599.99, 'Computers')
(2, 'Blue Snowball Microphone', 99.5, 'Microphones')
(3, 'System76 Thelio B1', 1255.55, 'Computers')
(4, 'Logitech M1', 34.99, 'Accessories')
(5, 'Seagate S1 SSD', 88.75, 'Accessories')
(6, 'MacBook Pro 16', 2100.5, 'Computers')
(7, 'Rode Z28', 275.99, 'Microphones')
(8, 'Lenovo ThinkPad', 950.75, 'Computers')
Formatted product descriptions:
('Dell XPS 17 : $1599.99',)
('Blue Snowball Microphone : $99.5',)
('System76 Thelio B1 : $1255.55',)
('Logitech M1 : $34.99',)
('Seagate S1 SSD : $88.75',)
('MacBook Pro 16 : $2100.5',)
('Rode Z28 : $275.99',)
('Lenovo ThinkPad : $950.75',)
The total price of all computers in the `products` table:
(5906.79,)
Further Reading
CASE¶
The CASE
statement is similar to a chain of conditional statements in a language like Python or JavaScript. You use it to generate different values based on some condition. At CWHQ, we use the CASE
statement to generate an additional column with a range of values generated from our other columns.
Consider a shopping app where we want to rank products by their affordability. Any product that costs $100 or less is considered "Cheap", any product between $100 and $1000 is considered "Affordable", and anything else is "Expensive".
Our products
table has the following structure:
SELECT * FROM products;
┌────────────┬──────────────────────────┬───────────────┬──────────────────┐
│ product_id │ product_name │ product_price │ product_category │
├────────────┼──────────────────────────┼───────────────┼──────────────────┤
│ 1 │ Dell XPS 17 │ 1599.99 │ Computers │
│ 2 │ Blue Snowball Microphone │ 99.5 │ Microphones │
│ 3 │ System76 Thelio B1 │ 1255.55 │ Computers │
│ 4 │ Logitech M1 │ 34.99 │ Accessories │
│ 5 │ Seagate S1 SSD │ 88.75 │ Accessories │
│ 6 │ MacBook Pro 16 │ 2100.5 │ Computers │
│ 7 │ Rode Z28 │ 275.99 │ Microphones │
│ 8 │ Lenovo ThinkPad │ 950.75 │ Computers │
└────────────┴──────────────────────────┴───────────────┴──────────────────┘
Notice that there is no affordability
column? We can create one and populate it with values based on the product_price
by using the CASE
statement and using AS
to ensure the result is placed in a column called affordability
.
The THEN
keyword is what populates the values in whatever column name we created with END AS
. If you have an ELSE
clause, it doesn't need a THEN
clause.
Raw SQL
SELECT product_name, product_price,
CASE
WHEN product_price <= 100
THEN 'Cheap'
WHEN product_price > 100 AND product_price <= 1000
THEN 'Affordable'
ELSE
'Expensive'
END AS affordability
FROM products
ORDER BY product_price;
┌──────────────────────────┬───────────────┬───────────────┐
│ product_name │ product_price │ affordability │
├──────────────────────────┼───────────────┼───────────────┤
│ Logitech M1 │ 34.99 │ Cheap │
│ Seagate S1 SSD │ 88.75 │ Cheap │
│ Blue Snowball Microphone │ 99.5 │ Cheap │
│ Rode Z28 │ 275.99 │ Affordable │
│ Lenovo ThinkPad │ 950.75 │ Affordable │
│ System76 Thelio B1 │ 1255.55 │ Expensive │
│ Dell XPS 17 │ 1599.99 │ Expensive │
│ MacBook Pro 16 │ 2100.5 │ Expensive │
└──────────────────────────┴───────────────┴───────────────┘
Python + SQL
import sqlite3
con = sqlite3.connect("products.db")
sql = con.cursor()
query = """
SELECT product_name, product_price,
CASE
WHEN product_price <= 100
THEN 'Cheap'
WHEN product_price > 100 AND product_price <= 1000
THEN 'Affordable'
ELSE
'Expensive'
END AS affordability
FROM products
ORDER BY product_price;
"""
result = sql.execute(query)
rows = result.fetchall()
for row in rows:
print(row)
Output
('Logitech M1', 34.99, 'Cheap')
('Seagate S1 SSD', 88.75, 'Cheap')
('Blue Snowball Microphone', 99.5, 'Cheap')
('Rode Z28', 275.99, 'Affordable')
('Lenovo ThinkPad', 950.75, 'Affordable')
('System76 Thelio B1', 1255.55, 'Expensive')
('Dell XPS 17', 1599.99, 'Expensive')
('MacBook Pro 16', 2100.5, 'Expensive')
Further Reading
Concatenation¶
SQLite uses the ||
operator for string concatenation. This is used at CWHQ to combine column names together (possibly with other string data) to merge the data from multiple columns into a single column. This is often used in conjunction with the AS
clause to rename the combined columns.
Raw SQL
SELECT * FROM products;
┌────────────┬──────────────────────────┬───────────────┬──────────────────┐
│ product_id │ product_name │ product_price │ product_category │
├────────────┼──────────────────────────┼───────────────┼──────────────────┤
│ 1 │ Dell XPS 17 │ 1599.99 │ Computers │
│ 2 │ Blue Snowball Microphone │ 99.5 │ Microphones │
│ 3 │ System76 Thelio B1 │ 1255.55 │ Computers │
│ 4 │ Logitech M1 │ 34.99 │ Accessories │
│ 5 │ Seagate S1 SSD │ 88.75 │ Accessories │
│ 6 │ MacBook Pro 16 │ 2100.5 │ Computers │
│ 7 │ Rode Z28 │ 275.99 │ Microphones │
│ 8 │ Lenovo ThinkPad │ 950.75 │ Computers │
└────────────┴──────────────────────────┴───────────────┴──────────────────┘
/*
* Build a result set that combines `product_name` and `product_price`
* into a single column using `||` and `AS`
*/
SELECT product_name || " : $" || product_price AS product_description
FROM products;
┌───────────────────────────────────┐
│ product_description │
├───────────────────────────────────┤
│ Dell XPS 17 : $1599.99 │
│ Blue Snowball Microphone : $99.5 │
│ System76 Thelio B1 : $1255.55 │
│ Logitech M1 : $34.99 │
│ Seagate S1 SSD : $88.75 │
│ MacBook Pro 16 : $2100.5 │
│ Rode Z28 : $275.99 │
│ Lenovo ThinkPad : $950.75 │
└───────────────────────────────────┘
Python + SQL
import sqlite3
con = sqlite3.connect("products-database.db")
sql = con.cursor()
def execute_query_and_display_rows(query):
result = sql.execute(query)
rows = result.fetchall()
for row in rows:
print(row)
query = """
SELECT * FROM products;
"""
print("All products:")
execute_query_and_display_rows(query)
query = """
SELECT product_name || " : $" || product_price AS product_description
FROM products;
"""
Output
All products:
(1, 'Dell XPS 17', 1599.99, 'Computers')
(2, 'Blue Snowball Microphone', 99.5, 'Microphones')
(3, 'System76 Thelio B1', 1255.55, 'Computers')
(4, 'Logitech M1', 34.99, 'Accessories')
(5, 'Seagate S1 SSD', 88.75, 'Accessories')
(6, 'MacBook Pro 16', 2100.5, 'Computers')
(7, 'Rode Z28', 275.99, 'Microphones')
(8, 'Lenovo ThinkPad', 950.75, 'Computers')
Formatted product descriptions:
('Dell XPS 17 : $1599.99',)
('Blue Snowball Microphone : $99.5',)
('System76 Thelio B1 : $1255.55',)
('Logitech M1 : $34.99',)
('Seagate S1 SSD : $88.75',)
('MacBook Pro 16 : $2100.5',)
('Rode Z28 : $275.99',)
('Lenovo ThinkPad : $950.75',)
CREATE TABLE¶
Relational databases are made up of tables, and you'll need to create tables to hold your data if we don't provide one for you. We often use IF NOT EXISTS
in CWHQ courses when creating a table because we'll run the statement every time our Python script runs, and an error would occur if you tried to create a table that already existed. Most tables should also have a PRIMARY KEY
integer to uniquely identify each row of data.
The general format of a CREATE TABLE
statement is:
CREATE TABLE IF NOT EXISTS table_name (
column_one DATATYPE OPTIONAL_CONSTRAINTS...,
column_two DATATYPE OPTIONAL_CONSTRAINTS...,
column_three DATATYPE OPTIONAL_CONSTRAINTS...
-- etc...
);
Note that each column definition is separated by a comma (,
) but the final column definition should not have a comma.
Here's an example of a CREATE TABLE
statement for the users
table from the What Is A Relational Database? section earlier in these docs:
Raw SQL
CREATE TABLE IF NOT EXISTS users (
user_id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT UNIQUE NOT NULL,
password TEXT NOT NULL
);
Python + SQL
import sqlite3
con = sqlite3.connect("users-database.db")
sql = con.cursor()
query = """
CREATE TABLE IF NOT EXISTS users (
user_id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT UNIQUE NOT NULL,
password TEXT NOT NULL
);
"""
sql.execute(query)
Column Datatypes¶
When writing column definitions, your column can be one of 5 storage classes (which are a generic datatype) in SQLite:
NULL
: Represents "nothingness"INTEGER
: Whole numbersREAL
: Decimal numbersTEXT
: Text dataBLOB
: Binary data (like images, music, etc.)
You'll mainly use INTEGER
and TEXT
for CWHQ projects.
Column Constraints¶
Besides the datatype, you can also put additional constraints on a column definition to enforce that a column is UNIQUE
, or NOT NULL
, or even a PRIMARY KEY
. You can see all of those at work in this example:
CREATE TABLE IF NOT EXISTS users (
user_id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT UNIQUE NOT NULL,
password TEXT NOT NULL
);
Further Reading
DELETE¶
To remove data in a SQL table, use the DELETE
statement. Make sure to use a WHERE
clause so that you only delete the rows you intend to.
Raw SQL
SELECT * FROM users;
┌─────────┬──────────┬────────────┐
│ user_id │ username │ password │
├─────────┼──────────┼────────────┤
│ 1 │ djs │ mypa$$word │
│ 2 │ django │ w0ff │
│ 3 │ alecg │ c0de │
└─────────┴──────────┴────────────┘
DELETE FROM users WHERE user_id = 3;
SELECT * FROM users;
┌─────────┬──────────┬────────────┐
│ user_id │ username │ password │
├─────────┼──────────┼────────────┤
│ 1 │ djs │ mypa$$word │
│ 2 │ django │ w0ff │
└─────────┴──────────┴────────────┘
Python + SQL
import sqlite3
con = sqlite3.connect("users-database.db")
sql = con.cursor()
# We'll use this twice, so it makes sense to be a function
def display_all_users():
query = """
SELECT * FROM users;
"""
result = sql.execute(query)
rows = result.fetchall()
print(rows)
display_all_users()
query = """
DELETE FROM users WHERE id = 3;
"""
sql.execute(query)
# Make sure to commit the changes to the DB
con.commit()
display_all_users()
Output
[(1, 'djs', 'mypa$$word'), (2, 'django', 'w0ff'), (3, 'alecg', 'c0de')]
[(1, 'djs', 'mypa$$word'), (2, 'django', 'w0ff')]
Deleting all rows from a table¶
If you leave out the WHERE
clause in a DELETE
statement, you remove all rows from the table. This is a handy way to clear out all the rows if you need to start with a fresh table:
Raw SQL
SELECT * FROM users;
┌─────────┬──────────┬────────────┐
│ user_id │ username │ password │
├─────────┼──────────┼────────────┤
│ 1 │ djs │ mypa$$word │
│ 2 │ django │ w0ff │
│ 3 │ alecg │ c0de │
└─────────┴──────────┴────────────┘
DELETE FROM users;
SELECT * FROM users;
-- nothing returned because the table is empty
Python + SQL
import sqlite3
con = sqlite3.connect("users-database.db")
sql = con.cursor()
# We'll use this twice, so it makes sense to be a function
def display_all_users():
query = """
SELECT * FROM users;
"""
result = sql.execute(query)
rows = result.fetchall()
print(rows)
display_all_users()
query = """
DELETE FROM users;
"""
sql.execute(query)
# Make sure to commit the changes to the DB
con.commit()
display_all_users()
Output
[(1, 'djs', 'mypa$$word'), (2, 'django', 'w0ff'), (3, 'alecg', 'c0de')]
[]
Further Reading
DISTINCT¶
If you want to get unique column values for a set of rows, use the DISTINCT
clause of a SELECT
query
For example, in the products
table below, it may be hard at a glance to see what categories are present, but with DISTINCT
it's easy to see we only have three!
Raw SQL
SELECT * FROM products;
┌────────────┬──────────────────────────┬───────────────┬──────────────────┐
│ product_id │ product_name │ product_price │ product_category │
├────────────┼──────────────────────────┼───────────────┼──────────────────┤
│ 1 │ Dell XPS 17 │ 1599.99 │ Computers │
│ 2 │ Blue Snowball Microphone │ 99.5 │ Microphones │
│ 3 │ System76 Thelio B1 │ 1255.55 │ Computers │
│ 4 │ Logitech M1 │ 34.99 │ Accessories │
│ 5 │ Seagate S1 SSD │ 88.75 │ Accessories │
│ 6 │ MacBook Pro 16 │ 2100.5 │ Computers │
│ 7 │ Rode Z28 │ 275.99 │ Microphones │
│ 8 │ Lenovo ThinkPad │ 950.75 │ Computers │
└────────────┴──────────────────────────┴───────────────┴──────────────────┘
-- Get only the unique categories for our products.
SELECT DISTINCT product_category FROM products;
┌──────────────────┐
│ product_category │
├──────────────────┤
│ Computers │
│ Microphones │
│ Accessories │
└──────────────────┘
Python + SQL
import sqlite3
con = sqlite3.connect("products-database.db")
sql = con.cursor()
query = """
SELECT * FROM products;
"""
result = sql.execute(query)
rows = result.fetchall()
# Easier to read if we loop then print each row since there are 8 rows.
for row in rows:
print(row)
query = """
SELECT DISTINCT product_category FROM products;
"""
result = sql.execute(query)
rows = result.fetchall()
print("\nDistinct categories:")
print(rows)
Output
(1, 'Dell XPS 17', 1599.99, 'Computers')
(2, 'Blue Snowball Microphone', 99.5, 'Microphones')
(3, 'System76 Thelio B1', 1255.55, 'Computers')
(4, 'Logitech M1', 34.99, 'Accessories')
(5, 'Seagate S1 SSD', 88.75, 'Accessories')
(6, 'MacBook Pro 16', 2100.5, 'Computers')
(7, 'Rode Z28', 275.99, 'Microphones')
(8, 'Lenovo ThinkPad', 950.75, 'Computers')
Distinct categories:
[('Computers',), ('Microphones',), ('Accessories',)]
Further Reading
DROP TABLE¶
The DROP TABLE
query deletes an entire table and it's definition from the database. You should usually use the IF EXISTS
clause with this query to ensure an error isn't thrown if the table you're trying to drop doesn't exist.
Raw SQL
SELECT * FROM users;
┌─────────┬──────────┬────────────┐
│ user_id │ username │ password │
├─────────┼──────────┼────────────┤
│ 1 │ djs │ mypa$$word │
│ 2 │ django │ w0ff │
│ 3 │ alecg │ c0de │
└─────────┴──────────┴────────────┘
DROP TABLE IF EXISTS users;
SELECT * FROM users;
-- Error: no such table: users
Python + SQL
import sqlite3
con = sqlite3.connect("users-database.db")
sql = con.cursor()
# We'll use this twice, so it makes sense to be a function
def display_all_users():
query = """
SELECT * FROM users;
"""
result = sql.execute(query)
rows = result.fetchall()
print(rows)
display_all_users()
query = """
DROP TABLE IF EXISTS users;
"""
sql.execute(query)
# This will throw an error since the `users` table doesn't exist.
display_all_users()
Output
[(1, 'djs', 'mypa$$word'), (2, 'django', 'w0ff'), (3, 'alecg', 'c0de')]
Traceback (most recent call last):
File "/home/daniel/documentation-examples/main.py", line 27, in <module>
display_all_users()
File "/home/daniel/documentation-examples/main.py", line 12, in display_all_users
result = sql.execute(query)
sqlite3.OperationalError: no such table: users
Further Reading
GROUP BY¶
If you need to "flatten" the results of a SELECT
query, the GROUP BY
clause is helpful. It let's you group the resulting rows by a particular column, effectively filtering out duplicate rows that have the same column value. You almost always use GROUP BY
with an Aggregate Function to perform some sort of calculation on a group of rows with similar column values, but you can also use it to filter a column by unique values (as we do to get the unique product categories below):
Raw SQL
SELECT * FROM products;
┌────────────┬──────────────────────────┬───────────────┬──────────────────┐
│ product_id │ product_name │ product_price │ product_category │
├────────────┼──────────────────────────┼───────────────┼──────────────────┤
│ 1 │ Dell XPS 17 │ 1599.99 │ Computers │
│ 2 │ Blue Snowball Microphone │ 99.5 │ Microphones │
│ 3 │ System76 Thelio B1 │ 1255.55 │ Computers │
│ 4 │ Logitech M1 │ 34.99 │ Accessories │
│ 5 │ Seagate S1 SSD │ 88.75 │ Accessories │
│ 6 │ MacBook Pro 16 │ 2100.5 │ Computers │
│ 7 │ Rode Z28 │ 275.99 │ Microphones │
│ 8 │ Lenovo ThinkPad │ 950.75 │ Computers │
└────────────┴──────────────────────────┴───────────────┴──────────────────┘
-- Get the unique product categories
SELECT product_category FROM products GROUP BY product_category;
┌──────────────────┐
│ product_category │
├──────────────────┤
│ Accessories │
│ Computers │
│ Microphones │
└──────────────────┘
-- Get the number of products in each `product_category`
SELECT product_category, COUNT(*) AS num_products_per_category
FROM products
GROUP BY product_category;
┌──────────────────┬───────────────────────────┐
│ product_category │ num_products_per_category │
├──────────────────┼───────────────────────────┤
│ Accessories │ 2 │
│ Computers │ 4 │
│ Microphones │ 2 │
└──────────────────┴───────────────────────────┘
Python + SQL
import sqlite3
con = sqlite3.connect("products-database.db")
sql = con.cursor()
def execute_query_and_display_rows(query):
result = sql.execute(query)
rows = result.fetchall()
for row in rows:
print(row)
query = """
SELECT * FROM products;
"""
print("All products:")
execute_query_and_display_rows(query)
query = """
SELECT product_category FROM products GROUP BY product_category;
"""
print("\nUnique `product_categories`:")
execute_query_and_display_rows(query)
query = """
SELECT product_category, COUNT(*) AS num_products_per_category
FROM products
GROUP BY product_category;
"""
print("\nNumber of products per category:")
execute_query_and_display_rows(query)
Output
All products:
(1, 'Dell XPS 17', 1599.99, 'Computers')
(2, 'Blue Snowball Microphone', 99.5, 'Microphones')
(3, 'System76 Thelio B1', 1255.55, 'Computers')
(4, 'Logitech M1', 34.99, 'Accessories')
(5, 'Seagate S1 SSD', 88.75, 'Accessories')
(6, 'MacBook Pro 16', 2100.5, 'Computers')
(7, 'Rode Z28', 275.99, 'Microphones')
(8, 'Lenovo ThinkPad', 950.75, 'Computers')
Unique `product_categories`:
('Accessories',)
('Computers',)
('Microphones',)
Number of products per category:
('Accessories', 2)
('Computers', 4)
('Microphones', 2)
Further Reading
INSERT¶
Once you've created a table, you'll want to put data in it. The INSERT
statement is used to add data to a SQL table. You list the column names in the ()
after the table name.
Consider this users
table definition:
CREATE TABLE IF NOT EXISTS users (
user_id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT UNIQUE NOT NULL,
password TEXT NOT NULL
);
If we wanted to fill this table with a few users, we would leave out the user_id
in the ()
as this table has the user_id
set to AUTOINCREMENT
. We would need to add the other two column names in whatever order we wished, though.
Raw SQL
INSERT INTO users (username, password) VALUES ("djs", "mypa$$word");
INSERT INTO users (username, password) VALUES ("django", "w0ff");
INSERT INTO users (username, password) VALUES ("alecg", "c0de");
SELECT * FROM users;
┌─────────┬──────────┬────────────┐
│ user_id │ username │ password │
├─────────┼──────────┼────────────┤
│ 1 │ djs │ mypa$$word │
│ 2 │ django │ w0ff │
│ 3 │ alecg │ c0de │
└─────────┴──────────┴────────────┘
Python + SQL
import sqlite3
con = sqlite3.connect("users-database.db")
sql = con.cursor()
query = """
INSERT INTO users (username, password) VALUES ("djs", "mypa$$word");
"""
sql.execute(query)
query = """
INSERT INTO users (username, password) VALUES ("django", "w0ff");
"""
sql.execute(query)
query = """
INSERT INTO users (username, password) VALUES ("alecg", "c0de");
"""
sql.execute(query)
# Make sure the changes are saved to the DB.
con.commit()
query = """
SELECT * FROM users;
"""
result = sql.execute(query)
rows = result.fetchall()
print(rows)
Output
[(1, 'djs', 'mypa$$word'), (2, 'django', 'w0ff'), (3, 'alecg', 'c0de')]
Inserting multiple rows into a table¶
If you want to insert multiple rows into a database table, you can use a single INSERT
statement and group each row in ()
.
Raw SQL
INSERT INTO users (username, password)
VALUES
("djs", "mypa$$word"),
("django", "w0ff"),
("alecg", "c0de");
SELECT * FROM users;
┌─────────┬──────────┬────────────┐
│ user_id │ username │ password │
├─────────┼──────────┼────────────┤
│ 1 │ djs │ mypa$$word │
│ 2 │ django │ w0ff │
│ 3 │ alecg │ c0de │
└─────────┴──────────┴────────────┘
Python + SQL
import sqlite3
con = sqlite3.connect("users-database.db")
sql = con.cursor()
query = """
INSERT INTO users (username, password)
VALUES
("djs", "mypa$$word"),
("django", "w0ff"),
("alecg", "c0de");
"""
sql.execute(query)
con.commit()
query = """
SELECT * FROM users;
"""
result = sql.execute(query)
rows = result.fetchall()
print(rows)
Output
[(1, 'djs', 'mypa$$word'), (2, 'django', 'w0ff'), (3, 'alecg', 'c0de')]
Further Reading
JOIN¶
Being able to combine data from related tables is one of the things that makes a relational database like SQLite so powerful.
Defining Table Relationships¶
Before you can JOIN
two tables, they must share a common key. Consider the following two table schemas, which represent data about authors and books:
CREATE TABLE authors (
author_id INTEGER PRIMARY KEY AUTOINCREMENT,
author_name TEXT NOT NULL
);
CREATE TABLE books (
book_id INTEGER PRIMARY KEY AUTOINCREMENT,
author_id INTEGER NOT NULL REFERENCES authors (author_id), -- the Foreign Key relationship
book_title TEXT UNIQUE NOT NULL
);
We can relate the two tables with the REFERENCES
keyword. The author_id
is known as a Foreign Key in the books
table because it's merely pointing to the PRIMARY KEY
of the authors
table. It's the author_id
shared between the authors
and books
tables that allows us to JOIN
them together.
JOINING Tables Together¶
Now that we have an established relationship between our authors
and books
tables, we can JOIN
them together. We'll preface each column name with the table it references since we'll be referring to multiple tables like this table_name.column_name
. This isn't always strictly required, but it makes it easier to understand which table each column references in the query.
Raw SQL
SELECT * FROM authors;
┌───────────┬───────────────┐
│ author_id │ author_name │
├───────────┼───────────────┤
│ 1 │ J.D. Salinger │
│ 2 │ Harper Lee │
│ 3 │ Truman Capote │
└───────────┴───────────────┘
SELECT * FROM books;
┌─────────┬───────────┬────────────────────────┐
│ book_id │ author_id │ book_title │
├─────────┼───────────┼────────────────────────┤
│ 1 │ 2 │ To Kill a Mockingbird │
│ 2 │ 3 │ In Cold Blood │
│ 3 │ 1 │ The Catcher in the Rye │
│ 4 │ 3 │ Breakfast at Tiffanys │
│ 5 │ 1 │ Franny and Zooey │
│ 6 │ 3 │ Summer Crossing │
│ 7 │ 2 │ Go Set a Watchman │
└─────────┴───────────┴────────────────────────┘
-- Join the `authors` and `books` tables together
SELECT authors.author_name, books.book_title
FROM authors
JOIN books ON authors.author_id = books.author_id;
┌───────────────┬────────────────────────┐
│ author_name │ book_title │
├───────────────┼────────────────────────┤
│ Harper Lee │ To Kill a Mockingbird │
│ Truman Capote │ In Cold Blood │
│ J.D. Salinger │ The Catcher in the Rye │
│ Truman Capote │ Breakfast at Tiffanys │
│ J.D. Salinger │ Franny and Zooey │
│ Truman Capote │ Summer Crossing │
│ Harper Lee │ Go Set a Watchman │
└───────────────┴────────────────────────┘
-- Getting a bit fancier by ordering by `author_name`
SELECT authors.author_name, books.book_title
FROM authors
JOIN books ON authors.author_id = books.author_id
ORDER BY authors.author_name;
┌───────────────┬────────────────────────┐
│ author_name │ book_title │
├───────────────┼────────────────────────┤
│ Harper Lee │ To Kill a Mockingbird │
│ Harper Lee │ Go Set a Watchman │
│ J.D. Salinger │ The Catcher in the Rye │
│ J.D. Salinger │ Franny and Zooey │
│ Truman Capote │ In Cold Blood │
│ Truman Capote │ Breakfast at Tiffanys │
│ Truman Capote │ Summer Crossing │
└───────────────┴────────────────────────┘
Python + SQL
import sqlite3
con = sqlite3.connect("library-database.db")
sql = con.cursor()
def execute_query_and_display_rows(query):
result = sql.execute(query)
rows = result.fetchall()
for row in rows:
print(row)
query = """
SELECT * FROM authors;
"""
print("All rows in the `authors` table:")
execute_query_and_display_rows(query)
query = """
SELECT * FROM books;
"""
print("\nAll rows in the `books` table:")
execute_query_and_display_rows(query)
query = """
SELECT authors.author_name, books.book_title
FROM authors
JOIN books ON authors.author_id = books.author_id;
"""
print("\nJoining the `authors` and `books` tables:")
execute_query_and_display_rows(query)
query = """
SELECT authors.author_name, books.book_title
FROM authors
JOIN books ON authors.author_id = books.author_id
ORDER BY authors.author_name;
"""
print("\nJoining and sorting the `authors` and `books` tables:")
execute_query_and_display_rows(query)
Output
All rows in the `authors` table:
(1, 'J.D. Salinger')
(2, 'Harper Lee')
(3, 'Truman Capote')
All rows in the `books` table:
(1, 2, 'To Kill a Mockingbird')
(2, 3, 'In Cold Blood')
(3, 1, 'The Catcher in the Rye')
(4, 3, "Breakfast at Tiffany's")
(5, 1, 'Franny and Zooey')
(6, 3, 'Summer Crossing')
(7, 2, 'Go Set a Watchman')
Joining the `authors` and `books` tables:
('Harper Lee', 'To Kill a Mockingbird')
('Truman Capote', 'In Cold Blood')
('J.D. Salinger', 'The Catcher in the Rye')
('Truman Capote', "Breakfast at Tiffany's")
('J.D. Salinger', 'Franny and Zooey')
('Truman Capote', 'Summer Crossing')
('Harper Lee', 'Go Set a Watchman')
Joining and sorting the `authors` and `books` tables:
('Harper Lee', 'To Kill a Mockingbird')
('Harper Lee', 'Go Set a Watchman')
('J.D. Salinger', 'The Catcher in the Rye')
('J.D. Salinger', 'Franny and Zooey')
('Truman Capote', 'In Cold Blood')
('Truman Capote', "Breakfast at Tiffany's")
('Truman Capote', 'Summer Crossing')
The USING()
shorthand¶
When using a JOIN
, you don't have to use the ON table.column_name = other_table.column_name
syntax if the column names are the same in both tables. We could rewrite the last JOIN
from the previous example in a shorter way with USING()
like so:
SELECT authors.author_name, books.book_title
FROM authors
JOIN books USING (author_id) -- This is a nice shorthand
ORDER BY authors.author_name;
┌───────────────┬────────────────────────┐
│ author_name │ book_title │
├───────────────┼────────────────────────┤
│ Harper Lee │ To Kill a Mockingbird │
│ Harper Lee │ Go Set a Watchman │
│ J.D. Salinger │ The Catcher in the Rye │
│ J.D. Salinger │ Franny and Zooey │
│ Truman Capote │ In Cold Blood │
│ Truman Capote │ Breakfast at Tiffanys │
│ Truman Capote │ Summer Crossing │
└───────────────┴────────────────────────┘
Further Reading
LIMIT¶
Sometimes, you may want to get a limited number of rows back from a SELECT
query. The LIMIT
clause allows you to do this:
Raw SQL
SELECT * FROM products;
┌────────────┬──────────────────────────┬───────────────┬──────────────────┐
│ product_id │ product_name │ product_price │ product_category │
├────────────┼──────────────────────────┼───────────────┼──────────────────┤
│ 1 │ Dell XPS 17 │ 1599.99 │ Computers │
│ 2 │ Blue Snowball Microphone │ 99.5 │ Microphones │
│ 3 │ System76 Thelio B1 │ 1255.55 │ Computers │
│ 4 │ Logitech M1 │ 34.99 │ Accessories │
│ 5 │ Seagate S1 SSD │ 88.75 │ Accessories │
│ 6 │ MacBook Pro 16 │ 2100.5 │ Computers │
│ 7 │ Rode Z28 │ 275.99 │ Microphones │
│ 8 │ Lenovo ThinkPad │ 950.75 │ Computers │
└────────────┴──────────────────────────┴───────────────┴──────────────────┘
-- Only get the first 3 products in the table (by `product_id`)
SELECT * FROM products LIMIT 3;
┌────────────┬──────────────────────────┬───────────────┬──────────────────┐
│ product_id │ product_name │ product_price │ product_category │
├────────────┼──────────────────────────┼───────────────┼──────────────────┤
│ 1 │ Dell XPS 17 │ 1599.99 │ Computers │
│ 2 │ Blue Snowball Microphone │ 99.5 │ Microphones │
│ 3 │ System76 Thelio B1 │ 1255.55 │ Computers │
└────────────┴──────────────────────────┴───────────────┴──────────────────┘
Python + SQL
import sqlite3
con = sqlite3.connect("products-database.db")
sql = con.cursor()
# We'll use this a few times so it makes sense for it to be a function
def fetch_and_display_rows(query):
result = sql.execute(query)
rows = result.fetchall()
for row in rows:
print(row)
query = """
SELECT * FROM products;
"""
fetch_and_display_rows(query)
query = """
SELECT * FROM products LIMIT 3;
"""
print("\nThe first three products in the table:")
fetch_and_display_rows(query)
Output
(1, 'Dell XPS 17', 1599.99, 'Computers')
(2, 'Blue Snowball Microphone', 99.5, 'Microphones')
(3, 'System76 Thelio B1', 1255.55, 'Computers')
(4, 'Logitech M1', 34.99, 'Accessories')
(5, 'Seagate S1 SSD', 88.75, 'Accessories')
(6, 'MacBook Pro 16', 2100.5, 'Computers')
(7, 'Rode Z28', 275.99, 'Microphones')
(8, 'Lenovo ThinkPad', 950.75, 'Computers')
The first three products in the table:
(1, 'Dell XPS 17', 1599.99, 'Computers')
(2, 'Blue Snowball Microphone', 99.5, 'Microphones')
(3, 'System76 Thelio B1', 1255.55, 'Computers')
Further Reading
NULL¶
The NULL
datatype allows you to express a missing or unknown value.
Avoiding NULL Values In CREATE TABLE Statements¶
Generally, NULL
values should be avoided by adding a NOT NULL
constraint to your CREATE TABLE
definitions like so:
CREATE TABLE products (
product_id INTEGER PRIMARY KEY AUTOINCREMENT,
product_name TEXT UNIQUE NOT NULL,
product_price REAL NOT NULL,
product_category TEXT NOT NULL
);
If a column has a NOT NULL
constraint and you try to enter a NULL
value for that column, you'll get an error:
Raw SQL
SELECT * FROM products;
┌────────────┬──────────────────────────┬───────────────┬──────────────────┐
│ product_id │ product_name │ product_price │ product_category │
├────────────┼──────────────────────────┼───────────────┼──────────────────┤
│ 1 │ Dell XPS 17 │ 1599.99 │ Computers │
│ 2 │ Blue Snowball Microphone │ 99.5 │ Microphones │
│ 3 │ System76 Thelio B1 │ 1255.55 │ Computers │
│ 4 │ Logitech M1 │ 34.99 │ Accessories │
│ 5 │ Seagate S1 SSD │ 88.75 │ Accessories │
│ 6 │ MacBook Pro 16 │ 2100.5 │ Computers │
│ 7 │ Rode Z28 │ 275.99 │ Microphones │
│ 8 │ Lenovo ThinkPad │ 950.75 │ Computers │
└────────────┴──────────────────────────┴───────────────┴──────────────────┘
INSERT INTO products (product_name, product_price, product_category)
VALUES ("mousepad", NULL, "Accessories");
-- Error: NOT NULL constraint failed: products.product_price
Python + SQL
import sqlite3
con = sqlite3.connect("products-database.db")
sql = con.cursor()
query = """
SELECT * FROM products;
"""
print("All products:")
result = sql.execute(query)
rows = result.fetchall()
for row in rows:
print(row)
query = """
INSERT INTO products (product_name, product_price, product_category)
VALUES ("mousepad", NULL, "Accessories");
"""
# This will throw an error
sql.execute(query)
con.commit()
Output
All products:
(1, 'Dell XPS 17', 1599.99, 'Computers')
(2, 'Blue Snowball Microphone', 99.5, 'Microphones')
(3, 'System76 Thelio B1', 1255.55, 'Computers')
(4, 'Logitech M1', 34.99, 'Accessories')
(5, 'Seagate S1 SSD', 88.75, 'Accessories')
(6, 'MacBook Pro 16', 2100.5, 'Computers')
(7, 'Rode Z28', 275.99, 'Microphones')
(8, 'Lenovo ThinkPad', 950.75, 'Computers')
Traceback (most recent call last):
File "/home/daniel/documentation-examples/main.py", line 24, in <module>
sql.execute(query)
sqlite3.IntegrityError: NOT NULL constraint failed: products.product_price
Filtering NULL Values In SELECT Statements¶
You can use IS NULL
and IS NOT NULL
to filter SELECT
statements by columns with or without NULL
values. This can be valuable to find rows with missing information or to only display rows with no missing information.
Note that in Python, NULL
translates to the None
datatype.
Raw SQL
/*
* We can see that the "Lenovo ThinkPad" doesn't have a `product_price`.
* That's because it's `NULL`!
*/
SELECT * FROM products;
┌────────────┬──────────────────────────┬───────────────┬──────────────────┐
│ product_id │ product_name │ product_price │ product_category │
├────────────┼──────────────────────────┼───────────────┼──────────────────┤
│ 1 │ Dell XPS 17 │ 1599.99 │ Computers │
│ 2 │ Blue Snowball Microphone │ 99.5 │ Microphones │
│ 3 │ System76 Thelio B1 │ 1255.55 │ Computers │
│ 4 │ Logitech M1 │ 34.99 │ Accessories │
│ 5 │ Seagate S1 SSD │ 88.75 │ Accessories │
│ 6 │ MacBook Pro 16 │ 2100.5 │ Computers │
│ 7 │ Rode Z28 │ 275.99 │ Microphones │
│ 8 │ Lenovo ThinkPad │ │ Computers │
└────────────┴──────────────────────────┴───────────────┴──────────────────┘
/*
* We can filter our results to find products with missing
* `product_price` data.
*/
SELECT * FROM products WHERE product_price IS NULL;
┌────────────┬─────────────────┬───────────────┬──────────────────┐
│ product_id │ product_name │ product_price │ product_category │
├────────────┼─────────────────┼───────────────┼──────────────────┤
│ 8 │ Lenovo ThinkPad │ │ Computers │
└────────────┴─────────────────┴───────────────┴──────────────────┘
/*
* We can also filter out rows that have a `NULL` value for
* `product_price`.
*/
SELECT * FROM products WHERE product_price IS NOT NULL;
┌────────────┬──────────────────────────┬───────────────┬──────────────────┐
│ product_id │ product_name │ product_price │ product_category │
├────────────┼──────────────────────────┼───────────────┼──────────────────┤
│ 1 │ Dell XPS 17 │ 1599.99 │ Computers │
│ 2 │ Blue Snowball Microphone │ 99.5 │ Microphones │
│ 3 │ System76 Thelio B1 │ 1255.55 │ Computers │
│ 4 │ Logitech M1 │ 34.99 │ Accessories │
│ 5 │ Seagate S1 SSD │ 88.75 │ Accessories │
│ 6 │ MacBook Pro 16 │ 2100.5 │ Computers │
│ 7 │ Rode Z28 │ 275.99 │ Microphones │
└────────────┴──────────────────────────┴───────────────┴──────────────────┘
Python + SQL
import sqlite3
con = sqlite3.connect("products-database.db")
sql = con.cursor()
def execute_query_and_display_rows(query):
result = sql.execute(query)
rows = result.fetchall()
for row in rows:
print(row)
query = """
SELECT * FROM products;
"""
print("All products:")
execute_query_and_display_rows(query)
query = """
SELECT * FROM products WHERE product_price IS NULL;
"""
print("\nProducts with `NULL` values for their price:")
execute_query_and_display_rows(query)
query = """
SELECT * FROM products WHERE product_price IS NOT NULL;
"""
print("\nProducts __without__ `NULL` values for their price:")
execute_query_and_display_rows(query)
Output
All products:
(1, 'Dell XPS 17', 1599.99, 'Computers')
(2, 'Blue Snowball Microphone', 99.5, 'Microphones')
(3, 'System76 Thelio B1', 1255.55, 'Computers')
(4, 'Logitech M1', 34.99, 'Accessories')
(5, 'Seagate S1 SSD', 88.75, 'Accessories')
(6, 'MacBook Pro 16', 2100.5, 'Computers')
(7, 'Rode Z28', 275.99, 'Microphones')
(8, 'Lenovo ThinkPad', None, 'Computers')
Products with `NULL` values for their price:
(8, 'Lenovo ThinkPad', None, 'Computers')
Products __without__ `NULL` values for their price:
(1, 'Dell XPS 17', 1599.99, 'Computers')
(2, 'Blue Snowball Microphone', 99.5, 'Microphones')
(3, 'System76 Thelio B1', 1255.55, 'Computers')
(4, 'Logitech M1', 34.99, 'Accessories')
(5, 'Seagate S1 SSD', 88.75, 'Accessories')
(6, 'MacBook Pro 16', 2100.5, 'Computers')
(7, 'Rode Z28', 275.99, 'Microphones')
Further Reading
OFFSET¶
If you've ever visited a website like Amazon.com, you know that when you search for a particular product, there are multiple pages of results. The OFFSET
clause allows you to move the starting point of the returned rows from a query. It's usually used in conjunction with a LIMIT
clause for things like pagination (as in the Amazon example).
Raw SQL
SELECT * FROM products;
┌────────────┬──────────────────────────┬───────────────┬──────────────────┐
│ product_id │ product_name │ product_price │ product_category │
├────────────┼──────────────────────────┼───────────────┼──────────────────┤
│ 1 │ Dell XPS 17 │ 1599.99 │ Computers │
│ 2 │ Blue Snowball Microphone │ 99.5 │ Microphones │
│ 3 │ System76 Thelio B1 │ 1255.55 │ Computers │
│ 4 │ Logitech M1 │ 34.99 │ Accessories │
│ 5 │ Seagate S1 SSD │ 88.75 │ Accessories │
│ 6 │ MacBook Pro 16 │ 2100.5 │ Computers │
│ 7 │ Rode Z28 │ 275.99 │ Microphones │
│ 8 │ Lenovo ThinkPad │ 950.75 │ Computers │
└────────────┴──────────────────────────┴───────────────┴──────────────────┘
-- Only get the first 3 products in the table (by `product_id`)
SELECT * FROM products LIMIT 3;
┌────────────┬──────────────────────────┬───────────────┬──────────────────┐
│ product_id │ product_name │ product_price │ product_category │
├────────────┼──────────────────────────┼───────────────┼──────────────────┤
│ 1 │ Dell XPS 17 │ 1599.99 │ Computers │
│ 2 │ Blue Snowball Microphone │ 99.5 │ Microphones │
│ 3 │ System76 Thelio B1 │ 1255.55 │ Computers │
└────────────┴──────────────────────────┴───────────────┴──────────────────┘
-- Get the next 3 products in the table
SELECT * FROM products LIMIT 3 OFFSET 3;
┌────────────┬────────────────┬───────────────┬──────────────────┐
│ product_id │ product_name │ product_price │ product_category │
├────────────┼────────────────┼───────────────┼──────────────────┤
│ 4 │ Logitech M1 │ 34.99 │ Accessories │
│ 5 │ Seagate S1 SSD │ 88.75 │ Accessories │
│ 6 │ MacBook Pro 16 │ 2100.5 │ Computers │
└────────────┴────────────────┴───────────────┴──────────────────┘
Python + SQL
import sqlite3
con = sqlite3.connect("products-database.db")
sql = con.cursor()
# We'll use this a few times so it makes sense for it to be a function
def fetch_and_display_rows(query):
result = sql.execute(query)
rows = result.fetchall()
for row in rows:
print(row)
query = """
SELECT * FROM products;
"""
fetch_and_display_rows(query)
query = """
SELECT * FROM products LIMIT 3;
"""
print("\nThe first three products in the table:")
fetch_and_display_rows(query)
query = """
SELECT * FROM products LIMIT 3 OFFSET 3;
"""
print("\nThe second group of three products in the table:")
fetch_and_display_rows(query)
Output
(1, 'Dell XPS 17', 1599.99, 'Computers')
(2, 'Blue Snowball Microphone', 99.5, 'Microphones')
(3, 'System76 Thelio B1', 1255.55, 'Computers')
(4, 'Logitech M1', 34.99, 'Accessories')
(5, 'Seagate S1 SSD', 88.75, 'Accessories')
(6, 'MacBook Pro 16', 2100.5, 'Computers')
(7, 'Rode Z28', 275.99, 'Microphones')
(8, 'Lenovo ThinkPad', 950.75, 'Computers')
The first three products in the table:
(1, 'Dell XPS 17', 1599.99, 'Computers')
(2, 'Blue Snowball Microphone', 99.5, 'Microphones')
(3, 'System76 Thelio B1', 1255.55, 'Computers')
The second group of three products in the table:
(4, 'Logitech M1', 34.99, 'Accessories')
(5, 'Seagate S1 SSD', 88.75, 'Accessories')
(6, 'MacBook Pro 16', 2100.5, 'Computers')
Further Reading
ORDER BY¶
The ORDER BY
clause allows you to order rows in ascending (ASC
) or descending (DESC
) order alphanumerically. You use it with a SELECT
query to order the output.
Raw SQL
SELECT * FROM products;
┌────────────┬──────────────────────────┬───────────────┬──────────────────┐
│ product_id │ product_name │ product_price │ product_category │
├────────────┼──────────────────────────┼───────────────┼──────────────────┤
│ 1 │ Dell XPS 17 │ 1599.99 │ Computers │
│ 2 │ Blue Snowball Microphone │ 99.5 │ Microphones │
│ 3 │ System76 Thelio B1 │ 1255.55 │ Computers │
│ 4 │ Logitech M1 │ 34.99 │ Accessories │
│ 5 │ Seagate S1 SSD │ 88.75 │ Accessories │
│ 6 │ MacBook Pro 16 │ 2100.5 │ Computers │
│ 7 │ Rode Z28 │ 275.99 │ Microphones │
│ 8 │ Lenovo ThinkPad │ 950.75 │ Computers │
└────────────┴──────────────────────────┴───────────────┴──────────────────┘
-- Ordering products from lowest price to highest price
SELECT * FROM products ORDER BY product_price;
┌────────────┬──────────────────────────┬───────────────┬──────────────────┐
│ product_id │ product_name │ product_price │ product_category │
├────────────┼──────────────────────────┼───────────────┼──────────────────┤
│ 4 │ Logitech M1 │ 34.99 │ Accessories │
│ 5 │ Seagate S1 SSD │ 88.75 │ Accessories │
│ 2 │ Blue Snowball Microphone │ 99.5 │ Microphones │
│ 7 │ Rode Z28 │ 275.99 │ Microphones │
│ 8 │ Lenovo ThinkPad │ 950.75 │ Computers │
│ 3 │ System76 Thelio B1 │ 1255.55 │ Computers │
│ 1 │ Dell XPS 17 │ 1599.99 │ Computers │
│ 6 │ MacBook Pro 16 │ 2100.5 │ Computers │
└────────────┴──────────────────────────┴───────────────┴──────────────────┘
-- ASC is the default, so it's the the same as doing nothing after ORDER BY
SELECT * FROM products ORDER BY product_price ASC;
┌────────────┬──────────────────────────┬───────────────┬──────────────────┐
│ product_id │ product_name │ product_price │ product_category │
├────────────┼──────────────────────────┼───────────────┼──────────────────┤
│ 4 │ Logitech M1 │ 34.99 │ Accessories │
│ 5 │ Seagate S1 SSD │ 88.75 │ Accessories │
│ 2 │ Blue Snowball Microphone │ 99.5 │ Microphones │
│ 7 │ Rode Z28 │ 275.99 │ Microphones │
│ 8 │ Lenovo ThinkPad │ 950.75 │ Computers │
│ 3 │ System76 Thelio B1 │ 1255.55 │ Computers │
│ 1 │ Dell XPS 17 │ 1599.99 │ Computers │
│ 6 │ MacBook Pro 16 │ 2100.5 │ Computers │
└────────────┴──────────────────────────┴───────────────┴──────────────────┘
-- Ordering products from highest price to lowest price
SELECT * FROM products ORDER BY product_price DESC;
┌────────────┬──────────────────────────┬───────────────┬──────────────────┐
│ product_id │ product_name │ product_price │ product_category │
├────────────┼──────────────────────────┼───────────────┼──────────────────┤
│ 6 │ MacBook Pro 16 │ 2100.5 │ Computers │
│ 1 │ Dell XPS 17 │ 1599.99 │ Computers │
│ 3 │ System76 Thelio B1 │ 1255.55 │ Computers │
│ 8 │ Lenovo ThinkPad │ 950.75 │ Computers │
│ 7 │ Rode Z28 │ 275.99 │ Microphones │
│ 2 │ Blue Snowball Microphone │ 99.5 │ Microphones │
│ 5 │ Seagate S1 SSD │ 88.75 │ Accessories │
│ 4 │ Logitech M1 │ 34.99 │ Accessories │
└────────────┴──────────────────────────┴───────────────┴──────────────────┘
Python + SQL
import sqlite3
con = sqlite3.connect("products-database.db")
sql = con.cursor()
# We'll use this a few times so it makes sense for it to be a function
def fetch_and_display_all_rows(query):
result = sql.execute(query)
rows = result.fetchall()
for row in rows:
print(row)
query = """
SELECT * FROM products;
"""
fetch_and_display_all_rows(query)
query = """
SELECT * FROM products ORDER BY product_price;
"""
print("\nProducts ordered from price lowest to highest price:")
fetch_and_display_all_rows(query)
query = """
SELECT * FROM products ORDER BY product_price DESC;
"""
print("\nProducts ordered from price highest to lowest price:")
fetch_and_display_all_rows(query)
Output
(1, 'Dell XPS 17', 1599.99, 'Computers')
(2, 'Blue Snowball Microphone', 99.5, 'Microphones')
(3, 'System76 Thelio B1', 1255.55, 'Computers')
(4, 'Logitech M1', 34.99, 'Accessories')
(5, 'Seagate S1 SSD', 88.75, 'Accessories')
(6, 'MacBook Pro 16', 2100.5, 'Computers')
(7, 'Rode Z28', 275.99, 'Microphones')
(8, 'Lenovo ThinkPad', 950.75, 'Computers')
Products ordered from price lowest to highest price:
(4, 'Logitech M1', 34.99, 'Accessories')
(5, 'Seagate S1 SSD', 88.75, 'Accessories')
(2, 'Blue Snowball Microphone', 99.5, 'Microphones')
(7, 'Rode Z28', 275.99, 'Microphones')
(8, 'Lenovo ThinkPad', 950.75, 'Computers')
(3, 'System76 Thelio B1', 1255.55, 'Computers')
(1, 'Dell XPS 17', 1599.99, 'Computers')
(6, 'MacBook Pro 16', 2100.5, 'Computers')
Products ordered from price highest to lowest price:
(6, 'MacBook Pro 16', 2100.5, 'Computers')
(1, 'Dell XPS 17', 1599.99, 'Computers')
(3, 'System76 Thelio B1', 1255.55, 'Computers')
(8, 'Lenovo ThinkPad', 950.75, 'Computers')
(7, 'Rode Z28', 275.99, 'Microphones')
(2, 'Blue Snowball Microphone', 99.5, 'Microphones')
(5, 'Seagate S1 SSD', 88.75, 'Accessories')
(4, 'Logitech M1', 34.99, 'Accessories')
Further Reading
SELECT¶
To see what data is in a SQL table, you use the SELECT
statement.
Selecting all of the rows and columns from a table¶
You can SELECT *
from a table and that'll give you all of the rows in that table along with all the columns. Be aware that the *
means "Give me all the columns" not "Give me all the rows". All rows are returned from a SELECT
query unless you begin using filters like WHERE
, LIMIT
, or DISTINCT
.
Raw SQL
SELECT * FROM users;
┌─────────┬──────────┬────────────┐
│ user_id │ username │ password │
├─────────┼──────────┼────────────┤
│ 1 │ djs │ mypa$$word │
│ 2 │ django │ w0ff │
│ 3 │ alecg │ c0de │
└─────────┴──────────┴────────────┘
When selecting data from Python, you can fetch all of the rows by using the fetchall()
method of the query result. Note that fetchall()
returns a list
of tuples
, so you would need to do further processing from Python to get the individual rows from this list
, such as looping through the rows.
Python + SQL
import sqlite3
con = sqlite3.connect("users-database.db")
sql = con.cursor()
query = """
SELECT * FROM users;
"""
result = sql.execute(query)
rows = result.fetchall()
print(rows)
for row in rows:
# Using multiple assignment to get the values from each row
user_id, username, password = row
print(f"User ID: {user_id}")
print(f"Username: {username}")
print(f"Password: {password}")
Output
[(1, 'djs', 'mypa$$word'), (2, 'django', 'w0ff'), (3, 'alecg', 'c0de')]
User ID: 1
Username: djs
Password: mypa$$word
User ID: 2
Username: django
Password: w0ff
User ID: 3
Username: alecg
Password: c0de
Selecting specific columns from a table¶
If you only want certain columns returned, you can list them separated by commas after the SELECT
keyword. Notice how the user_id
column is not present in the result set in the query below.
Raw SQL
SELECT username, password FROM users;
┌──────────┬────────────┐
│ username │ password │
├──────────┼────────────┤
│ djs │ mypa$$word │
│ django │ w0ff │
│ alecg │ c0de │
└──────────┴────────────┘
Python + SQL
import sqlite3
con = sqlite3.connect("users-database.db")
sql = con.cursor()
query = """
SELECT username, password FROM users;
"""
result = sql.execute(query)
rows = result.fetchall()
print(rows)
Output
[('djs', 'mypa$$word'), ('django', 'w0ff'), ('alecg', 'c0de')]
Further Reading
UPDATE¶
If you need to change data in a SQL table, the UPDATE
statement is used. Make sure to use a WHERE
clause so that you only update the rows you intend to change.
Raw SQL
SELECT * FROM users;
┌─────────┬──────────┬────────────┐
│ user_id │ username │ password │
├─────────┼──────────┼────────────┤
│ 1 │ djs │ mypa$$word │
│ 2 │ django │ w0ff │
│ 3 │ alecg │ c0de │
└─────────┴──────────┴────────────┘
-- The `djs` user will now have `danielj` as their username.
UPDATE users SET username = "danielj" WHERE user_id = 1;
SELECT * FROM users;
┌─────────┬──────────┬────────────┐
│ user_id │ username │ password │
├─────────┼──────────┼────────────┤
│ 1 │ danielj │ mypa$$word │
│ 2 │ django │ w0ff │
│ 3 │ alecg │ c0de │
└─────────┴──────────┴────────────┘
Python + SQL
import sqlite3
con = sqlite3.connect("users-database.db")
sql = con.cursor()
# We'll use this twice, so it makes sense to be a function
def display_all_users():
query = """
SELECT * FROM users;
"""
result = sql.execute(query)
rows = result.fetchall()
print(rows)
display_all_users()
query = """
UPDATE users SET username = "danielj" WHERE id = 1;
"""
sql.execute(query)
# Make sure to commit the changes to the DB
con.commit()
display_all_users()
Output
[(1, 'djs', 'mypa$$word'), (2, 'django', 'w0ff'), (3, 'alecg', 'c0de')]
[(1, 'danielj', 'mypa$$word'), (2, 'django', 'w0ff'), (3, 'alecg', 'c0de')]
Updating multiple columns¶
If you need to update multiple columns, you can separate the SET
clauses with commas. We've also put each new SQL command on a new line and added some indentation to make this longer query easier to read.
Raw SQL
SELECT * FROM users;
┌─────────┬──────────┬────────────┐
│ user_id │ username │ password │
├─────────┼──────────┼────────────┤
│ 1 │ djs │ mypa$$word │
│ 2 │ django │ w0ff │
│ 3 │ alecg │ c0de │
└─────────┴──────────┴────────────┘
UPDATE users
SET
username = "danielj", -- note the comma here
password = "b3tTerpa$$w0rd"
WHERE user_id = 1;
SELECT * FROM users;
┌─────────┬──────────┬────────────────┐
│ user_id │ username │ password │
├─────────┼──────────┼────────────────┤
│ 1 │ danielj │ b3tTerpa$$w0rd │
│ 2 │ django │ w0ff │
│ 3 │ alecg │ c0de │
└─────────┴──────────┴────────────────┘
Python + SQL
import sqlite3
con = sqlite3.connect("users-database.db")
sql = con.cursor()
# We'll use this twice, so it makes sense to be a function
def display_all_users():
query = """
SELECT * FROM users;
"""
result = sql.execute(query)
rows = result.fetchall()
print(rows)
display_all_users()
query = """
UPDATE users
SET
username = "danielj",
password = "b3tTerpa$$w0rd"
WHERE user_id = 1;
"""
sql.execute(query)
# Make sure to commit the changes to the DB
con.commit()
display_all_users()
Output
[(1, 'djs', 'mypa$$word'), (2, 'django', 'w0ff'), (3, 'alecg', 'c0de')]
[(1, 'danielj', 'b3tTerpa$$w0rd'), (2, 'django', 'w0ff'), (3, 'alecg', 'c0de')]
Further Reading
WHERE¶
To filter the results from a SQL query, use the WHERE
clause.
Raw SQL
SELECT * FROM users;
┌─────────┬──────────┬────────────┐
│ user_id │ username │ password │
├─────────┼──────────┼────────────┤
│ 1 │ djs │ mypa$$word │
│ 2 │ django │ w0ff │
│ 3 │ alecg │ c0de │
└─────────┴──────────┴────────────┘
SELECT * FROM users WHERE username = "djs";
┌─────────┬──────────┬────────────┐
│ user_id │ username │ password │
├─────────┼──────────┼────────────┤
│ 1 │ djs │ mypa$$word │
└─────────┴──────────┴────────────┘
Python + SQL
import sqlite3
con = sqlite3.connect("users-database.db")
sql = con.cursor()
def run_query_and_display_results(query):
result = sql.execute(query)
rows = result.fetchall()
print(rows)
query = """
SELECT * FROM users;
"""
run_query_and_display_results(query)
query = """
SELECT * FROM users WHERE username = "djs";
"""
run_query_and_display_results(query)
Output
[(1, 'djs', 'mypa$$word'), (2, 'django', 'w0ff'), (3, 'alecg', 'c0de')]
[(1, 'djs', 'mypa$$word')]
Getting a single row from Python when using WHERE
¶
If you only need a single row from a SELECT
statement using a WHERE
clause in your Python programs, use fetchone()
. This returns a tuple
of the data in each column, and you can use techniques like tuple unpacking or indexing to pull the individual values from the tuple
.
Python + SQL
import sqlite3
con = sqlite3.connect("users-database.db")
sql = con.cursor()
query = """
SELECT * FROM users WHERE username = "djs";
"""
result = sql.execute(query)
row = result.fetchone()
# This will be a tuple
print(row)
# Getting the values from the row with tuple unpacking
user_id, username, password = row
print(f"User ID: {user_id}")
print(f"Username: {username}")
print(f"Password: {password}")
Output
(1, 'djs', 'mypa$$word')
User ID: 1
Username: djs
Password: mypa$$word
Using conditional logic with WHERE
clauses¶
There are many operators available to use in a WHERE
clause. The ones that you can use with numerical data are shown below (=
, IN
, and NOT IN
can also be used with TEXT
data):
Operator | Description |
---|---|
= | Equality (works for numbers and TEXT) |
> | Greater-than |
< | Less-than |
>= | Greater-than or equal-to |
<= | Less-than or equal-to |
BETWEEN...AND | Number is between two values |
NOT BETWEEN...AND | Number is not between two values |
IN (...) | Number/Text exists in a list |
NOT IN (...) | Number/Text does not exist in a list |
Here are examples of a few of the operators on a table of products
:
Raw SQL
SELECT * FROM products;
┌────────────┬──────────────────────────┬───────────────┬──────────────────┐
│ product_id │ product_name │ product_price │ product_category │
├────────────┼──────────────────────────┼───────────────┼──────────────────┤
│ 1 │ Dell XPS 17 │ 1599.99 │ Computers │
│ 2 │ Blue Snowball Microphone │ 99.5 │ Microphones │
│ 3 │ System76 Thelio B1 │ 1255.55 │ Computers │
│ 4 │ Logitech M1 │ 34.99 │ Accessories │
│ 5 │ Seagate S1 SSD │ 88.75 │ Accessories │
│ 6 │ MacBook Pro 16 │ 2100.5 │ Computers │
│ 7 │ Rode Z28 │ 275.99 │ Microphones │
│ 8 │ Lenovo ThinkPad │ 950.75 │ Computers │
└────────────┴──────────────────────────┴───────────────┴──────────────────┘
-- Get the product where the `product_name` is "Lenovo ThinkPad".
SELECT * FROM products WHERE product_name = "Lenovo ThinkPad";
┌────────────┬─────────────────┬───────────────┬──────────────────┐
│ product_id │ product_name │ product_price │ product_category │
├────────────┼─────────────────┼───────────────┼──────────────────┤
│ 8 │ Lenovo ThinkPad │ 950.75 │ Computers │
└────────────┴─────────────────┴───────────────┴──────────────────┘
-- Get the products that cost less than $1000.
SELECT * FROM products WHERE product_price < 1000;
┌────────────┬──────────────────────────┬───────────────┬──────────────────┐
│ product_id │ product_name │ product_price │ product_category │
├────────────┼──────────────────────────┼───────────────┼──────────────────┤
│ 2 │ Blue Snowball Microphone │ 99.5 │ Microphones │
│ 4 │ Logitech M1 │ 34.99 │ Accessories │
│ 5 │ Seagate S1 SSD │ 88.75 │ Accessories │
│ 7 │ Rode Z28 │ 275.99 │ Microphones │
│ 8 │ Lenovo ThinkPad │ 950.75 │ Computers │
└────────────┴──────────────────────────┴───────────────┴──────────────────┘
-- Get the products that cost more than $1000.
SELECT * FROM products WHERE product_price > 1000;
┌────────────┬────────────────────┬───────────────┬──────────────────┐
│ product_id │ product_name │ product_price │ product_category │
├────────────┼────────────────────┼───────────────┼──────────────────┤
│ 1 │ Dell XPS 17 │ 1599.99 │ Computers │
│ 3 │ System76 Thelio B1 │ 1255.55 │ Computers │
│ 6 │ MacBook Pro 16 │ 2100.5 │ Computers │
└────────────┴────────────────────┴───────────────┴──────────────────┘
-- Get the products whose prices are between $50 and $300 (inclusive).
SELECT * FROM products WHERE product_price BETWEEN 50 AND 300;
┌────────────┬──────────────────────────┬───────────────┬──────────────────┐
│ product_id │ product_name │ product_price │ product_category │
├────────────┼──────────────────────────┼───────────────┼──────────────────┤
│ 2 │ Blue Snowball Microphone │ 99.5 │ Microphones │
│ 5 │ Seagate S1 SSD │ 88.75 │ Accessories │
│ 7 │ Rode Z28 │ 275.99 │ Microphones │
└────────────┴──────────────────────────┴───────────────┴──────────────────┘
-- Get the products that are in the "Microphones" and "Computers" categories.
SELECT * FROM products WHERE product_category IN ("Microphones", "Computers");
┌────────────┬──────────────────────────┬───────────────┬──────────────────┐
│ product_id │ product_name │ product_price │ product_category │
├────────────┼──────────────────────────┼───────────────┼──────────────────┤
│ 1 │ Dell XPS 17 │ 1599.99 │ Computers │
│ 2 │ Blue Snowball Microphone │ 99.5 │ Microphones │
│ 3 │ System76 Thelio B1 │ 1255.55 │ Computers │
│ 6 │ MacBook Pro 16 │ 2100.5 │ Computers │
│ 7 │ Rode Z28 │ 275.99 │ Microphones │
│ 8 │ Lenovo ThinkPad │ 950.75 │ Computers │
└────────────┴──────────────────────────┴───────────────┴──────────────────┘
Python + SQL
import sqlite3
con = sqlite3.connect("products-database.db")
sql = con.cursor()
# We'll use this a few times so it makes sense for it to be a function
def fetch_and_display_all_rows(query):
result = sql.execute(query)
rows = result.fetchall()
for row in rows:
print(row)
query = """
SELECT * FROM products;
"""
fetch_and_display_all_rows(query)
query = """
SELECT * FROM products WHERE product_name = "Lenovo ThinkPad";
"""
print("\nLooking for the 'Lenovo ThinkPad':")
fetch_and_display_all_rows(query)
query = """
SELECT * FROM products WHERE product_price < 1000;
"""
print("\nProducts cheaper than $1000:")
fetch_and_display_all_rows(query)
query = """
SELECT * FROM products WHERE product_price > 1000;
"""
print("\nProducts more expensive than $1000:")
fetch_and_display_all_rows(query)
query = """
SELECT * FROM products WHERE product_price BETWEEN 50 AND 300;
"""
print("\nProducts between $50 and $300 (inclusive):")
fetch_and_display_all_rows(query)
query = """
SELECT * FROM products WHERE product_category IN ("Microphones", "Computers");
"""
print("\nProducts in the 'Microphones' and 'Computers' categories:")
fetch_and_display_all_rows(query)
Output
(1, 'Dell XPS 17', 1599.99, 'Computers')
(2, 'Blue Snowball Microphone', 99.5, 'Microphones')
(3, 'System76 Thelio B1', 1255.55, 'Computers')
(4, 'Logitech M1', 34.99, 'Accessories')
(5, 'Seagate S1 SSD', 88.75, 'Accessories')
(6, 'MacBook Pro 16', 2100.5, 'Computers')
(7, 'Rode Z28', 275.99, 'Microphones')
(8, 'Lenovo ThinkPad', 950.75, 'Computers')
Looking for the 'Lenovo ThinkPad':
(8, 'Lenovo ThinkPad', 950.75, 'Computers')
Products cheaper than $1000:
(2, 'Blue Snowball Microphone', 99.5, 'Microphones')
(4, 'Logitech M1', 34.99, 'Accessories')
(5, 'Seagate S1 SSD', 88.75, 'Accessories')
(7, 'Rode Z28', 275.99, 'Microphones')
(8, 'Lenovo ThinkPad', 950.75, 'Computers')
Products more expensive than $1000:
(1, 'Dell XPS 17', 1599.99, 'Computers')
(3, 'System76 Thelio B1', 1255.55, 'Computers')
(6, 'MacBook Pro 16', 2100.5, 'Computers')
Products between $50 and $300 (inclusive):
(2, 'Blue Snowball Microphone', 99.5, 'Microphones')
(5, 'Seagate S1 SSD', 88.75, 'Accessories')
(7, 'Rode Z28', 275.99, 'Microphones')
Products in the 'Microphones' and 'Computers' categories:
(1, 'Dell XPS 17', 1599.99, 'Computers')
(2, 'Blue Snowball Microphone', 99.5, 'Microphones')
(3, 'System76 Thelio B1', 1255.55, 'Computers')
(6, 'MacBook Pro 16', 2100.5, 'Computers')
(7, 'Rode Z28', 275.99, 'Microphones')
(8, 'Lenovo ThinkPad', 950.75, 'Computers')
Complex conditional logic with WHERE
clauses¶
You can join multiple WHERE
clauses with the logical AND
and OR
operators to make complex conditional statements, just like in a programming language like Python or JavaScript.
Raw SQL
SELECT * FROM products;
┌────────────┬──────────────────────────┬───────────────┬──────────────────┐
│ product_id │ product_name │ product_price │ product_category │
├────────────┼──────────────────────────┼───────────────┼──────────────────┤
│ 1 │ Dell XPS 17 │ 1599.99 │ Computers │
│ 2 │ Blue Snowball Microphone │ 99.5 │ Microphones │
│ 3 │ System76 Thelio B1 │ 1255.55 │ Computers │
│ 4 │ Logitech M1 │ 34.99 │ Accessories │
│ 5 │ Seagate S1 SSD │ 88.75 │ Accessories │
│ 6 │ MacBook Pro 16 │ 2100.5 │ Computers │
│ 7 │ Rode Z28 │ 275.99 │ Microphones │
│ 8 │ Lenovo ThinkPad │ 950.75 │ Computers │
└────────────┴──────────────────────────┴───────────────┴──────────────────┘
-- Get all microphones less than $200.
SELECT * FROM products
WHERE product_category = "Microphones" AND product_price < 200;
┌────────────┬──────────────────────────┬───────────────┬──────────────────┐
│ product_id │ product_name │ product_price │ product_category │
├────────────┼──────────────────────────┼───────────────┼──────────────────┤
│ 2 │ Blue Snowball Microphone │ 99.5 │ Microphones │
└────────────┴──────────────────────────┴───────────────┴──────────────────┘
-- Get any microphones or computers less than $1000.
SELECT * FROM products
WHERE product_price < 1000 AND product_category IN ("Computers", "Microphones");
┌────────────┬──────────────────────────┬───────────────┬──────────────────┐
│ product_id │ product_name │ product_price │ product_category │
├────────────┼──────────────────────────┼───────────────┼──────────────────┤
│ 2 │ Blue Snowball Microphone │ 99.5 │ Microphones │
│ 7 │ Rode Z28 │ 275.99 │ Microphones │
│ 8 │ Lenovo ThinkPad │ 950.75 │ Computers │
└────────────┴──────────────────────────┴───────────────┴──────────────────┘
Python + SQL
import sqlite3
con = sqlite3.connect("products-database.db")
sql = con.cursor()
# We'll use this a few times so it makes sense for it to be a function
def fetch_and_display_all_rows(query):
result = sql.execute(query)
rows = result.fetchall()
for row in rows:
print(row)
query = """
SELECT * FROM products;
"""
fetch_and_display_all_rows(query)
query = """
SELECT * FROM products
WHERE product_category = "Microphones" AND product_price < 200;
"""
print("\nGetting microphones less than $200:")
fetch_and_display_all_rows(query)
query = """
SELECT * FROM products
WHERE product_price < 1000 AND product_category IN ("Computers", "Microphones");
"""
print("\nComputers and Microphones cheaper than $1000:")
fetch_and_display_all_rows(query)
Output
(1, 'Dell XPS 17', 1599.99, 'Computers')
(2, 'Blue Snowball Microphone', 99.5, 'Microphones')
(3, 'System76 Thelio B1', 1255.55, 'Computers')
(4, 'Logitech M1', 34.99, 'Accessories')
(5, 'Seagate S1 SSD', 88.75, 'Accessories')
(6, 'MacBook Pro 16', 2100.5, 'Computers')
(7, 'Rode Z28', 275.99, 'Microphones')
(8, 'Lenovo ThinkPad', 950.75, 'Computers')
Getting microphones less than $200:
(2, 'Blue Snowball Microphone', 99.5, 'Microphones')
Computers and Microphones cheaper than $1000:
(2, 'Blue Snowball Microphone', 99.5, 'Microphones')
(7, 'Rode Z28', 275.99, 'Microphones')
(8, 'Lenovo ThinkPad', 950.75, 'Computers')
Further Reading