Simple SQL with simplestuff.sqlite
An example database that is so simple that all of its contents can be printed on this page. But the data and structure is "complex" enough to teach moderately advanced SQL syntax, including self-joins, subqueries, and recursive common-table-expressions.
The data values are meant to be simple enough so that, at a glance, you can mentally compute that a woman named Angela owns two dogs without having to write any SQL, nevermind a JOIN query. So when you do learn how to write a SQL join query, you know what answer you're supposed to get.
Download the database: simplestuff.sqlite
About the data
The simplestuff.sqlite database consists of 3 relational tables: people, bios, and pets. People each have a
name and an
id, they have biographical information, and some of these people own pets.
That's all there's to it. Why is a person's birthdate and gender in a different table --
bios -- rather than being columns in the
people table -- other than to provide convenient examples for JOIN queries? Sometimes -- actually most of the time -- data just isn't conveniently packaged in one file, table, or even database. Knowing SQL and how to JOIN is how we get past those problems.
The schema for the database, i.e. the structure of all 3 tables, can be seen in this file: schema.sql
SELECT * FROM people;
Pretty straightforward table of "people". Although real people usually have last names, I left that out to keep things simple. Also, the records in
people are in order both alphabetically by
name and by
And it's worth pointing out that the
id field is not a number, but a text string.
SELECT * FROM bios;
bios table is connected to
people via the
The biographical records have a *one-to-one relationship with the people. In other words, at any given point in time, each person has one, and only one birthdate and gender.
However, this doesn't necessarily mean that all records in
bios have a corresponding person. You can eyeball the last record in
bios having an outlier value (
id with no corresponding
In real-life databases, these kind of mismatches are not uncommon. Think of deleting a record from one table and forgetting to delete its corresponding related record from the other table.
SELECT * FROM pets;
pets table, like
bios, is connected to
people using the
people.id field. However, unlike
bios, the linking column -- also known as the foreign key column -- is not named
pets has a many-to-one relationship to
people. A person may own a single pet, in which case it seems like a one-to-one relationship. But another person may own many pets. Or none at all.
Note that in the real world, the relationship between people and pets is actually many-to-many: Not only can a person have many pets, but a pet may have many owners. But
simplestuff.sqlite doesn't attempt to model the real world, just a simple one.
One more thing to note: The
price value for the pet named 'Flipper' is listed as
| name | species | purchase_date | price | owner_id | | -------- | ------- | ------------- | ----- | -------- | ... | Flipper | dog | 2014-05-09 | NULL | 005 | ...
That is not intended to be a string literal, i.e. to be quoted as in the case of
'dog' -- but the special value of
NULL, which is used in SQL to represent non-existence. This is not the same as
0 or a blank/empty string. We can think of Flipper as not having been paid for, rather than having been acquired for free or for an unknown value.s