Chapter 01 · Data, Databases, DBMSs, and SQL
Building a Safe SQL Practice Environment
Create an isolated SQL laboratory that is free, reproducible, easy to reset, and protected from accidental interaction with valuable data.
Learning outcomes
A good learning environment is disposable, reproducible, isolated from valuable data, and easy to inspect. The goal is not merely to “make SQL run,” but to build habits that scale to production database work.
Choose among the SQLite CLI, SQLite Fiddle, and Python sqlite3 for a free practice environment.
Create a predictable folder, database file, and repeatable SQL bootstrap script.
Use transactions, backups, read-only access, and disposable copies to reduce accidental damage.
Diagnose common command-line, path, syntax, and locking problems.
Why SQLite is the first laboratory
SQLite is an in-process SQL database engine. It does not require a separate server, administrator account, or network port. A complete database can live in one file, making it easy to copy, reset, inspect, and archive.
| Option | Best use | Persistence |
|---|---|---|
| SQLite command-line shell | Full local course workflow, scripts, files, backups, and dot commands | Database file on your machine |
| SQLite Fiddle | Immediate browser experimentation without installation | Browser session; treat as disposable |
Python sqlite3 | Students who already have Python and want an application-access path | Database file controlled by the script |
| In-memory SQLite | Tests and experiments that must disappear automatically | Lost when the connection closes |
Examples use portable SQL where practical. SQLite-specific dot commands and functions are labeled. Later database courses repeat the concepts in PostgreSQL, MySQL, SQL Server, and other systems.
Safety boundary: never practice on production data
A training environment must not connect to a production database or contain confidential customer, employee, health, financial, authentication, or proprietary records. Use generated data, public datasets with clear licenses, or deliberately fictional examples.
Create a visible boundary:
- a separate folder and database file;
- distinct names such as
practice.dbrather than a production-like name; - no saved production credentials;
- no VPN or network route required for the lab;
- version-controlled SQL scripts, but not copied database files containing sensitive data;
- disposable backups before destructive experiments.
A committed DELETE, UPDATE, DROP, or overwrite can be irreversible without a tested backup. Safety comes from isolation, transactions, review, backups, and restore practice.
Option A: SQLite command-line shell
Obtain the SQLite command-line tools from the official SQLite download page or your operating system’s trusted package manager. Verify the executable from a terminal:
sqlite3 --versionsqlite3 --help The exact version number is not important for these lessons. The command must run and identify itself as SQLite. If the shell reports that sqlite3 is not recognized or not found, the executable is not installed or its directory is not on the command search path.
Create a dedicated workspace
mkdir sql-foundations-labcd sql-foundations-lab # Save the SQL shown below as setup.sql, then run:sqlite3 practice.db < setup.sql # Open the database interactively:sqlite3 practice.db # Inside the SQLite prompt:.headers on.mode box.tables.schema practice_notesSELECT * FROM practice_notes;.quit When a named database file does not exist, the SQLite CLI creates it. Dot commands such as .headers, .mode, .tables, and .schema belong to the CLI, not to standard SQL. Enter them without a semicolon.
Option B: use SQLite Fiddle
The official SQLite Fiddle runs a WebAssembly build in the browser. It is useful when installation is unavailable, but browser storage and UI behavior are not a durable course workspace.
- Open SQLite Fiddle.
- Paste the SQL bootstrap script from this lesson.
- Run the statements and inspect the result.
- Copy useful SQL into a local
.sqltext file before closing the tab.
Do not paste secrets or private datasets into an online playground. Even when a tool runs locally in the browser, use fictional course data and treat the session as temporary.
Option C: use Python’s sqlite3 module
Python includes a standard interface to SQLite in normal distributions. This path also introduces an important production habit: SQL should be stored and executed deliberately rather than copied through an unknown graphical client.
from pathlib import Pathimport sqlite3 root = Path(__file__).resolve().parentdatabase_path = root / "practice.db"script_path = root / "setup.sql" with sqlite3.connect(database_path) as connection: connection.executescript(script_path.read_text(encoding="utf-8")) rows = connection.execute( "SELECT note_id, topic, note_text FROM practice_notes ORDER BY note_id" ).fetchall() for row in rows: print(row) Save the script as run_lab.py next to setup.sql, then run python run_lab.py or python3 run_lab.py, depending on your environment. The script resolves paths relative to itself, so it behaves consistently even when launched from another working directory.
Create a reproducible bootstrap script
Interactive typing is useful for exploration. A script is better for repeatability, review, Git history, and resetting the environment. Save the following as setup.sql.
PRAGMA foreign_keys = ON; DROP TABLE IF EXISTS practice_notes; CREATE TABLE practice_notes ( note_id INTEGER PRIMARY KEY, topic TEXT NOT NULL, note_text TEXT NOT NULL, created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP); INSERT INTO practice_notes (topic, note_text)VALUES ('environment', 'The bootstrap script ran successfully.'), ('safety', 'Practice databases must not contain production data.'); SELECT note_id, topic, note_text, created_atFROM practice_notesORDER BY note_id; The script drops and rebuilds one disposable table. That is acceptable only because the file is a dedicated practice database. In later chapters, migrations and safe schema evolution replace destructive resets.
Another learner should be able to start with an empty folder, run the documented command, and obtain the same schema and seed data without manual editing.
Recommended project layout
sql-foundations-lab/├── README.md├── setup.sql├── queries.sql├── run_lab.py├── practice.db # generated; usually ignored by Git├── backups/│ └── practice-before-experiment.db└── notes/ └── observations.md Keep source files and generated state separate. SQL scripts, documentation, and small fictional seed files belong in version control. Database files, secrets, temporary exports, and large generated outputs generally do not.
*.db*.db-journal*.db-wal*.db-shm.envbackups/exports/ Safe experiment workflow
- State the question. Example: “What happens when a unique constraint is violated?”
- Start from known state. Re-run
setup.sqlor copy a clean database. - Back up before destructive work. Copy the file while no writer is active, or use SQLite’s backup command.
- Preview the target rows. Run a
SELECTwith the same predicate beforeUPDATEorDELETE. - Use a transaction. Inspect the result before deciding to
COMMITorROLLBACK. - Record the observation. Save the SQL and result, not only a screenshot.
- Reset. Confirm that the lab returns to a known state.
BEGIN; SELECT note_id, topic, note_textFROM practice_notesWHERE topic = 'safety'; UPDATE practice_notesSET note_text = 'Changed during a reversible experiment.'WHERE topic = 'safety'; SELECT note_id, topic, note_textFROM practice_notesWHERE topic = 'safety'; ROLLBACK; -- Replace with COMMIT only when the change is intended. Back up and inspect the database
sqlite3 practice.db .databases.tables.schema.schema practice_notes -- Create a consistent backup from inside the CLI:.backup backups/practice-backup.db -- Export SQL capable of rebuilding the database:.output backups/practice-dump.sql.dump.output stdout -- Open a database without write permission:.open --readonly backups/practice-backup.db A backup is not proven until it can be restored and queried. Periodically open the backup, list its tables, run a known query, and verify expected row counts. The same principle later applies to PostgreSQL dumps, physical backups, snapshots, and disaster-recovery systems.
Environment verification checklist
| Check | Expected evidence |
|---|---|
| Executable | sqlite3 --version returns successfully, or Python imports sqlite3 |
| Working directory | practice.db appears in the intended lab folder |
| Schema | .schema practice_notes matches setup.sql |
| Seed data | The final SELECT returns two predictable rows |
| Reset | Running the bootstrap script again returns the same result |
| Backup | The backup opens read-only and returns the same rows |
| Isolation | No production host, credential, or sensitive record is involved |
Troubleshooting
sqlite3 is not recognized or not found
Confirm installation, open a new terminal, and add the executable directory to your user PATH. As an immediate fallback, use SQLite Fiddle or Python’s sqlite3.
The prompt waits instead of executing
SQL statements normally end with a semicolon. A missing quote, parenthesis, or semicolon can leave the shell waiting for more input. Press Ctrl+C to cancel an unfinished statement.
near ".": syntax error
Dot commands work only in the SQLite CLI and must begin at the start of an input line. They are not SQL and cannot be sent through every driver or application API.
The database file appeared in the wrong folder
Relative paths use the process’s current working directory. Use .databases to inspect the active file and prefer explicit project-relative paths in scripts.
database is locked
Another connection may hold an unfinished write transaction. Close unused clients, commit or roll back open work, and avoid editing one practice file simultaneously from many tools.
Foreign keys do not reject invalid references
SQLite requires PRAGMA foreign_keys = ON; for each connection. Verify it with PRAGMA foreign_keys;.
Checkpoint and practice
Concept check
- Why is a script preferable to a sequence of undocumented interactive commands?
- Why should a database file normally be excluded from Git?
- What proves that a backup is usable?
- Which SQLite commands are CLI commands rather than SQL?
Review the answers
A script is repeatable, reviewable, and versionable. Database files can contain generated state, sensitive records, locks, and noisy binary changes. A successful restore and verification query prove a backup. Commands beginning with a dot, such as .tables, .schema, and .backup, belong to the CLI.
Environment exercise
- Create the recommended folder.
- Run
setup.sqltwice and confirm identical final state. - Create and verify a backup.
- Run one update inside a transaction and roll it back.
- Write a short README containing the exact reset and verification commands.
Chapter 1 summary
You can now distinguish data, databases, DBMSs, and SQL; classify data by explicit structure; recognize operational, analytical, hybrid, and streaming workloads; describe the relational model; and operate a safe SQL laboratory. Chapter 2 begins the detailed language journey with tables, schemas, namespaces, and data types.