Tips 21–30: DDL & Indexing
The last 10 tips: precise data types, sensible lengths, NOT NULL, clustered primary keys, indexed foreign keys, and how to keep indexes lean, fresh and defragmented.
Performance starts before the first query — in how the tables are designed (DDL) — and continues long after, in how the indexes are maintained. These final 10 tips cover both.
Tip 21 — Choose precise data types
Avoid
VARCHAR/TEXTwhen a more exact type fits.
sql
-- ❌ Bad
jersey_no VARCHAR(50)
-- ✅ Good
jersey_no TINYINT| Type | Storage | Range / use |
|---|---|---|
TINYINT | 1 byte | 0 – 255 (jersey numbers, ages) |
SMALLINT | 2 bytes | −32,768 – 32,767 |
INT | 4 bytes | ±2.1 billion (ids, runs) |
BIGINT | 8 bytes | very large ids |
DATE | 3 bytes | dates — never store dates as text |
CHAR(3) | 3 bytes | fixed-length codes like 'RCB' |
Smaller types → more rows per 8 KB page → fewer pages to read. Correct types also give correct sorting ('10' < '9' as text!) and avoid implicit conversions that block index seeks.
Tip 22 — No excessive lengths
Avoid
VARCHAR(MAX)unless it is truly needed.
sql
-- ❌ Bad
name VARCHAR(MAX)
-- ✅ Good
name VARCHAR(50)VARCHAR(MAX) can hold up to 2 GB, cannot be an index key column, may be stored off-row, and makes SQL Server request more memory for sorts. Size columns to the real data.
Tip 23 — Use NOT NULL
Enforce integrity with
NOT NULLwherever a value is required.
sql
-- ❌ Bad
id INT
-- ✅ Good
id INT NOT NULLNOT NULL stops bad data at the door, and it helps the optimizer: it knows no NULL checks are needed (for example, NOT IN can be planned efficiently).
Tip 24 — Clustered primary key
Every table should have a clustered primary key — it gives the data a structure.
sql
-- ❌ Bad: table without a key (a heap)
CREATE TABLE players (id INT, name VARCHAR(50));
-- ✅ Good
CREATE TABLE players (
id INT NOT NULL PRIMARY KEY CLUSTERED,
name VARCHAR(50) NOT NULL
);Without it the table is a heap: every lookup by id is a full scan, and updates can leave "forwarded records" behind. A good clustered key is narrow, unique, static and ever-increasing (e.g. INT IDENTITY(1,1)).
Tip 25 — Index the foreign keys
Add non-clustered indexes to foreign keys that are queried or joined often.
sql
-- ❌ Bad: FK players.team -> teams.team, but no index on players.team
-- ✅ Good
CREATE INDEX IX_fk_team ON players (team);SQL Server automatically indexes primary keys, but not foreign keys. FK columns are exactly the ones used in joins (tip #10), and deleting a parent row must check the child table — without an index that check is a full scan.
Tip 26 — Avoid over-indexing
Too many indexes slow down INSERT, UPDATE and DELETE.
sql
-- ❌ Bad: 15 indexes on one table
-- ✅ Good: keep only the useful onesEvery INSERT into players must also insert into every index on it. 15 indexes = 16 writes per row, plus more storage and more maintenance. Index for your real, frequent queries — not for every column.
Tip 27 — Drop unused indexes
Review regularly: dropping indexes nobody reads saves space and improves write speed.
sql
-- ❌ Bad: an index nobody reads
-- ✅ Good
DROP INDEX IX_unused ON players;Find them with sys.dm_db_index_usage_stats (no seeks/scans/lookups, many updates) — see Indexing Strategy, Statistics & Maintenance.
Tip 28 — Update statistics weekly
Fresh statistics help the optimizer choose the best plan.
sql
-- ❌ Bad: stale statistics -> wrong row estimates -> bad plans
-- ✅ Good
UPDATE STATISTICS players;Schedule it (e.g. weekly via a SQL Agent job, or EXEC sp_updatestats for the whole database), and after big data loads.
Tip 29 — Fix fragmented indexes
Reorganize or rebuild fragmented indexes regularly (e.g. weekly).
sql
-- ❌ Bad: fragmented index
-- ✅ Good
ALTER INDEX ALL ON players REBUILD;Rule of thumb: ~5–30% fragmentation → REORGANIZE; over ~30% → REBUILD. Check with sys.dm_db_index_physical_stats.
Tip 30 — Partition + columnstore
For huge fact tables: partition the data, then add a columnstore index.
sql
-- ❌ Bad: one giant 500M-row table
-- ✅ Good: PARTITION by season + COLUMNSTORE
CREATE CLUSTERED COLUMNSTORE INDEX CCI_innings
ON innings
ON ps_season (match_date);Partition elimination reads only the needed seasons; columnstore compresses and scans fast inside them; maintenance happens one season at a time.
Cheat sheet — indexes & partitioning
| Topic | Remember |
|---|---|
| Clustered | sorts the table itself — ONE per table (primary key) |
| Non-clustered | separate structure + pointers — many per table |
| Columnstore | column by column — analytics, big scans |
| Unique / Filtered | no duplicates / index only some rows |
| Index SEEK > SCAN | check the execution plan to be sure |
| OLTP vs OLAP | write-heavy: clustered PK · read-heavy: columnstore |
| Partition function | boundaries → partitions → filegroups → scheme |
| Maintain | update statistics, fix fragmentation, drop unused |
Common mistakes
- ❌ Storing dates or numbers as
VARCHAR. ✅DATE,INT,TINYINT,DECIMAL. - ❌ Creating an index for every column "for speed". ✅ Fewer, well-chosen (often composite) indexes.
- ❌ Assuming foreign keys are indexed automatically. ✅ In SQL Server they are not — add the index.
Interview tip
Q: Why are too many indexes bad? A: Every INSERT, UPDATE and DELETE has to maintain every index, so writes slow down; indexes use storage and memory; they need statistics and defragmentation; and the optimizer has more choices to evaluate. Keep the indexes that real queries use and drop the rest.
Key points to remember
- Tip 21: choose precise data types (TINYINT, INT, DATE) — smaller rows mean fewer pages and correct comparisons.
- Tip 22: avoid VARCHAR(MAX) unless truly needed — it can't be an index key and wastes memory.
- Tip 23: use NOT NULL for required values — integrity plus better optimizer decisions.
- Tip 24: give every table a clustered primary key (narrow, unique, static, ever-increasing).
- Tip 25: index foreign keys — SQL Server indexes primary keys automatically but NOT foreign keys.
- Tips 26–27: avoid over-indexing and drop unused indexes — every index slows every write.
- Tips 28–29: update statistics and fix fragmentation (REORGANIZE / REBUILD) on a schedule, e.g. weekly.
- Tip 30: for huge fact tables, partition by a key like season and add a columnstore index.
Try it yourself
SQL Server: well-designed table (tips 21-25)
CREATE TABLE squad_players (
id INT IDENTITY(1,1) NOT NULL PRIMARY KEY CLUSTERED, -- tip 24
name VARCHAR(50) NOT NULL, -- tips 22, 23
team VARCHAR(10) NOT NULL REFERENCES teams (team),
jersey_no TINYINT NOT NULL, -- tip 21
debut DATE NULL
);
-- tip 25: index the foreign key
CREATE NONCLUSTERED INDEX IX_fk_team ON squad_players (team);SQL Server-specific — run this in SQL Server (SSMS / Azure Data Studio).
Try it: precise types, NOT NULL and an FK index
Runs on a fresh IPL practice database in your browser · edit the query and press Ctrl + Enter
SQL Server: index maintenance (tips 27-29)
-- tip 27: drop an index nobody uses
DROP INDEX IX_unused ON players;
-- tip 28: refresh statistics (schedule weekly)
UPDATE STATISTICS players;
-- tip 29: fix fragmentation
ALTER INDEX ALL ON players REORGANIZE; -- light (~5-30%)
ALTER INDEX ALL ON players REBUILD; -- heavy (> 30%)SQL Server-specific — run this in SQL Server (SSMS / Azure Data Studio).
Practice: text vs number sorting (why types matter)
Runs on a fresh IPL practice database in your browser · edit the query and press Ctrl + Enter
Revision Flashcards
Tap a card to flip it and see the answer.
