sqlite3 Command Basics - The Shortest Path to Trying SQL in the Terminal

sqlite3 Command Basics - The Shortest Path to Trying SQL in the Terminal

What you'll be able to do

  • Open a database with `sqlite3 gym.db`, create a table, and insert data
  • Pick out only the rows you need with `SELECT` / `WHERE` / `ORDER BY` / `LIMIT` / `COUNT(*)`
  • Pipe SQL results into `sort` / `uniq` and turn a database back into text with `.dump`

Prerequisites (read these first)

What You'll Learn

  • How to create a database as a single file with sqlite3
  • The smallest SQL to make a table, add data, and pick it out with SELECT
  • Why the numbers differ between COUNT(*) and wc -l
  • How to pipe SQL results into sort / uniq, and how to turn a database back into text with .dump

Words used here (sorted out first)

  • Database: a system that stores data so you can search and count it later. Often written "DB".
  • Table: data arranged in rows and columns. It is close to a sheet in Excel.
  • Rows and columns: a row runs across a table, a column runs down. They are also called "records" and "fields".
  • SQL: the language you use to give orders to a database. Many people read it "S-Q-L" or "sequel". One command written in SQL is also called a "query".
  • SQLite and sqlite3: SQLite is the name of the software. sqlite3 is the name of the command that operates it.
  • CSV: a text file whose values are separated by commas (,).
  • PromptA symbol (like $ or #) shown when the shell is waiting for your input.: the mark at the start of a line that says "you can type here". In a normal terminalAn interactive program that reads the commands you type and runs them. the line ends with $, like user@pcname:~$. Do not type the $ shown in this article.

Environment

1. Why sqlite3?

Conclusion: SQLite needs no server. The whole database is a single file, so you can handle it like any file you see with ls.

Lina: Linny-senpai, I want to try SQL. But setting up a database server sounds like a lot of work.
Linny-senpai: That is true for many databases. But SQLite needs no server. The whole database becomes one file.
Lina: Just one file? Can I see it with ls?
Linny-senpai: Yes. The gym.db we will make shows up like this with ls -l.
$ ls -l gym.db
-rw-r--r-- 1 user user 8192 Oct 24 02:18 gym.db

There is no gym.db yet, so you can try this ls -l after section 4. The owner name and the date depend on your environment.

2. How Do I Install sqlite3?

Conclusion: On Ubuntu and WSL, install it with apt. A Mac already has it. On both, check it with sqlite3 --version.

On Ubuntu / Debian-based systems

$ sudo apt update
$ sudo apt install sqlite3
$ sqlite3 --version
3.45.1 2024-01-30 16:01:20 e876e51a0ed5c5b3126f52e532044363a014bc594cfefa87ffb5b82257ccalt1 (64-bit)

sudo runs a command with administrator rights. If it asks for a password, type your own login password. The screen shows nothing as you type, not even *. Type it all the way and press Enter. The first number printed by sqlite3 --version is the version. In this example it is 3.45.1.

On Windows: open Ubuntu in WSL (a way to run Linux inside Windows). Then follow the same apt steps. If you do not have WSL yet, start with WSL2 Introduction.

On a Mac: macOS already comes with sqlite3. Check it with sqlite3 --version. You can try everything in this article with the sqlite3 that ships with macOS.

It is safer to make a practice directoryA container that organizes files. Same idea as a "folder" on Windows or macOS. (a folder) first.

$ mkdir ~/sqlite-practice
$ cd ~/sqlite-practice

Every file in this article goes inside it.

3. What Changes When I Start sqlite3?

Conclusion: Starting sqlite3 gym.db changes the prompt to sqlite>. Commands that begin with a dot are orders to sqlite3 itself.

Linny-senpai: Let's start it. Write the database file name after sqlite3.
Lina: I have not made gym.db yet. Is that OK?
Linny-senpai: It is fine. The official documentation says the file is created automatically if it does not exist. To be exact, the file appears when you make a table.
$ sqlite3 gym.db
SQLite version 3.45.1 2024-01-30 16:01:20
Enter ".help" for usage hints.
sqlite>
Lina: The last line changed from $ to sqlite>.
Linny-senpai: That means "you are inside sqlite3 now". Here you type SQL and "dot-commands" that start with .. Dot-commands are orders to sqlite3 itself.

These are the five you use first.

Dot-command What it does
.tables List the tables
.schema members Show how the members table is defined
.headers on Print the column names on the first line
.mode column Line results up in columns
.quit Leave sqlite3 and go back to $

Do not put a semicolon (;) at the end of a dot-command. Always put ; at the end of SQL.

Version 3.54.0 and later show a different prompt (see the 3.54.0 entry in the release history). The example in the official documentation is SQLite-3.54 ex1.db->. What you type is the same.

In the code examples, do not type the leading $ or sqlite>. Type only what comes after it. If you use the copy button, delete the leading sqlite> (or $ ) after pasting, then press Enter.

Sections 4 and 5 continue at the sqlite> prompt. If you stop halfway, leave with .quit and come back with sqlite3 gym.db. The table and its data stay in the file. Do not type the CREATE TABLE and INSERT from section 4 again (doing so adds the same rows again). .headers on and .mode column are cleared, so type those two again when you resume from section 5.

4. How Do I Make a Table and Add Data?

Conclusion: CREATE TABLE decides the shape of a table, and INSERT adds data one row at a time. Always end SQL with ;.

Linny-senpai: Let's make a table of gym members. It has four columns: id (number), name, city (where they live), and age.
Lina: So it is like writing headings in the first row in Excel.
sqlite> CREATE TABLE members (id INTEGER PRIMARY KEY, name TEXT, city TEXT, age INTEGER);
sqlite> INSERT INTO members (name, city, age) VALUES ('Pip', 'Tokyo', 24);
sqlite> INSERT INTO members (name, city, age) VALUES ('Momo', 'Osaka', 31);
sqlite> INSERT INTO members (name, city, age) VALUES ('Kai', 'Tokyo', 19);

If it works, nothing is printed and the next sqlite> appears.

  • INTEGER means a whole number, and TEXT means text (a string)
  • Because of id INTEGER PRIMARY KEY, id gets the numbers 1, 2, 3 automatically
  • Put text in single quotes ('), like 'Tokyo'

Check the result with dot-commands.

sqlite> .tables
members
sqlite> .schema members
CREATE TABLE members (id INTEGER PRIMARY KEY, name TEXT, city TEXT, age INTEGER);
Lina: .tables is like ls, and .schema is like cat.
Linny-senpai: Good way to see it. Use .tables to see table names, and .schema to read how a table is built.

If you forget ; and press Enter, the prompt changes to ...>. It means "waiting for the rest". Type just ; and press Enter to run it.

5. How Do I Pick Out Only the Rows I Need?

Conclusion: Narrow with WHERE, order with ORDER BY, and cut with LIMIT. They play the same roles as grep, sort, and head.

First, print everything in the table. * means "all columns".

sqlite> SELECT * FROM members;
1|Pip|Tokyo|24
2|Momo|Osaka|31
3|Kai|Tokyo|19
Lina: It worked, but the | separators are hard to read. I can't tell which column is which.
Linny-senpai: That is what .headers on and .mode column are for. The column names appear and everything lines up.
sqlite> .headers on
sqlite> .mode column
sqlite> SELECT * FROM members;
id  name  city   age
--  ----  -----  ---
1   Pip   Tokyo  24
2   Momo  Osaka  31
3   Kai   Tokyo  19

In version 3.53.0 and later, a terminal session shows a boxed table from the start (per https://sqlite.org/climode.html and the 3.53.0 entry in the release history). Even with a newer version, type .headers on and .mode column in this article.

5-1. Narrow rows with WHERE (like grep)

sqlite> SELECT name, age FROM members WHERE city = 'Tokyo';
name  age
----  ---
Pip   24
Kai   19

Like grep Tokyo, only the rows that match the condition appear.

5-2. Order rows with ORDER BY (like sort)

sqlite> SELECT name, age FROM members ORDER BY age DESC;
name  age
----  ---
Momo  31
Pip   24
Kai   19

DESC means largest first. It is the same direction as sort -rn. Without DESC, the order is smallest first.

5-3. Cut the count with LIMIT (like head)

sqlite> SELECT name, age FROM members ORDER BY age DESC LIMIT 2;
name  age
----  ---
Momo  31
Pip   24

Like head -n 2, only the first two rows appear.

6. What Happens When I Count?

Conclusion: COUNT(*) counts the rows of a table. Write it to CSV and count with wc -l, and the column-name line adds one extra.

sqlite> SELECT COUNT(*) FROM members;
COUNT(*)
--------
3
Lina: Three members. I want to save it as CSV and check the number with wc -l too.
Linny-senpai: Use .mode csv for the CSV format, and .once to write "only the next result" to a file.
sqlite> .mode csv
sqlite> .once members.csv
sqlite> SELECT * FROM members;
sqlite> .quit
$ wc -l members.csv
4 members.csv

.quit brought the prompt back to $, so the last wc -l runs as a normal Linux command.

Lina: What? Four lines, but there are three members. No error came up, yet the numbers don't match.
Linny-senpai: Let's look inside.
$ cat members.csv
id,name,city,age
1,Pip,Tokyo,24
2,Momo,Osaka,31
3,Kai,Tokyo,19
Lina: Oh, the first line is id,name,city,age. I turned on .headers on earlier, so the column names were written too.
Linny-senpai: Exactly. wc -l counts lines in a file, and COUNT(*) counts rows in a table. They count different things.
Lina: So a command that runs fine can still give the wrong number. I see why comparing two ways of counting matters.

To count without the column-name line, use tail -n +2 (print from line 2 to the end).

$ tail -n +2 members.csv | wc -l
3

Now it matches the 3 from COUNT(*). Settings such as .headers on go back to the defaults when you leave sqlite3.

7. How Do I Pipe SQL Results?

Conclusion: Writing sqlite3 FILE "SQL" runs it once and exits. The result is plain text, so it pipes straight into sort or uniq.

Lina: Going into sqlite3 and leaving with .quit every time is a bit of a hassle.
Linny-senpai: Write one SQL statement after the file name, and it runs right there and exits (per the official documentation). The result is just text, so you can pipe it.
$ sqlite3 gym.db "SELECT city FROM members" | sort | uniq -c
      1 Osaka
      2 Tokyo
  • sqlite3 gym.db "SELECT city FROM members" prints the city names one per line
  • sort puts the same cities next to each other
  • uniq -c counts identical lines

This form does not use .headers on, so no column-name line appears. According to the official documentation, results come out in "list mode" (separated by |).

7-1. Try interactive mode in the virtual terminal

The state where the sqlite> prompt from section 3 is showing is called "interactive mode". You type one line at a time and see the result right away.

You can also try interactive mode in the virtual terminal on this site. Open the lesson "Count the Members" and type the lines below in order. Do not type the leading $ or sqlite>.

$ sqlite3 gym.db
sqlite> SELECT COUNT(*) FROM members;
(one line with the number of members in the lesson's gym.db)
sqlite> .quit

The gym.db in the virtual terminal is prepared for the lesson, so the count differs from the 3 members in this article. When you count correctly, the lesson shows as complete. The one-shot form from section 7 (sqlite3 gym.db "SELECT COUNT(*) FROM members;") completes it as well. In the virtual terminal, the prompt is sqlite> whatever the version.

8. How Do I Turn a Database Back into Text?

Conclusion: .dump turns the whole database into SQL text. Feed that text to sqlite3 and you can rebuild the same database.

> overwrites without asking

If a file with the same name already exists, > erases it and writes again. Unlike a GUI, it does not ask "Do you want to replace it?". The .once in section 6 overwrites in the same way. Before you type it, check with ls that no file has the same name, and try it inside your practice directory.

$ sqlite3 gym.db .dump > backup.sql
$ cat backup.sql
PRAGMA foreign_keys=OFF;
BEGIN TRANSACTION;
CREATE TABLE members (id INTEGER PRIMARY KEY, name TEXT, city TEXT, age INTEGER);
INSERT INTO members VALUES(1,'Pip','Tokyo',24);
INSERT INTO members VALUES(2,'Momo','Osaka',31);
INSERT INTO members VALUES(3,'Kai','Tokyo',19);
COMMIT;
Lina: The CREATE TABLE and INSERT I typed are listed here.
Linny-senpai: It wrote out the database as SQL that rebuilds it. Let's feed it to a new file.

BEGIN TRANSACTION and COMMIT run the SQL between them as one unit. The PRAGMA line is a setting that sqlite3 added on its own. You can skip them for now.

$ sqlite3 new.db < backup.sql
$ sqlite3 new.db "SELECT * FROM members"
1|Pip|Tokyo|24
2|Momo|Osaka|31
3|Kai|Tokyo|19

< feeds the contents of a file to a command. The SQL in backup.sql ran from top to bottom, and new.db now has the same table.

9. Common Mistakes and Fixes

Conclusion: Most mistakes come from four things: semicolons, quotes, letter case, and file names. If an error appears, read it. If there is no error but the result looks wrong, look at the contents directly with SELECT * or ls -l.

Symptom Cause Check / fix
Parse error: near "sqlite": syntax error You copied the leading sqlite> too Delete sqlite> and type it again
The prompt stays at ...> The ; at the end of the SQL is missing Type just ; and press Enter
Parse error: no such column: Tokyo The text is not in ' quotes QuoteWrapping a string in quote marks (' or ") so it is treated as one single value, e.g. one with spaces in it. it, like WHERE city = 'Tokyo'
WHERE city = 'tokyo' returns nothing Text comparison treats upper and lower case as different Match how it is stored in the table (Tokyo)
gym.db is missing after starting and quitting at once In 3.45.1, the file is not created if you do nothing Make a table before .quit, then check with ls -l
The member count went up after table members already exists You ran the INSERT lines twice Remove them with DELETE FROM members WHERE id > 3;, or make gym.db again
new.db < backup.sql says table members already exists new.db already has the same table Feed it to a file with another name (e.g. new2.db)

The 'tokyo' case returns zero rows without an error. When a result is empty, first run SELECT * FROM members; to see how the values are stored.

10. Mini Exercises: Try It Yourself

Conclusion: Try ordering, counting, and piping with your own hands. The answers are inside the folded sections.

Exercise 1: Order by youngest first

Task: Enter with sqlite3 gym.db and type .headers on and .mode column. Then print every member's name and age, youngest first.

Show hint 1 (direction)

Add a phrase for "ordering" after SELECT. Leave out the word you used for largest-first.

Show hint 2 (command name)

Use ORDER BY age. Do not add DESC.

Show answer
sqlite> SELECT name, age FROM members ORDER BY age;
name  age
----  ---
Kai   19
Pip   24
Momo  31

Exercise 2: Count the members in Osaka

Task: Count how many members have city set to Osaka, using SQL only.

Show hint 1 (direction)

Combine two things: "counting" and "narrowing by a condition".

Show hint 2 (command name)

Add WHERE city = 'Osaka' to SELECT COUNT(*).

Show answer
sqlite> SELECT COUNT(*) FROM members WHERE city = 'Osaka';
COUNT(*)
--------
1

Exercise 3: Count Tokyo members with a pipe

Task: First, go back to $ with .quit. Then, without entering sqlite3, print the names of the members in Tokyo, and count those lines with a Linux command.

Show hint 1 (direction)

Check that the prompt is $. Use the form that runs sqlite3 once and exits. Pass its result to the command that counts lines.

Show hint 2 (command name)

Add | wc -l after sqlite3 gym.db "SELECT ...".

Show answer
$ sqlite3 gym.db "SELECT name FROM members WHERE city = 'Tokyo'" | wc -l
2

This form prints no column-name line, so the wc -l number is the member count as is.

11. Review

Lina: Let me sum up. SQLite needs no server, and the database was a single file called gym.db.
Linny-senpai: Right. We made the table with CREATE TABLE and added rows with INSERT.
Lina: WHERE is like grep, ORDER BY like sort, and LIMIT like head. And wc -l was one higher because of the column-name line.
Linny-senpai: That is the most important part. Comparing counts made in two ways shows you the gap. Also remember that .dump turns it back into text.

Today's 3-Line Summary

  1. SQLite needs no server, and the database is one file you can see with ls
  2. SELECT / WHERE / ORDER BY / LIMIT match up with grep / sort / head
  3. COUNT(*) and wc -l count different things. Watch the first line of a CSV (the column names)

Summary: What to Read Next

Share this article

Next steps