Batch 3: SQL Databases LDAP YAML JSON ← Full TOC

← SQL Index

🏗️ DDL & Schema — CREATE, ALTER, DROP, indexes, constraints, data types

Data Definition Language (DDL) creates and modifies database structure. Good schema design with proper constraints, indexes, and data types is fundamental to database performance and data integrity.

📋 SQL Reference

Statement / FeatureSyntaxDescription
CREATE TABLECREATE TABLE name (col type constraints, ...)Create new table
CREATE TABLE IF NOT EXISTSCREATE TABLE IF NOT EXISTS name (...)Create only if not exists
DROP TABLEDROP TABLE tablenameDelete table and all data — irreversible!
DROP TABLE IF EXISTSDROP TABLE IF EXISTS tablenameDrop only if exists — no error if missing
ALTER TABLE ADDALTER TABLE t ADD COLUMN col typeAdd new column
ALTER TABLE DROPALTER TABLE t DROP COLUMN colRemove column
ALTER TABLE MODIFYALTER TABLE t MODIFY col newtype / ALTER COLUMNChange column type
ALTER TABLE RENAMEALTER TABLE old RENAME TO newRename table
RENAME COLUMNALTER TABLE t RENAME COLUMN old TO newRename column (PostgreSQL/SQLite)
PRIMARY KEYcol INTEGER PRIMARY KEY / PRIMARY KEY (col1,col2)Unique non-null identifier
FOREIGN KEYFOREIGN KEY (col) REFERENCES other(col) ON DELETE CASCADEReferential integrity
UNIQUEcol VARCHAR(100) UNIQUE / UNIQUE (col1,col2)Enforce uniqueness
NOT NULLcol VARCHAR(100) NOT NULLDisallow null values
DEFAULTcol INTEGER DEFAULT 0 / DEFAULT CURRENT_TIMESTAMPDefault value when not specified
CHECKsalary DECIMAL CHECK (salary > 0) / CHECK (status IN ('a','b'))Value constraint
AUTO_INCREMENTid INT AUTO_INCREMENT PRIMARY KEYMySQL auto-increment primary key
SERIALid SERIAL PRIMARY KEYPostgreSQL auto-increment
IDENTITYid INT GENERATED ALWAYS AS IDENTITYSQL standard / PostgreSQL 10+ / SQL Server
CREATE INDEXCREATE INDEX idx_name ON table(col)Speed up queries on column
CREATE UNIQUE INDEXCREATE UNIQUE INDEX idx ON table(col)Index + uniqueness constraint
CREATE INDEX multiCREATE INDEX idx ON table(col1, col2)Composite index
DROP INDEXDROP INDEX idx_nameRemove index
CREATE VIEWCREATE VIEW vname AS SELECT ...Named virtual table from query
CREATE OR REPLACE VIEWCREATE OR REPLACE VIEW vname AS SELECT ...Create or update view
DROP VIEWDROP VIEW viewnameRemove view
VARCHARVARCHAR(255)Variable-length string up to n chars
TEXTTEXTUnlimited-length string (MySQL/PG)
INTEGER / INTINTEGER32-bit integer
BIGINTBIGINT64-bit integer
DECIMALDECIMAL(10,2)Exact numeric with precision and scale
FLOAT / REALFLOAT / REALApproximate floating point
BOOLEANBOOLEAN / BOOLTrue/false (MySQL: TINYINT(1))
DATEDATEDate only: YYYY-MM-DD
TIMESTAMPTIMESTAMP / TIMESTAMP WITH TIME ZONEDate + time
JSONJSON / JSONBJSON data type (PostgreSQL JSONB = binary, indexed)
ENUMENUM('val1','val2')Fixed set of allowed values (MySQL)

💡 Example

-- DDL — Schema Design Best Practices

-- ── Create tables with full constraints ──────────────────────
CREATE TABLE universities (
    id          SERIAL PRIMARY KEY,
    name        VARCHAR(200) NOT NULL,
    domain      VARCHAR(100) NOT NULL UNIQUE,
    founded     INTEGER CHECK (founded BETWEEN 1000 AND 2100),
    country     CHAR(2) NOT NULL DEFAULT 'US',
    active      BOOLEAN NOT NULL DEFAULT TRUE,
    created_at  TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at  TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE courses (
    id          SERIAL PRIMARY KEY,
    univ_id     INTEGER NOT NULL REFERENCES universities(id) ON DELETE RESTRICT,
    code        VARCHAR(20) NOT NULL,
    title       VARCHAR(300) NOT NULL,
    credits     SMALLINT NOT NULL DEFAULT 3 CHECK (credits BETWEEN 1 AND 12),
    level       VARCHAR(20) NOT NULL DEFAULT 'beginner'
                  CHECK (level IN ('beginner','intermediate','advanced')),
    price       DECIMAL(8,2) NOT NULL DEFAULT 0.00 CHECK (price >= 0),
    published   BOOLEAN NOT NULL DEFAULT FALSE,
    created_at  TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    UNIQUE (univ_id, code)   -- course codes unique per university
);

CREATE TABLE enrollments (
    id          SERIAL PRIMARY KEY,
    course_id   INTEGER NOT NULL REFERENCES courses(id) ON DELETE CASCADE,
    student_email VARCHAR(150) NOT NULL,
    enrolled_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    completed_at TIMESTAMP,
    score       DECIMAL(5,2) CHECK (score IS NULL OR (score BETWEEN 0 AND 100)),
    UNIQUE (course_id, student_email)
);

-- ── Indexes ──────────────────────────────────────────────────
CREATE INDEX idx_courses_univ ON courses(univ_id);
CREATE INDEX idx_courses_level ON courses(level) WHERE published = TRUE;
CREATE INDEX idx_enrollments_student ON enrollments(student_email);
CREATE INDEX idx_enrollments_course ON enrollments(course_id, enrolled_at DESC);

-- ── Views ────────────────────────────────────────────────────
CREATE OR REPLACE VIEW course_stats AS
SELECT
    c.id, c.code, c.title, c.credits,
    u.name AS university,
    COUNT(e.id) AS enrollments,
    AVG(e.score) AS avg_score,
    SUM(CASE WHEN e.completed_at IS NOT NULL THEN 1 ELSE 0 END) AS completions
FROM courses c
JOIN universities u ON c.univ_id = u.id
LEFT JOIN enrollments e ON c.id = e.course_id
WHERE c.published = TRUE
GROUP BY c.id, c.code, c.title, c.credits, u.name;

-- Usage
SELECT * FROM course_stats ORDER BY enrollments DESC LIMIT 10;

-- ── ALTER TABLE examples ─────────────────────────────────────
ALTER TABLE courses ADD COLUMN tags TEXT[];          -- PostgreSQL array
ALTER TABLE courses ADD COLUMN description TEXT;
ALTER TABLE courses ALTER COLUMN price SET DEFAULT 0.00;
ALTER TABLE enrollments ADD COLUMN grade CHAR(2);
CREATE INDEX idx_courses_tags ON courses USING GIN(tags);  -- PostgreSQL GIN index

← Core SQL  |  🏠 Index  |  Aggregate Functions →