Skip to main content
Once you’re comfortable writing SELECT queries and JOINs, the next step is understanding the principles that guide good database design — and the advanced features that make complex analytical queries possible without losing individual row detail. This chapter introduces three essential topics: database normalization to keep your schema clean and consistent, ACID properties to guarantee reliable transactions, and window functions to rank, number, and partition data without collapsing rows the way GROUP BY does.

Database Normalization

Normalization is the process of organizing a database schema to reduce data redundancy and improve data consistency. A normalized database stores each piece of information in exactly one place, which means updates only need to happen in one location — dramatically reducing the chance of inconsistencies. Benefits of normalization:
  • Eliminates duplicate data
  • Improves data consistency
  • Reduces storage space
  • Simplifies UPDATE and DELETE operations
  • Easier long-term maintenance

First Normal Form (1NF)

A table is in 1NF if:
  • Every column contains a single, atomic (indivisible) value
  • There are no repeating groups of columns
  • Every row is unique (has a primary key)
Not in 1NF — the Subjects column holds multiple values in one cell: In 1NF — each cell holds exactly one value:
The Subjects column stores multiple values (Python, SQL) in a single cell. 1NF requires that every column contain only a single, atomic value — you cannot have lists or comma-separated values in a column.

Second Normal Form (2NF)

A table is in 2NF if:
  • It is already in 1NF
  • Every non-key column depends on the entire primary key (not just part of it)
This mainly applies to tables with composite primary keys (a primary key made from two or more columns). If a non-key column depends only on one part of the composite key, it violates 2NF and should be moved to a separate table.
2NF is relevant when a table has a composite primary key — a key made up of two or more columns. If a non-key attribute depends on only one column of that composite key (a partial dependency), the table is not in 2NF.

Third Normal Form (3NF)

A table is in 3NF if:
  • It is already in 2NF
  • No non-key column depends on another non-key column (no transitive dependencies)
Violates 3NF — the Manager column depends on Department, not directly on the primary key Employee ID: In 3NF — move manager information to the Department table: Department table: Employee table:
3NF removes transitive dependencies, where one non-key column indirectly depends on the primary key through another non-key column. By eliminating these, you ensure each non-key attribute describes only the primary key entity — nothing else.

ACID Properties

When a database executes a transaction (a sequence of operations treated as a single unit), it must guarantee four properties collectively known as ACID. These properties ensure that your data stays reliable and consistent even in the face of errors, crashes, or concurrent access.

Real-World Example: Bank Transfer

Consider transferring ₹500 from Account A to Account B:
  • Atomicity: If the second UPDATE fails, the first UPDATE is also rolled back. You never end up with ₹500 deducted but not deposited.
  • Consistency: Total money across all accounts remains the same before and after.
  • Isolation: Another transaction reading Account A’s balance during the transfer sees either the original amount or the final amount — never a partial state.
  • Durability: Once committed, the transfer survives a power outage or server restart.
Durability — once a transaction is committed, the data is permanently stored on disk. A system crash after the commit cannot undo it.

Window Functions

A window function performs a calculation across a set of rows that are related to the current row — without collapsing those rows into a single group the way GROUP BY does. Every row remains visible in the output; the window function simply adds an extra calculated column alongside it.

ROW_NUMBER()

Assigns a unique sequential integer to every row within a partition, starting from 1. No two rows ever share the same ROW_NUMBER.
Result:

RANK()

Assigns the same rank to rows with equal values, then skips the next rank to account for the tie.

DENSE_RANK()

Also assigns the same rank to ties, but does not skip the next rank.
Use RANK() when you want to reflect actual competition positions (e.g., two second-place finishers mean no third place). Use DENSE_RANK() when you want continuous ranking regardless of ties.

PARTITION BY

PARTITION BY divides the rows into separate groups before applying the window function — similar to GROUP BY, but without collapsing the rows.
This gives every employee a rank within their own department, resetting to 1 at the start of each new department group.

GROUP BY vs. Window Functions

A window function performs calculations while keeping every row in the result set. GROUP BY collapses multiple rows into a single summary row, making individual record details unavailable. Window functions let you simultaneously see per-row details and a calculated aggregate (like rank or running total) in the same query.

Summary

This chapter covered three advanced areas of SQL: Database Normalization
  • 1NF — atomic column values, no repeating groups
  • 2NF — no partial dependencies on a composite key
  • 3NF — no transitive dependencies between non-key columns
ACID Properties
  • Atomicity — all-or-nothing transactions
  • Consistency — always valid state transitions
  • Isolation — concurrent transactions don’t interfere
  • Durability — committed data survives failures
Window Functions
  • ROW_NUMBER() — unique sequential numbering
  • RANK() — same rank for ties, skips next rank
  • DENSE_RANK() — same rank for ties, no gaps
  • PARTITION BY — apply window per group while keeping all rows
These concepts complete the SQL fundamentals covered in this course and prepare you for more advanced topics such as indexes, views, stored procedures, and query optimization.