Database Basics — From Excel to Postgres
Every Excel concept you already know maps directly to how databases work.
Why First: The Breaking Point of Excel
You manage client files. You have a master Excel workbook with 50,000 rows — every client, every deadline, every document submission. It works fine when you manage it alone.
Then you hire two staff members. All three of you open the same file simultaneously via Google Drive. One person accidentally applies a bad formula to an entire column. Another saves a version that overwrites the first person's edits. By Friday, you don't know which version of the data is correct.
Then the file grows to 200,000 rows. Excel slows to a crawl. Pivot tables take 4 minutes to refresh. A colleague opens it on an older laptop and it crashes.
Then a junior staff member accidentally deletes 3,000 rows. They save. The file is gone.
Excel was built for analysis. It was never built for this.
Databases were. Here's how to think about them.
The Direct Mapping: Excel → Database
Every database concept has a direct Excel equivalent. Use this as your translation dictionary.
The visual below shows the same fee data side by side — Excel on the left, Supabase on the right. The columns, rows, and structure are identical. The capabilities are very different.
Same fee records in both tools. Supabase adds RLS, multi-user access, automatic IDs, and an API — none of which Excel has.
Table = Worksheet Tab
A database table is exactly like a worksheet tab. One tab per type of data.
In Udyogaseva, the database has separate tables for profiles, jobs, applications, companies, candidates — just like you'd have separate tabs in a well-structured Excel workbook.
The difference: a database forces you to be explicit about what goes in each column — no mixing types, no merged cells, no colour-coded secrets.
Row = One Record
One row in the database = one item of that type.
- One row in
profiles= one user account - One row in
jobs= one job posting - One row in
applications= one person applying to one job
Column = A Field
Every column has a name and a type — it only accepts one kind of data. You declare this upfront.
| Database Type | What it stores | Excel equivalent |
|---|---|---|
text | Any text | Text cell |
integer | Whole numbers | Number cell (no decimals) |
numeric(12,2) | Decimal numbers | Currency cell |
boolean | true / false | Checkbox |
timestamptz | Date + time + timezone | Date cell |
uuid | Unique ID (auto-generated) | Auto-incrementing row number |
jsonb | Structured JSON data | A cell with a nested table inside (kind of) |
Primary Key = The Row ID That Never Changes
In Excel, rows are numbered 1, 2, 3 — but if you insert a row, all the numbers shift. If you delete row 5, the old row 6 becomes the new row 5. Nothing is stable.
In a database, every row has a primary key — a unique ID that is assigned once and never changes, ever. Even if you delete the row (which you shouldn't — more on that later), that ID is never reused.
In Udyogaseva, every table uses a UUID as the primary key — something like a3b8c9d2-f4e5-4a3b-8c9d-2f4e5a3b8c9d. Globally unique. Never changes. Never reused.
Foreign Key = VLOOKUP Reference
When one table needs data from another table, you don't copy the data — you reference it. This is exactly like =VLOOKUP(A2, ClientSheet!A:D, 3, FALSE) — instead of copying the client name into every row, you look it up from the clients sheet.
In a database, a foreign key creates a permanent, enforced reference between tables.
The company_id column in jobs is a foreign key — it references the id column in recruiter_companies. You store the ID, not the company name. The database enforces that you can't reference a company that doesn't exist.
=VLOOKUP(A2, ClientSheet!A:D, 3) and the value in A2 doesn't exist in ClientSheet, VLOOKUP returns an error. A database foreign key does the same — it physically prevents you from referencing a row that does not exist.profiles). Look at the columns panel. Find a column labeled with a link icon — that is a foreign key. Click it to see which table and column it references.The Udyogaseva Schema in Plain English
Udyogaseva is a job platform for finance professionals. Here's how the core database is structured, in plain English:
This is the same pattern you would use for most apps. Users → their extended data → the things they create → the relationships between things.
SQL: Just Enough to Understand What's Happening
You will mostly use Supabase's JavaScript SDK to query data — not write SQL by hand. But you need to read SQL to understand what's happening in migrations and edge functions.
Three commands cover 90% of what you'll read:
SELECT — Read data from a table:
INSERT — Add a new row:
UPDATE — Modify existing rows:
When you see these in migration files, you're reading the actual history of how the database was built. Each migration file is one change to the structure.
Indexes: Why Queries Are Fast (Or Not)
In Excel, if you want to find a specific row, Excel scans every row from top to bottom until it finds a match. Fast for 100 rows. Slow for 100,000 rows. You solve this by sorting your data — a sorted column lets you jump to the right section instantly.
A database index does the same thing. You tell the database: "I will frequently search this column — keep it sorted so lookups are fast."
Rule: Any column you filter by frequently (in a WHERE clause) should have an index. Any column you sort by frequently (in an ORDER BY) should have an index.
Migrations: Versioned Schema Changes
Your database schema is code. Like code, it changes over time. Unlike code, you can't just "undo" a database change — you might lose data.
Migrations are the solution. A migration is a SQL file that describes one change to your database structure. Each migration has a timestamp in its filename. They run in order, one at a time. The history is permanent.
Never edit a migration file that has already been applied to production. If you made a mistake, write a new migration that corrects it. Editing applied migrations breaks the history and can cause your local environment to diverge from production in ways that are difficult to diagnose.
Relationships: One-to-Many and Many-to-Many
Two relationship patterns appear in almost every app:
One-to-Many: One recruiter has many jobs. One company has many recruiters.
Many-to-Many: One job requires many skills. One skill appears in many jobs.
You can't store this in two tables — you need a third table (called a junction table) that holds the relationship:
job_skills is the junction table. It has one row for each (job, skill) pair. The UNIQUE(job_id, skill_id) constraint is the database equivalent of Excel's data validation — it physically prevents duplicate entries.
This is exactly how you'd design a pivot table's source data: one row per combination, no merging, no repeating group columns.
See the Connection: Database Drives the UI
This is the core insight of full-stack development. The UI on the left does not have its own data — it reads from the database table on the right. When you change a row in the table, the UI instantly reflects it. When you add a row, a new card appears. When you delete a row, it disappears.
Edit any cell in the table on the right — the invoice tracker on the left updates instantly. This is exactly how every Supabase-powered app works: the database is the source of truth, and the UI is a live view of it.
The Golden Rules Before You Write Code
- Design your schema before writing any frontend. Know your tables, know your relationships, know your foreign keys. Drawing it on paper first is not optional — it's faster than refactoring later.
- Every table needs
id,created_at,updated_at,deleted_at. These four columns are required in every table, without exception. - Never store derived data. If something can be calculated from other columns, don't store it — calculate it. Stored derived data gets out of sync.
- Never repeat data. If you find yourself writing the company name into 50 different rows, you need a foreign key to a companies table.
These rules are the difference between a database you can maintain and a database that becomes a liability.
JoinVisualizer below. Hover a client row and watch the matching invoice rows highlight. Then switch from INNER JOIN to LEFT JOIN and observe how orphan rows (clients with no invoices) behave differently. This is the visual intuition behind foreign keys and relationships.See It in Action: Database JOINs
Hover any client row to highlight the matching invoices. Switch between INNER JOIN and LEFT JOIN to see how orphan rows are handled.
- I can explain the breaking point of Excel — why databases exist for multi-user, high-volume data
- I understand primary keys — assigned once, never changes, never reused, even after row deletion
- I understand foreign keys — enforced VLOOKUPs that prevent referencing rows that don't exist
- I know what a migration is and why you NEVER edit an applied one — always write a new one
- I interacted with the JoinVisualizer and can explain INNER JOIN vs LEFT JOIN in plain English
- I have designed at least one schema on paper before opening Supabase