Relational Database Basics
Tables, schemas, SQL, and connections—what you need to store and query structured data in a backend.
Relational Database Basics
Tables, schemas, SQL, and connections—what you need to store and query structured data in a backend.
Lesson outline
What a relational database is
A relational database stores data in tables (rows and columns). Each row is a record; each column has a type (integer, text, timestamp). Tables can reference each other via foreign keys, so you model relationships (e.g. one user, many orders) without duplicating data.
Examples: PostgreSQL, MySQL, SQLite, SQL Server. They speak SQL (Structured Query Language). Your backend connects to the DB, sends SQL, and gets results back.
Schemas and tables
A schema (in the "namespace" sense) groups tables; in PostgreSQL you have public by default. You create tables with CREATE TABLE: define column names, types, and constraints. PRIMARY KEY uniquely identifies a row; FOREIGN KEY points to another table’s primary key.
Example: users (id, email, created_at) and orders (id, user_id, total, created_at). orders.user_id references users.id. That enforces referential integrity: you cannot have an order without a valid user.
Connecting from the backend
Your app opens a connection to the database (host, port, database name, user, password). Connections are expensive, so you use a connection pool: a set of open connections that the app borrows and returns. Libraries like pg (Node), psycopg2 (Python), database/sql (Go), JDBC (Java) handle this.
Never put credentials in code. Use environment variables or a secrets manager; in production use TLS to the DB and restrict network access (e.g. private subnet, no public IP).
SQL basics
SELECT reads data: SELECT id, email FROM users WHERE id = 1. You can JOIN tables (e.g. orders with users), ORDER BY, LIMIT, and GROUP BY for aggregates. INSERT adds rows; UPDATE changes them; DELETE removes them. These are the core operations you will use every day.
Parameterized queries (placeholders like $1, ?, :id) are mandatory. You pass values from the app; the driver escapes them. Never concatenate user input into SQL—that causes SQL injection.
Migrations and schema changes
As the app evolves, you add columns, tables, or indexes. Doing this manually is error-prone. Migrations are versioned scripts (e.g. 001_create_users.sql, 002_add_orders.sql) that you run in order. Tools like Flyway, Alembic, golang-migrate, or TypeORM track which migrations ran and apply only new ones.
Always test migrations on a copy of production data. Some changes (e.g. adding a non-null column to a big table) need a multi-step migration to avoid locking or downtime.
Ready to see how this works in the cloud?
Switch to Career Paths for structured paths (e.g. Developer, DevOps) and provider-specific lessons.
View role-based pathsSign in to track your progress and mark lessons complete.
Discussion
Questions? Discuss in the community or start a thread below.
Join DiscordIn-app Q&A
Sign in to start or join a thread.